Introduction
============
Apologies for going silent these past couple months just to return with
a patchset over 3000 lines larger than the last -- I should have been
releasing intermediate versions along the way so shame on me! The
Counter system has effectively been rewritten anew, so I believe very
little of the code in the previous versions of this patchset remain.
However, the Generic Counter paradigm has pretty much remained the same
so the theory should be familar. Regardless, I realize I'm dropping off
this patchset near the winter holidays so I don't expect a review until
well into January -- I'm just releasing this now before I myself head
off on an end of year sabbatical.
The most significant difference between this version and the previous,
as well as part of the reason for the implementation code changes, is
the complete separation of the Generic Counter system from IIO. I
decided it was improper to build the Generic Counter system on top of
IIO core: it was leading to ugly code, convulted hacks, and forced
restrictions on the Generic Counter interface in order to appease the
architecture of the IIO system. Most importantly, the IIO core code that
was leveraged by the Generic Counter was so minor (essentially just the
sysfs attribute support) that it did not justify the extensive
hoop-jumping performed to make the code work.
So this patchset introduces the Generic Counter interface without the
dependence on IIO code. This now gives the Generic Counter system the
freedom to aptly represent counter devices without implementation
compatibility concerns regarding other high-level subsystems.
This also makes sense ontologically I believe because whereas the IIO
system appears more focused on representing the industrial I/O of a
device and their configuration directly, the Generic Counter system is
more concerned with the abstract representation of that counter device
and the relationships and configurations within which define its
operation at a high-level; a counter driver could in theory separately
support both the high-level Generic Counter representation of the device
as a whole (what are we counting conceptually, how much are we counting,
etc.), as well as the low-level IIO representation of the individual
inputs and outputs on that device (are the signals differential, do
certain signals have current requirements, etc.).
Overview
========
This patchset may be divided into three main groups:
* Generic Counter
* Simple Counter
* Quadrature Counter
Each group begins with a patch introducing the implementation of the
interface system, followed afterwards by documentation patches. I
recommend reading through the documentation patches first to familiarize
your with the interface itself before jumping into the source code for
the implementation.
The Simple Counter and Quadrature Counter groups also have example
driver code in the dummy-counter and 104-quad-8 patches respectively.
The Simple Counter and Quadrature Counter systems themselves being
subclasses of the Generic Counter may serve as example driver code for
the Generic Counter interface -- though I may end up adding an explicit
Generic Counter example in a later patch to the dummy-counter for easier
reference.
Since the Generic Counter system no longer depends on IIO, I moved all
Counter related source code to the drivers/iio/counter/ directory to
keep everything contained together. In addition, with the IIO Kconfig
dependency removed, the COUNTER menu appear now appears at the same
level as the IIO menu:
-> Device drivers
-> Counter Support (COUNTER [=m])
I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
order to match the Kconfig heirarchy or to keep it where it is to match
the legacy IIO counter location established when we first added the
104-QUAD-8 driver.
Paradigm updates
================
The Generic Counter paradigm has essentially remained the same from the
previous patch, but I have made some minor updates. In particular, I've
finally settled on a naming convention for the core components of a
Counter:
COUNT
-----
A Count represents the count data for a set of Signals. A Count
has a count function mode (e.g. "increase" or "quadrature x4")
which represents the update behavior for the count data. A Count
also has a set of one or more associated Signals.
This component was called "Value" in the previous patches. I believe
"Count" is a more direct name for this data, and it also matches how
datasheets and people commonly refer to this information in
documentation.
SIGNAL
------
A Signal represents a counter input data; this is the data that
is typically analyzed by the counter to determine the count
data. A Signal may be associated to one or more Counts.
The naming for this component has not changed since the previous
patches. I believe "Signal" is a fitting enough name for the input
data, as well as matching the common nomenclature for existing counter
devices.
SYNAPSE
-------
A Synapse represents the association of a Signal with a
respective Count. Signal data affects respective Count data, and
the Synapse represents this relationship. The Synapse action
mode (e.g. "rising edge" or "double pulse") specifies the Signal
data condition which triggers the respective Count's count
function evaluation to update the count data. It is possible for
the Synapse action mode to be "none" if a Signal is associated
with a Count but does not trigger the count function (e.g. the
direction signal line for a Pulse-Direction encoding counter).
This component was called "Trigger" in the previous patches. I do not
believe "Trigger" was a good name for two main reasons: it could easily
be confused for the existing IIO trigger concept, and most importantly
it does not convey the connection association aspect of the
Count-Signal relationship.
I settled on the "Synapse" name both due to etymology -- from Greek
_sunapsis_ meaning "conjunction" -- as well as a biological analogy:
just as neurons connect and fire communication over synapses, so does a
Counter Signal connect and fire communication to a Counter Count over a
Counter Synapse.
Following the same naming convention and analogy, what was previously
called trigger_mode is now known as action_mode, named in reference to
action potential -- the condition in a neuron which triggers a fire
communication over a synapse, just as a Counter Signal condition
specified in the action_mode of a Counter Synapse triggers the count
function evaluation for a Counter Count.
Counter classes descriptions
============================
The Generic Counter interface is the most general interface for
supporting counter devices; if it qualifies as a Counter, then it can be
represented by the Generic Counter interface. Unfortunately, the
flexibility of the interface does result in a more cumbersome
integration for driver authors: much of the components must be manually
configured by the author, which can be a tedious task for large and
complex counter devices.
To this end, two subclasses of the Generic Counter interface as
introduced in this patchset: the Simple Counter interface, and the
Quadrature Counter interface. Both of these interfaces inherit the
Generic Counter paradigm, and may be seen as extensions to the interface
which restrict the components to a respective specific class of counter
devices in order to provide a more apt interface for such devices.
Simple Counter
--------------
Simple Counters are devices that count edge pulses on an input
line (e.g. tally counters).
Since the relationship between Signals and Counts is known to be
one-to-one, a simple_counter_count structure already contains
the associated Signal member for the respective Count. A driver
author no longer needs to worry about allocating a separate
Signal and Synapse, nor about configuring the association
between the respective Count and Signal; the Simple Counter
interface abstracts away such details.
Furthermore, since the device type is known, component
properties may be further defined and restricted: Count data is
a signed integer, Signal data "low" and "high" state is set via
enumeration constants, and so are count function and action mode
restricted to well-defined "increase"/"decrease" and
"none"/"rising edge"/"falling edge"/"both edges" enumeration
constants respectively.
Quadrature Counter
------------------
Quadrature Counters are devices that track position based on
quadrature pair signals (e.g. rotary encoder counters).
Since the relationship between Signals and Counts is known to be
a quadrature pair of Signals to each Count, a quad_counter_count
structure already contains the associated Signal members for the
respective Count. A driver author no longer needs to worry about
allocating separate Signals and Synapses for each quadrature
pair, nor about configuring the association between the
respective Count and Signals; the Quadrature Counter interface
abstracts away such details.
Furthermore, since the device type is known, component
properties may be further defined and restricted: Count data is
a signed integer, Signal data "low" and "high" state is set via
enumeration constants, and so is count function mode restricted
to well-defined enumeration constants to represent modes such as
"pulse-direction" and "quadrature x4" for example.
Note how driver authors no longer need to interact with Synapses
directly when utilizing the Simple Counter and Quadrature Counter
interfaces. This should make it easier too for authors to add support
since they don't need to fully understand the underlying Counter
paradigm in order to take advantage of the interfaces -- just define the
Counts and Signals, and they're ready to go.
Even more so, the Quadrature Counter interface takes it a step further
and doesn't require action_modes to be explicitly set -- rather they are
implicitly determined internally by the system based on the direction
and function mode. Abstractions like these should make the Counter
interface system as a whole robust enough to handle the diverse classes
of counter devices out in the real world.
Compilation warnings
====================
There are three main compilation warnings which pop for this patchset.
I've inspected these warnings and none are errors, however they do
require some explanation.
* 104-quad-8: warning: enumeration value
‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
switch
The first warning is rather simple to explain: the
QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
statement's else condition, so an explicit case condition is not
necessary. I can add a default case line to pacify the compiler, but
since it would be empty the effort seems frivolous. In some sense as
well, a default case may make the switch logic less clear by implying
the possibility of additional cases which are not possible in the
context of that code path.
* simple-counter: warning: assignment discards ‘const’ qualifier
from pointer target type
* quad-counter: warning: assignment discards ‘const’ qualifier
from pointer target type
The second warning comes from the mapping of
simple_counter_device_ext/quad_counter_device_ext,
simple_counter_count_ext/quad_counter_count_ext, and
simple_counter_signal_ext/quad_counter_signal_ext to the internal
Counter counter_device_ext, counter_count_ext, and counter_signal_ext
structures respectively.
The priv member of the counter_device_ext, counter_count_ext, or
counter_signal_ext is leveraged to pass the respective
simple_counter_device_ext/quad_counter_device_ext,
simple_counter_count_ext/quad_counter_count_ext, or
simple_counter_signal_ext/quad_counter_signal_ext structure to their
respective read/write callback. The priv member is generic on purpose to
allow any desired data to be passed; the supplied read/write callbacks
should know the datatype of the passed-in priv argument so they cast it
appropriately to access their expected data.
As such, the 'const' qualifier of the structures are thus discarded but
subsequently cast back when the respective registered callback functions
are called. Since this is the intended use case of the priv member -- to
generically pass driver data for later recast -- I don't believe this
warning needs to be rectified.
* generic-counter: warning: passing argument 5 of
‘counter_attribute_create’ discards ‘const’ qualifier
from pointer target type
* generic-counter: warning: passing argument 6 of
‘counter_attribute_create’ discards ‘const’ qualifier
from pointer target type
The third warnings comes from a similar situation to the second warning:
a 'const' argument is passed generically via 'void *' for later recast.
In this cast, I decided to create a generic function called
counter_attribute_create in order to simplify the sysfs attribute
registration code in the generic-counter.c file.
The counter_attribute_create function takes in read and write callbacks,
as well as two optional generic data arguments to be stored as 'void *'
(the component and component_data parameters). Using this setup allows
the counter_attribute_create function to be the sole function necessary
to create a desired Generic Counter sysfs attribute: read and write
callbacks are passed along with relevant Counter component and data
generically, which can be cast back later inside those read and write
functions to match the expected datatype.
Using a generic counter_attribute_create function reduces duplicate
code, but it does result in many superfluous compilation warnings. I can
define new attribute_create functions specific to each type of sysfs
attribute in order to pacify the warnings, but that seems to be such a
waste to increase the amount of code with duplications that are
unnecessary. What would you recommend; should I attempt to pacify these
warnings or leave them be?
Known TODO items
================
Although I've added the interface documentation files with rst file
extensions, I still need to familiarize myself with Sphinx markup
constructs to take advantage of the language. For example, I've copied
verbatim several structure definitions into the documentation directly,
but I believe this would be better left dynamically generated by using
the relevant markup syntax. I'll try to clean up the documentation then
once I've brushed up on Sphinx.
As noted in a previous patchset version, the signal_write callback
should be removed from the interface; there are few if any cases where
it makese sense to have a signal_write callback since Signals are
always considered inputs in the context of the Counter paradigm.
I've retained the signal_write callback in this version since I'm unsure
how to implement the dummy-counter Signal source. Benjamin Gaignard
suggested implementing dummy-counter as a gpio-counter which could use
gpio to provide a software quadratic counter. Is this the path I should
take?
Furthermore, the dummy-counter driver defines its own single
platform_device which restricts it to loading only a single instance.
I can fix this to allow multiple instances in the next patchset version
-- as suggested, I'll check out industrialio-sw-device.c for reference.
Right now the dummy-counter driver only has example code for the Simple
Counter interface. It may be prudent to add example code for the Generic
Counter and Quadrature Counter interfaces too. I think dummy-counter
should serve as the reference driver implementation for all the Counter
interfaces, so that driver authors have an example of how to integrate
the particular interface they desire.
Finally, I only added very basic support for the Quadrature Counter
interface in the 104-QUAD-8 driver. It's possible to support all
existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
corresponding quad_counter_device_ext, quad_counter_count_ext, and
quad_counter_signal_ext structures, such that only the
/sys/bus/counter/devices/counterX/ directory needs to be accessed to
interact with the 104-QUAD-8 device. I'll try to add support for those
remaining sysfs attributes in the next patchset version.
If I missed anything from the last patchset version review just remind
me again and I'll add it to my TODO list. ;)
William Breathitt Gray (11):
iio: Introduce the Generic Counter interface
counter: Documentation: Add Generic Counter sysfs documentation
docs: Add Generic Counter interface documentation
counter: Introduce the Simple Counter interface
counter: Documentation: Add Simple Counter sysfs documentation
docs: Add Simple Counter interface documentation
counter: Add dummy counter driver
counter: Introduce the Quadrature Counter interface
counter: Documentation: Add Quadrature Counter sysfs documentation
docs: Add Quadrature Counter interface documentation
counter: 104-quad-8: Add Quadrature Counter interface support
.../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
.../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
.../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
Documentation/driver-api/iio/index.rst | 3 +
Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
MAINTAINERS | 9 +
drivers/iio/Kconfig | 3 +-
drivers/iio/counter/104-quad-8.c | 257 +++++-
drivers/iio/counter/Kconfig | 35 +-
drivers/iio/counter/Makefile | 6 +
drivers/iio/counter/dummy-counter.c | 308 +++++++
drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
include/linux/iio/counter.h | 629 +++++++++++++
17 files changed, 5216 insertions(+), 15 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
create mode 100644 Documentation/driver-api/iio/generic-counter.rst
create mode 100644 Documentation/driver-api/iio/quad-counter.rst
create mode 100644 Documentation/driver-api/iio/simple-counter.rst
create mode 100644 drivers/iio/counter/dummy-counter.c
create mode 100644 drivers/iio/counter/generic-counter.c
create mode 100644 drivers/iio/counter/quad-counter.c
create mode 100644 drivers/iio/counter/simple-counter.c
create mode 100644 include/linux/iio/counter.h
--
2.15.1
This patch introduces the 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 where possible rather than the Generic Counter interface
directly.
In the context of the Generic Counter interface, a counter is defined as
a device that reports one or more "counter counts" based on the state
changes of one or more "counter signals" as evaluated by a defined
"counter function."
Driver callbacks should be provided to communicate with the device: to
read and write various Signals and Counts, and to set and get the
"action mode" and "function mode" for various Synapses and Counts
respectively.
To support a counter device, a driver must first allocate the available
Counter Signals via counter_signal structures. These Signals should
be stored as an array and set to the signals array member of an
allocated counter_device structure before the Counter is registered to
the system.
Counter Counts may be allocated via counter_count structures, and
respective Counter Signal associations (Synapses) made via
counter_synapse structures. Associated counter_synapse structures are
stored as an array and set to the the synapses array member of the
respective counter_count structure. These counter_count structures are
set to the counts array member of an allocated counter_device structure
before the Counter is registered to the system.
A counter device is registered to the system by passing the respective
initialized counter_device structure to the counter_register function;
similarly, the counter_unregister function unregisters the respective
Counter. The devm_counter_register and devm_counter_unregister functions
serve as device memory-managed versions of the counter_register and
counter_unregister functions respectively.
Signed-off-by: William Breathitt Gray <[email protected]>
---
MAINTAINERS | 7 +
drivers/iio/Kconfig | 3 +-
drivers/iio/counter/Kconfig | 16 +-
drivers/iio/counter/Makefile | 3 +
drivers/iio/counter/generic-counter.c | 992 ++++++++++++++++++++++++++++++++++
include/linux/iio/counter.h | 239 ++++++++
6 files changed, 1255 insertions(+), 5 deletions(-)
create mode 100644 drivers/iio/counter/generic-counter.c
create mode 100644 include/linux/iio/counter.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 36f76be322a3..07dd7b933bfa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3661,6 +3661,13 @@ W: http://www.fi.muni.cz/~kas/cosa/
S: Maintained
F: drivers/net/wan/cosa*
+COUNTER INTERFACE
+M: William Breathitt Gray <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/iio/counter/
+F: include/linux/iio/counter.h
+
CPMAC ETHERNET DRIVER
M: Florian Fainelli <[email protected]>
L: [email protected]
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b3c8c6ef0dff..62a923aeb462 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -73,7 +73,6 @@ source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
-source "drivers/iio/counter/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
@@ -95,3 +94,5 @@ source "drivers/iio/proximity/Kconfig"
source "drivers/iio/temperature/Kconfig"
endif # IIO
+
+source "drivers/iio/counter/Kconfig"
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 474e1ac4e7c0..4eaf4e53c5aa 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -3,11 +3,18 @@
#
# When adding new entries keep the list in alphabetical order
-menu "Counters"
+menuconfig COUNTER
+ tristate "Counter support"
+ help
+ Provides support for Counter devices. The Generic Counter API provides
+ rudimentary support for counters and serves as building blocks to
+ create more complex counter interfaces.
+
+if COUNTER
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
- depends on PC104 && X86 && ISA_BUS_API
+ depends on PC104 && X86 && ISA_BUS_API && IIO
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
@@ -23,11 +30,12 @@ config 104_QUAD_8
config STM32_LPTIMER_CNT
tristate "STM32 LP Timer encoder counter driver"
- depends on MFD_STM32_LPTIMER || COMPILE_TEST
+ depends on (MFD_STM32_LPTIMER || COMPILE_TEST) && IIO
help
Select this option to enable STM32 Low-Power Timer quadrature encoder
and counter driver.
To compile this driver as a module, choose M here: the
module will be called stm32-lptimer-cnt.
-endmenu
+
+endif # COUNTER
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 1b9a896eb488..513c49d832d4 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -4,5 +4,8 @@
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_COUNTER) += counter.o
+counter-$(CONFIG_COUNTER) += generic-counter.o
+
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/generic-counter.c b/drivers/iio/counter/generic-counter.c
new file mode 100644
index 000000000000..0efd36ee2118
--- /dev/null
+++ b/drivers/iio/counter/generic-counter.c
@@ -0,0 +1,992 @@
+/*
+ * Generic 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.
+ */
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include <linux/iio/counter.h>
+
+struct counter_device_attr {
+ struct device_attribute dev_attr;
+ struct list_head l;
+ void *component;
+ void *component_data;
+};
+
+static int counter_attribute_create(const char *const prefix,
+ const char *const name,
+ ssize_t (*show)(struct device *dev, struct device_attribute *attr,
+ char *buf),
+ ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len),
+ void *const component, void *const component_data,
+ struct counter_device_state *const device_state)
+{
+ struct counter_device_attr *counter_attr;
+ struct device_attribute *dev_attr;
+ struct counter_device_attr *t;
+ int err;
+ struct list_head *const attr_list = &device_state->attr_list;
+
+ /* Allocate a Counter device attribute */
+ counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
+ if (!counter_attr)
+ return -ENOMEM;
+ dev_attr = &counter_attr->dev_attr;
+
+ sysfs_attr_init(&dev_attr->attr);
+
+ /* Configure device attribute */
+ dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", prefix, name);
+ if (!dev_attr->attr.name) {
+ err = -ENOMEM;
+ goto err_free_counter_attr;
+ }
+ if (show) {
+ dev_attr->attr.mode |= 0444;
+ dev_attr->show = show;
+ }
+ if (store) {
+ dev_attr->attr.mode |= 0200;
+ dev_attr->store = store;
+ }
+
+ /* Check for duplicate name */
+ list_for_each_entry(t, attr_list, l)
+ if (!strcmp(t->dev_attr.attr.name, dev_attr->attr.name)) {
+ dev_err(&device_state->dev,
+ "tried to double register : %s\n",
+ t->dev_attr.attr.name);
+ err = -EBUSY;
+ goto err_free_attr_name;
+ }
+
+ /* Store associated Counter component with attribute */
+ counter_attr->component = component;
+ counter_attr->component_data = component_data;
+
+ /* Keep track of the attribute for later cleanup */
+ list_add(&counter_attr->l, attr_list);
+ device_state->num_attr++;
+
+ return 0;
+
+err_free_attr_name:
+ kfree(dev_attr->attr.name);
+err_free_counter_attr:
+ kfree(counter_attr);
+ return err;
+}
+
+#define to_counter_attr(_dev_attr) \
+ container_of(_dev_attr, struct counter_device_attr, dev_attr)
+
+static ssize_t counter_signal_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_signal *const signal = to_counter_attr(attr)->component;
+
+ return counter->signal_read(counter, signal, buf);
+}
+
+static ssize_t counter_signal_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_signal *const signal = to_counter_attr(attr)->component;
+
+ return counter->signal_write(counter, signal, buf, len);
+}
+
+static ssize_t counter_device_attr_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const char *const name = to_counter_attr(attr)->component;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", name);
+}
+
+static ssize_t counter_signal_ext_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const struct counter_signal_ext *const ext = devattr->component_data;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_signal *const signal = devattr->component;
+
+ return ext->read(counter, signal, ext->priv, buf);
+}
+
+static ssize_t counter_signal_ext_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const struct counter_signal_ext *const ext = devattr->component_data;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_signal *const signal = devattr->component;
+
+ return ext->write(counter, signal, ext->priv, buf, len);
+}
+
+static int counter_signal_ext_register(const char *const prefix,
+ struct counter_signal *const signal,
+ struct counter_device_state *const device_state)
+{
+ const size_t num_ext = signal->num_ext;
+ size_t i;
+ const struct counter_signal_ext *ext;
+ int err;
+
+ /* Return early if no extensions */
+ if (!signal->ext || !num_ext)
+ return 0;
+
+ /* Create an attribute for each extension */
+ for (i = 0 ; i < num_ext; i++) {
+ ext = signal->ext + i;
+ err = counter_attribute_create(prefix, ext->name,
+ (ext->read) ? counter_signal_ext_show : NULL,
+ (ext->write) ? counter_signal_ext_store : NULL,
+ signal, ext, device_state);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int counter_signal_attributes_create(const char *const signal_attr_name,
+ const struct counter_device *const counter,
+ struct counter_signal *const signal)
+{
+ struct counter_device_state *const device_state = counter->device_state;
+ int err;
+ const char *prefix;
+
+ /* Create main Signal attribute */
+ err = counter_attribute_create("", signal_attr_name,
+ (counter->signal_read) ? counter_signal_show : NULL,
+ (counter->signal_write) ? counter_signal_store : NULL,
+ signal, NULL, device_state);
+ if (err)
+ return err;
+
+ prefix = kasprintf(GFP_KERNEL, "%s_", signal_attr_name);
+ if (!prefix)
+ return -ENOMEM;
+
+ /* Create Signal name attribute */
+ if (signal->name) {
+ err = counter_attribute_create(prefix, "name",
+ counter_device_attr_name_show, NULL, signal->name, NULL,
+ device_state);
+ if (err)
+ goto err_free_prefix;
+ }
+
+ /* Register Signal extension attributes */
+ err = counter_signal_ext_register(prefix, signal, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ kfree(prefix);
+
+ return 0;
+
+err_free_prefix:
+ kfree(prefix);
+ return err;
+}
+
+static int counter_signals_register(const struct counter_device *const counter)
+{
+ const size_t num_signals = counter->num_signals;
+ struct counter_device_state *const device_state = counter->device_state;
+ struct device *const dev = &device_state->dev;
+ size_t i;
+ struct counter_signal *signal;
+ const char *name;
+ int err;
+
+ /* At least one Signal must be defined */
+ if (!counter->signals || !num_signals) {
+ dev_err(dev, "Signals undefined\n");
+ return -EINVAL;
+ }
+
+ /* Register each Signal */
+ for (i = 0; i < num_signals; i++) {
+ signal = counter->signals + i;
+
+ /* Generate Signal attribute name */
+ name = kasprintf(GFP_KERNEL, "signal%d", signal->id);
+ if (!name)
+ return -ENOMEM;
+
+ /* Create all attributes associated with Signal */
+ err = counter_signal_attributes_create(name, counter, signal);
+ if (err)
+ goto err_free_name;
+
+ kfree(name);
+ }
+
+ return 0;
+
+err_free_name:
+ kfree(name);
+ return err;
+}
+
+static ssize_t counter_action_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ int err;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = devattr->component_data;
+ struct counter_synapse *const synapse = devattr->component;
+ size_t action;
+
+ err = counter->action_get(counter, count, synapse, &action);
+ if (err)
+ return err;
+
+ synapse->action = action;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", synapse->actions[action]);
+}
+
+static ssize_t counter_action_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ struct counter_synapse *const synapse = devattr->component;
+ size_t action;
+ const size_t num_actions = synapse->num_actions;
+ int err;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = devattr->component_data;
+
+ /* Find requested action mode */
+ for (action = 0; action < num_actions; action++)
+ if (sysfs_streq(buf, synapse->actions[action]))
+ break;
+ /* If requested action mode not found */
+ if (action >= num_actions)
+ return -EINVAL;
+
+ err = counter->action_set(counter, count, synapse, action);
+ if (err)
+ return err;
+
+ synapse->action = action;
+
+ return len;
+}
+
+static ssize_t counter_device_attr_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const char *const *const modes = devattr->component;
+ const size_t num_modes = *(size_t *)devattr->component_data;
+ ssize_t len = 0;
+ size_t i;
+
+ for (i = 0; i < num_modes; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", modes[i]);
+
+ return len;
+}
+
+static int counter_synapses_register(const struct counter_device *const counter,
+ struct counter_count *const count, const char *const count_attr_name)
+{
+ struct counter_device_state *const device_state = counter->device_state;
+ const size_t num_synapses = count->num_synapses;
+ struct device *const dev = &device_state->dev;
+ size_t i;
+ struct counter_synapse *synapse;
+ const char *prefix;
+ int err;
+
+ /* At least one Synapse must be defined */
+ if (!count->synapses || !num_synapses) {
+ dev_err(dev, "Count '%d' Synapses undefined\n", count->id);
+ return -EINVAL;
+ }
+
+ /* Register each Synapse */
+ for (i = 0; i < num_synapses; i++) {
+ synapse = count->synapses + i;
+
+ /* Ensure all Synapses have a defined Signal */
+ if (!synapse->signal) {
+ dev_err(dev,
+ "Count '%d' Synapse '%zu' Signal undefined\n",
+ count->id, i);
+ return -EINVAL;
+ }
+
+ /* At least one action mode must be defined for each Synapse */
+ if (!synapse->actions || !synapse->num_actions) {
+ dev_err(dev,
+ "Count '%d' Signal '%d' action modes undefined\n",
+ count->id, synapse->signal->id);
+ return -EINVAL;
+ }
+
+ /* Generate attribute prefix */
+ prefix = kasprintf(GFP_KERNEL, "%s_signal%d_", count_attr_name,
+ synapse->signal->id);
+ if (!prefix)
+ return -ENOMEM;
+
+ /* Create action attribute */
+ err = counter_attribute_create(prefix, "action",
+ (counter->action_get) ? counter_action_show : NULL,
+ (counter->action_set) ? counter_action_store : NULL,
+ synapse, count, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ /* Create action_available attribute */
+ err = counter_attribute_create(prefix, "action_available",
+ counter_device_attr_available_show, NULL,
+ synapse->actions, &synapse->num_actions, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ kfree(prefix);
+ }
+
+ return 0;
+
+err_free_prefix:
+ kfree(prefix);
+ return err;
+}
+
+static ssize_t counter_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = to_counter_attr(attr)->component;
+
+ return counter->count_read(counter, count, buf);
+}
+
+static ssize_t counter_count_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = to_counter_attr(attr)->component;
+
+ return counter->count_write(counter, count, buf, len);
+}
+
+static ssize_t counter_function_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = to_counter_attr(attr)->component;
+ size_t function;
+
+ err = counter->function_get(counter, count, &function);
+ if (err)
+ return err;
+
+ count->function = function;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", count->functions[function]);
+}
+
+static ssize_t counter_function_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct counter_count *const count = to_counter_attr(attr)->component;
+ const size_t num_functions = count->num_functions;
+ size_t function;
+ int err;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+
+ /* Find requested Count function mode */
+ for (function = 0; function < num_functions; function++)
+ if (sysfs_streq(buf, count->functions[function]))
+ break;
+ /* Return error if requested Count function mode not found */
+ if (function >= num_functions)
+ return -EINVAL;
+
+ err = counter->function_set(counter, count, function);
+ if (err)
+ return err;
+
+ count->function = function;
+
+ return len;
+}
+
+static ssize_t counter_count_synapses_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ size_t i;
+ struct counter_count *const count = to_counter_attr(attr)->component;
+ const struct counter_synapse *synapse;
+ ssize_t len = 0;
+
+ /* Print out a list of every Signal association to Count */
+ for (i = 0; i < count->num_synapses; i++) {
+ synapse = count->synapses + i;
+ len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
+ synapse->signal->id, synapse->signal->name,
+ synapse->actions[synapse->action]);
+ if (len >= PAGE_SIZE)
+ return -ENOMEM;
+ }
+
+ return len;
+}
+
+static ssize_t counter_count_ext_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const struct counter_count_ext *const ext = devattr->component_data;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = devattr->component;
+
+ return ext->read(counter, count, ext->priv, buf);
+}
+
+static ssize_t counter_count_ext_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const struct counter_count_ext *const ext = devattr->component_data;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_count *const count = devattr->component;
+
+ return ext->write(counter, count, ext->priv, buf, len);
+}
+
+static int counter_count_ext_register(const char *const prefix,
+ struct counter_count *const count,
+ struct counter_device_state *const device_state)
+{
+ const size_t num_ext = count->num_ext;
+ size_t i;
+ const struct counter_count_ext *ext;
+ int err;
+
+ /* Return early if no extensions */
+ if (!count->ext || !num_ext)
+ return 0;
+
+ /* Create an attribute for each extension */
+ for (i = 0 ; i < num_ext; i++) {
+ ext = count->ext + i;
+ err = counter_attribute_create(prefix, ext->name,
+ (ext->read) ? counter_count_ext_show : NULL,
+ (ext->write) ? counter_count_ext_store : NULL,
+ count, ext, device_state);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int counter_count_attributes_create(const char *const count_attr_name,
+ const struct counter_device *const counter,
+ struct counter_count *const count)
+{
+ struct counter_device_state *const device_state = counter->device_state;
+ int err;
+ const char *prefix;
+
+ /* Create main Count attribute */
+ err = counter_attribute_create("", count_attr_name,
+ (counter->count_read) ? counter_count_show : NULL,
+ (counter->count_write) ? counter_count_store : NULL,
+ count, NULL, device_state);
+ if (err)
+ return err;
+
+ prefix = kasprintf(GFP_KERNEL, "%s_", count_attr_name);
+ if (!prefix)
+ return -ENOMEM;
+
+ /* Create Count function attribute */
+ err = counter_attribute_create(prefix, "function",
+ (counter->function_get) ? counter_function_show : NULL,
+ (counter->function_set) ? counter_function_store : NULL,
+ count, NULL, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ /* Create Count function_available attribute */
+ err = counter_attribute_create(prefix, "function_available",
+ counter_device_attr_available_show, NULL, count->functions,
+ &count->num_functions, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ /* Create Count synapses attribute */
+ err = counter_attribute_create(prefix, "synapses",
+ counter_count_synapses_show, NULL, count, NULL, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ /* Create Count name attribute */
+ if (count->name) {
+ err = counter_attribute_create(prefix, "name",
+ counter_device_attr_name_show, NULL, count->name, NULL,
+ device_state);
+ if (err)
+ goto err_free_prefix;
+ }
+
+ /* Register Count extension attributes */
+ err = counter_count_ext_register(prefix, count, device_state);
+ if (err)
+ goto err_free_prefix;
+
+ kfree(prefix);
+
+ return 0;
+
+err_free_prefix:
+ kfree(prefix);
+ return err;
+}
+
+static int counter_counts_register(const struct counter_device *const counter)
+{
+ const size_t num_counts = counter->num_counts;
+ struct device *const dev = &counter->device_state->dev;
+ size_t i;
+ struct counter_count *count;
+ const char *name;
+ int err;
+
+ /* At least one Count must be defined */
+ if (!counter->counts || !num_counts) {
+ dev_err(dev, "Counts undefined\n");
+ return -EINVAL;
+ }
+
+ /* Register each Count */
+ for (i = 0; i < num_counts; i++) {
+ count = counter->counts + i;
+
+ /* At least one function mode must be defined for each Count */
+ if (!count->functions || !count->num_functions) {
+ dev_err(dev, "Count '%d' function modes undefined\n",
+ count->id);
+ return -EINVAL;
+ }
+
+ /* Generate attribute name */
+ name = kasprintf(GFP_KERNEL, "count%d", count->id);
+ if (!name)
+ return -ENOMEM;
+
+ /* Register the Synapses associated with each Count */
+ err = counter_synapses_register(counter, count, name);
+ if (err)
+ goto err_free_name;
+
+ /* Create all attributes associated with Count */
+ err = counter_count_attributes_create(name, counter, count);
+ if (err)
+ goto err_free_name;
+
+ kfree(name);
+ }
+
+ return 0;
+
+err_free_name:
+ kfree(name);
+ return err;
+}
+
+static struct bus_type counter_bus_type = {
+ .name = "counter"
+};
+
+static dev_t counter_devt;
+
+#define COUNTER_DEV_MAX 256
+
+static int __init counter_init(void)
+{
+ int err;
+
+ err = bus_register(&counter_bus_type);
+ if (err) {
+ pr_err("counter: could not register Counter bus type\n");
+ return err;
+ }
+
+ err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+ if (err) {
+ pr_err("counter: failed to allocate char dev region\n");
+ bus_unregister(&counter_bus_type);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit counter_exit(void)
+{
+ if (counter_devt)
+ unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
+ bus_unregister(&counter_bus_type);
+}
+
+static void free_counter_device_state_attr_list(struct list_head *attr_list)
+{
+ struct counter_device_attr *p, *n;
+
+ list_for_each_entry_safe(p, n, attr_list, l) {
+ kfree(p->dev_attr.attr.name);
+ list_del(&p->l);
+ kfree(p);
+ }
+}
+
+/* Provides a unique ID for each counter device */
+static DEFINE_IDA(counter_ida);
+
+static void counter_device_release(struct device *dev)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct counter_device_state *const device_state = counter->device_state;
+
+ kfree(device_state->attr_group.attrs);
+ free_counter_device_state_attr_list(&device_state->attr_list);
+ ida_simple_remove(&counter_ida, device_state->id);
+ kfree(device_state);
+}
+
+static struct device_type counter_device_type = {
+ .name = "counter_device",
+ .release = counter_device_release
+};
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+ struct counter_device_state *const device_state = container_of(
+ inode->i_cdev, struct counter_device_state, chrdev);
+ struct device *const dev = &device_state->dev;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+
+ get_device(dev);
+ filp->private_data = counter;
+
+ return 0;
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+ struct counter_device_state *const device_state = container_of(
+ inode->i_cdev, struct counter_device_state, chrdev);
+
+ put_device(&device_state->dev);
+
+ return 0;
+}
+
+static const struct file_operations counter_fileops = {
+ .owner = THIS_MODULE,
+ .open = counter_chrdev_open,
+ .release = counter_chrdev_release
+};
+
+static ssize_t counter_device_ext_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const struct counter_device_ext *const ext = devattr->component_data;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+
+ return ext->read(counter, ext->priv, buf);
+}
+
+static ssize_t counter_device_ext_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ const struct counter_device_attr *const devattr = to_counter_attr(attr);
+ const struct counter_device_ext *const ext = devattr->component_data;
+ struct counter_device *const counter = dev_get_drvdata(dev);
+
+ return ext->write(counter, ext->priv, buf, len);
+}
+
+static int counter_device_ext_register(struct counter_device *const counter)
+{
+ const size_t num_ext = counter->num_ext;
+ size_t i;
+ const struct counter_device_ext *ext;
+ int err;
+
+ /* Return early if no extensions */
+ if (!counter->ext || !num_ext)
+ return 0;
+
+ /* Create an attribute for each extension */
+ for (i = 0 ; i < num_ext; i++) {
+ ext = counter->ext + i;
+ err = counter_attribute_create("", ext->name,
+ (ext->read) ? counter_device_ext_show : NULL,
+ (ext->write) ? counter_device_ext_store : NULL,
+ NULL, ext, counter->device_state);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static ssize_t counter_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct counter_device *const counter = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", counter->name);
+}
+
+static DEVICE_ATTR_RO(counter_name);
+
+/**
+ * counter_register - register Counter to the system
+ * @counter: pointer to Counter to register
+ *
+ * This function registers a Counter to the system. A sysfs "counter" directory
+ * will be created and populated with sysfs attributes correlating with the
+ * Counter Signals, Synapses, and Counts respectively.
+ */
+int counter_register(struct counter_device *const counter)
+{
+ int err;
+ struct counter_device_state *device_state;
+ struct counter_device_attr *p;
+ size_t i = 0;
+
+ if (!counter)
+ return -EINVAL;
+
+ /* Allocate internal state container for Counter device */
+ device_state = kzalloc(sizeof(*device_state), GFP_KERNEL);
+ if (!device_state)
+ return -ENOMEM;
+ counter->device_state = device_state;
+
+ /* Acquire unique ID */
+ device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
+ if (device_state->id < 0) {
+ err = device_state->id;
+ goto err_free_device_state;
+ }
+
+ /* Configure device structure for Counter */
+ device_state->dev.type = &counter_device_type;
+ device_state->dev.bus = &counter_bus_type;
+ if (counter->parent) {
+ device_state->dev.parent = counter->parent;
+ device_state->dev.of_node = counter->parent->of_node;
+ }
+ dev_set_name(&device_state->dev, "counter%d", device_state->id);
+ device_initialize(&device_state->dev);
+ dev_set_drvdata(&device_state->dev, counter);
+ device_state->dev.devt = MKDEV(MAJOR(counter_devt), device_state->id);
+
+ /* Initialize attribute list */
+ INIT_LIST_HEAD(&device_state->attr_list);
+
+ /* Verify Signals are valid and register */
+ err = counter_signals_register(counter);
+ if (err)
+ goto err_free_attributes;
+
+ /* Verify Counts and respective Synapses are valid and register */
+ err = counter_counts_register(counter);
+ if (err)
+ goto err_free_attributes;
+
+ /* Register Counter device extension attributes */
+ err = counter_device_ext_register(counter);
+ if (err)
+ goto err_free_attributes;
+
+ /* Account for name attribute */
+ if (counter->name)
+ device_state->num_attr++;
+
+ /* Allocate space for attribute pointers in attribute group */
+ device_state->attr_group.attrs = kcalloc(device_state->num_attr + 1,
+ sizeof(*device_state->attr_group.attrs), GFP_KERNEL);
+ if (!device_state->attr_group.attrs) {
+ err = -ENOMEM;
+ goto err_free_attributes;
+ }
+
+ /* Add attribute pointers to attribute group */
+ list_for_each_entry(p, &device_state->attr_list, l)
+ device_state->attr_group.attrs[i++] = &p->dev_attr.attr;
+ if (counter->name)
+ device_state->attr_group.attrs[i] = &dev_attr_counter_name.attr;
+
+ /* Associate attributes with device */
+ device_state->groups[0] = &device_state->attr_group;
+ device_state->dev.groups = device_state->groups;
+
+ /* Initialize associated character device */
+ cdev_init(&device_state->chrdev, &counter_fileops);
+ device_state->chrdev.owner = THIS_MODULE;
+ err = cdev_device_add(&device_state->chrdev, &device_state->dev);
+ if (err)
+ goto err_free_attr_group_attrs;
+
+ return 0;
+
+err_free_attr_group_attrs:
+ kfree(counter->device_state->attr_group.attrs);
+err_free_attributes:
+ free_counter_device_state_attr_list(&counter->device_state->attr_list);
+ ida_simple_remove(&counter_ida, counter->device_state->id);
+err_free_device_state:
+ kfree(counter->device_state);
+ return err;
+}
+EXPORT_SYMBOL(counter_register);
+
+/**
+ * counter_unregister - unregister Counter from the system
+ * @counter: pointer to Counter to unregister
+ *
+ * The Counter is unregistered from the system; all allocated memory is freed.
+ */
+void counter_unregister(struct counter_device *const counter)
+{
+ struct cdev *cdev;
+ struct device *dev;
+
+ if (counter) {
+ cdev = &counter->device_state->chrdev;
+ dev = &counter->device_state->dev;
+
+ cdev_device_del(cdev, dev);
+ put_device(dev);
+ }
+}
+EXPORT_SYMBOL(counter_unregister);
+
+static void devm_counter_unreg(struct device *dev, void *res)
+{
+ counter_unregister(*(struct counter_device **)res);
+}
+
+/**
+ * devm_counter_register - Resource-managed counter_register
+ * @dev: device to allocate counter_device for
+ * @counter: pointer to Counter to register
+ *
+ * Managed counter_register. The Counter registered with this function is
+ * automatically unregistered on driver detach. This function calls
+ * counter_register internally. Refer to that function for more information.
+ *
+ * If an Counter registered with this function needs to be unregistered
+ * separately, devm_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_counter_register(struct device *dev,
+ struct counter_device *const counter)
+{
+ struct counter_device **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = counter_register(counter);
+ if (!ret) {
+ *ptr = counter;
+ devres_add(dev, ptr);
+ } else
+ devres_free(ptr);
+
+ return ret;
+}
+EXPORT_SYMBOL(devm_counter_register);
+
+static int devm_counter_match(struct device *dev, void *res, void *data)
+{
+ struct counter_device **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_counter_unregister - Resource-managed counter_unregister
+ * @dev: device this counter_device belongs to
+ * @counter: the Counter associated with the device
+ *
+ * Unregister Counter registered with devm_counter_register.
+ */
+void devm_counter_unregister(struct device *dev,
+ struct counter_device *const counter)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_counter_unreg,
+ devm_counter_match, counter);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_counter_unregister);
+
+subsys_initcall(counter_init);
+module_exit(counter_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
+MODULE_DESCRIPTION("Generic Counter interface");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
new file mode 100644
index 000000000000..070ed8fd53fb
--- /dev/null
+++ b/include/linux/iio/counter.h
@@ -0,0 +1,239 @@
+/*
+ * 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 _COUNTER_H_
+#define _COUNTER_H_
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+struct counter_device;
+struct counter_signal;
+
+/**
+ * struct counter_signal_ext - Counter Signal extensions
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct counter_signal_ext {
+ const char *name;
+ ssize_t (*read)(struct counter_device *counter,
+ struct counter_signal *signal, void *priv,
+ char *buf);
+ ssize_t (*write)(struct counter_device *counter,
+ struct counter_signal *signal, void *priv,
+ const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct counter_signal - Counter Signal node
+ * @id: [DRIVER] unique ID used to identify signal
+ * @name: [DRIVER] device-specific signal name
+ * @ext: [DRIVER] optional array of Counter Signal extensions
+ * @num_ext: [DRIVER] number of Counter Signal extensions specified in @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct counter_signal {
+ int id;
+ const char *name;
+
+ const struct counter_signal_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+/**
+ * struct counter_synapse - Counter Synapse node
+ * @action: [DRIVER] current action mode
+ * @actions: [DRIVER] available action modes
+ * @num_actions: [DRIVER] number of action modes specified in @actions
+ * @signal: [DRIVER] pointer to associated signal
+ */
+struct counter_synapse {
+ size_t action;
+ const char *const *actions;
+ size_t num_actions;
+ struct counter_signal *signal;
+};
+
+struct counter_count;
+
+/**
+ * struct counter_count_ext - Counter Count extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct counter_count_ext {
+ const char *name;
+ ssize_t (*read)(struct counter_device *counter,
+ struct counter_count *count, void *priv,
+ char *buf);
+ ssize_t (*write)(struct counter_device *counter,
+ struct counter_count *count, void *priv,
+ const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct counter_count - Counter Count node
+ * @id: [DRIVER] unique ID used to identify Count
+ * @name: [DRIVER] device-specific Count name
+ * @function: [DRIVER] current function mode
+ * @functions: [DRIVER] available function modes
+ * @num_functions: [DRIVER] number of functions specified in @functions
+ * @synapses: [DRIVER] array of synapses for initialization
+ * @num_synapses: [DRIVER] number of synapses specified in @synapses
+ * @ext: [DRIVER] optional array of Counter Count extensions
+ * @num_ext: [DRIVER] number of Counter Count extensions specified in
+ * @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct counter_count {
+ int id;
+ const char *name;
+ size_t function;
+ const char *const *functions;
+ size_t num_functions;
+
+ struct counter_synapse *synapses;
+ size_t num_synapses;
+
+ const struct counter_count_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+/**
+ * struct counter_device_state - internal state container for a Counter device
+ * @id: unique ID used to identify the Counter
+ * @dev: internal device structure
+ * @chrdev: associated character device
+ * @attr_list: list to keep track of created Counter sysfs attributes
+ * @attr_group: Counter sysfs attributes group
+ * @groups: attribute groups
+ * @num_attr: number of Counter sysfs attributes
+ */
+struct counter_device_state {
+ int id;
+ struct device dev;
+ struct cdev chrdev;
+ struct list_head attr_list;
+ struct attribute_group attr_group;
+ const struct attribute_group *groups[2];
+ size_t num_attr;
+};
+
+/**
+ * struct counter_device_ext - Counter device extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct counter_device_ext {
+ const char *name;
+ ssize_t (*read)(struct counter_device *counter, void *priv,
+ char *buf);
+ ssize_t (*write)(struct counter_device *counter, void *priv,
+ const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct counter_device - Counter data structure
+ * @name: [DRIVER] name of the device
+ * @parent: [DRIVER] optional parent device providing the counters
+ * @device_state: [INTERN] internal device state container
+ * @signal_read: [DRIVER] read callback for Signal attribute; may be NULL
+ * @signal_write: [DRIVER] write callback for Signal attribute; may be
+ * NULL
+ * @count_read: [DRIVER] read callback for Count attribute; may be NULL
+ * @count_write: [DRIVER] write callback for Count attribute; may be NULL
+ * @function_get: [DRIVER] function to get the current count function
+ * mode. Returns 0 on success and negative error code on
+ * error. The index of the respective Count's returned
+ * function mode should be passed back via the function
+ * parameter.
+ * @function_set: [DRIVER] function to set the count function mode.
+ * function is the index of the requested function mode
+ * from the respective Count's functions array.
+ * @action_get: [DRIVER] function to get the current action mode.
+ * Returns 0 on success and negative error code on error.
+ * The index of the respective Signal's returned action
+ * mode should be passed back via the action parameter.
+ * @action_set: [DRIVER] function to set the action mode. action is the
+ * index of the requested action mode from the respective
+ * Synapse's actions array.
+ * @signals: [DRIVER] array of Signals
+ * @num_signals: [DRIVER] number of Signals specified in @signals
+ * @counts: [DRIVER] array of Counts
+ * @num_counts: [DRIVER] number of Counts specified in @counts
+ * @ext: [DRIVER] optional array of Counter device extensions
+ * @num_ext: [DRIVER] number of Counter device extensions specified
+ * in @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct counter_device {
+ const char *name;
+ struct device *parent;
+ struct counter_device_state *device_state;
+
+ ssize_t (*signal_read)(struct counter_device *counter,
+ struct counter_signal *signal, char *buf);
+ ssize_t (*signal_write)(struct counter_device *counter,
+ struct counter_signal *signal, const char *buf,
+ size_t len);
+ ssize_t (*count_read)(struct counter_device *counter,
+ struct counter_count *count, char *buf);
+ ssize_t (*count_write)(struct counter_device *counter,
+ struct counter_count *count, const char *buf,
+ size_t len);
+ int (*function_get)(struct counter_device *counter,
+ struct counter_count *count, size_t *function);
+ int (*function_set)(struct counter_device *counter,
+ struct counter_count *count, size_t function);
+ int (*action_get)(struct counter_device *counter,
+ struct counter_count *count,
+ struct counter_synapse *synapse, size_t *action);
+ int (*action_set)(struct counter_device *counter,
+ struct counter_count *count,
+ struct counter_synapse *synapse, size_t action);
+
+ struct counter_signal *signals;
+ size_t num_signals;
+ struct counter_count *counts;
+ size_t num_counts;
+
+ const struct counter_device_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+extern int counter_register(struct counter_device *const counter);
+extern void counter_unregister(struct counter_device *const counter);
+extern int devm_counter_register(struct device *dev,
+ struct counter_device *const counter);
+extern void devm_counter_unregister(struct device *dev,
+ struct counter_device *const counter);
+
+#endif /* _COUNTER_H_ */
--
2.15.1
This patch adds standard documentation for the userspace sysfs
attributes of the Generic Counter interface.
Signed-off-by: William Breathitt Gray <[email protected]>
---
.../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 74 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
diff --git a/Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs b/Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
new file mode 100644
index 000000000000..3b1c3c4498d1
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
@@ -0,0 +1,73 @@
+What: /sys/bus/counter/devices/counterX/countY
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count data of Count Y. Typically, this is an accumulated count
+ value.
+
+What: /sys/bus/counter/devices/counterX/countY_function
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count function mode of Count Y; count function evaluation is
+ triggered by conditions specified by the countY_signalZ_action
+ attributes.
+
+What: /sys/bus/counter/devices/counterX/countY_function_available
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Discrete set of available count function modes for the
+ configuration of the respective Count Y are listed in this file.
+
+What: /sys/bus/counter/devices/counterX/countY_name
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Read-only attribute that indicates the device-specific name of
+ Count Y.
+
+What: /sys/bus/counter/devices/counterX/countY_signalZ_action
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Action mode of Count Y for Signal Z. This attribute indicates
+ the condition of Signal Z that triggers the count function
+ evaluation for Count Y.
+
+What: /sys/bus/counter/devices/counterX/countY_signalZ_action_available
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Discrete set of available action modes are listed in this file
+ for the configuration of the respective Synapse associating
+ Signal Z to Count Y.
+
+What: /sys/bus/counter/devices/counterX/counter_name
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Read-only attribute that indicates the device-specific name of
+ the Counter.
+
+What: /sys/bus/counter/devices/counterX/signalY
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Signal data of Signal Y. Typically, this is an input line level
+ state.
+
+What: /sys/bus/counter/devices/counterX/signalY_name
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Read-only attribute that indicates the device-specific name of
+ Signal Y.
+
+What: /sys/bus/counter/devices/counterX/countY_synapses
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ List of Synapses associating Signals to Count Y. The columns
+ represent the following in the respective order: Signal ID,
+ Signal name, and current action mode.
diff --git a/MAINTAINERS b/MAINTAINERS
index 07dd7b933bfa..38da1bc615b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3665,6 +3665,7 @@ COUNTER INTERFACE
M: William Breathitt Gray <[email protected]>
L: [email protected]
S: Maintained
+F: Documentation/ABI/testing/sysfs-bus-counter-*
F: drivers/iio/counter/
F: include/linux/iio/counter.h
--
2.15.1
This patch adds high-level documentation about the Generic Counter
interface.
Signed-off-by: William Breathitt Gray <[email protected]>
---
Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++++++++++++++++
Documentation/driver-api/iio/index.rst | 1 +
MAINTAINERS | 1 +
3 files changed, 436 insertions(+)
create mode 100644 Documentation/driver-api/iio/generic-counter.rst
diff --git a/Documentation/driver-api/iio/generic-counter.rst b/Documentation/driver-api/iio/generic-counter.rst
new file mode 100644
index 000000000000..219c571e90ce
--- /dev/null
+++ b/Documentation/driver-api/iio/generic-counter.rst
@@ -0,0 +1,434 @@
+=========================
+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 tally counters, 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:
+
+ COUNT
+ -----
+ A Count represents the count data for a set of Signals. A Count
+ has a count function mode (e.g. "increase" or "quadrature x4")
+ which represents the update behavior for the count data. A Count
+ also has a set of one or more associated Signals.
+
+ SIGNAL
+ ------
+ A Signal represents a counter input data; this is the data that
+ is typically analyzed by the counter to determine the count
+ data. A Signal may be associated to one or more Counts.
+
+ SYNAPSE
+ -------
+ A Synapse represents the association of a Signal with a
+ respective Count. Signal data affects respective Count data, and
+ the Synapse represents this relationship. The Synapse action
+ mode (e.g. "rising edge" or "double pulse") specifies the Signal
+ data condition which triggers the respective Count's count
+ function evaluation to update the count data. It is possible for
+ the Synapse action mode to be "none" if a Signal is associated
+ with a Count but does not trigger the count function (e.g. the
+ direction signal line for a Pulse-Direction encoding counter).
+
+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 Counts
+each associated to a set of Signals, whose respective Synapse instances
+represent the count function update conditions for the associated
+Counts.
+
+Paradigm
+========
+
+The most basic counter device may be expressed as a single Count
+associated with a single Signal via a single Synapse. Take for example
+a counter device which simply accumulates a count of rising edges on a
+source input line.
+
+ Count Synapse Signal
+ ----- ------- ------
++---------------------+
+| Data: Count | Rising Edge ________
+| Function: Increase | <------------- / Source \
+| | ____________
++---------------------+
+
+In this example, the Signal is a source input line with a pulsing
+voltage, while the Count is a persistent count value which is repeatedly
+incremented. The Signal is associated with the respective Count via a
+Synapse. The increase function is triggered by the Signal data condition
+specified by the Synapse -- in this case a rising edge condition on the
+voltage input line. In summary, the counter device existence and
+behavior is aptly represented by respective Count, Signal, and Synapse
+components: a rising edge condition triggers an increase function on an
+accumulating count datum.
+
+A counter device is not limited to a single Signal; in fact, in theory
+many number of Signals may be associated with even a single Count. For
+example, a quadrature encoder counter device can keep track of position
+based on the states of two input lines.
+
+ Count Synapse 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 Count: 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 Count, Signals, and Synapses simply represent this
+hardware behavior and functionality.
+
+Signals associated to the same Count can have differing Synapse action
+mode conditions. For example, a quadrature encoder counter device
+operating in a non-quadrature Pulse-Direction mode could have one input
+line dedicated for movement and a second input line dedicated for
+direction.
+
+ Count Synapse Signal
+ ----- ------- ------
++---------------------------+
+| Data: Position | Rising Edge ___
+| Function: Pulse-Direction | <------------- / A \ (Movement)
+| | _______
+| |
+| | None ___
+| | <------------- / B \ (Direction)
+| | _______
++---------------------------+
+
+Only Signal A triggers the "Pulse-Direction" update function, but the
+instantaneous state of Signal B is still required in order to know the
+direction so that the position data may be properly updated. Ultimately,
+both Signals are associated to the same Count via two respective
+Synapses, but only one Synapse has an active action mode condition which
+triggers the respective count function while the other is left with a
+"None" condition action mode to indicate its respective Signal's
+availability for state evaluation despite its non-triggering mode.
+
+The flexibility of Synapses allows for the representation of counters
+whose respective Count Signal relationships would be difficult to
+represent as isolated inputs. For example a tri-color LED color counter
+can easily be represented with three Synapses:
+
+ Count Synapse Signal
+ ----- ------- ------
++------------------+
+| Data: Count | Both Edges _______
+| Function: Purple | <------------ / Red \
+| | ___________
+| |
+| | Both Edges _______
+| | <------------ / Green \
+| | ___________
+| |
+| | Both Edges _______
+| | <------------ / Blue \
+| | ___________
++------------------+
+
+This counter accumulates the number of times the tri-color LED has
+turned to purple. By utilizing the Synapses to associate the three color
+Signals to the single Count, the count function evaluation relationship
+becomes apparent: combined color is checked when an individual color is
+turned on or off, and if only Red and Blue are active then Count datum
+is incremented to indicate the color has now turned to Purple.
+
+Although the examples thus far have been representations of physical
+devices, this is not a necessity. A counter simply represent the
+evaluation (Count) of input data (Signals) triggered by specific
+conditions (Synapses). 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.
+
+ Count Synapse 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 Synapse action modes consist of the various
+expressed sequence tags (ESTs), and the Count is a list of diseases
+discovered (in this particular example the function is evaluating for
+possible cancers). Note how the Signal in this example does not
+represent a physical voltage line, nor does the Synapse action mode
+represent a physical voltage line state change, nor does the Count
+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, Synapse, and Count 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 (e.g. an accumulated
+count of rising edges).
+
+Userspace Interface
+===================
+
+Several sysfs attributes are generated by the Generic Counter interface,
+and reside under the /sys/bus/counter/devices/counterX directory, where
+counterX refers to the respective counter device. Please see
+Documentation/ABI/testing/sys-bus-counter-generic-sysfs for detailed
+information on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Generic Counter paradigm Counts, Signals, and Synapses of respective
+counter devices.
+
+Driver API
+==========
+
+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 counter_signal_ext
+-------------------------
+
+This structure defines a Generic Counter Signal extension attribute.
+These attributes expose auxiliary configuration and functionality
+specific to the respective Generic Counter Signal.
+
+ name: Attribute name
+ read: Read callback for this attribute; may be NULL
+ write: Write callback for this attribute; may be NULL
+ priv: Data private to the driver
+
+struct counter_signal
+---------------------
+
+This structure defines a Generic Counter Signal component. Typically,
+this will correlate with an input channel on a physical counter device.
+This structure is the simplest of the Generic Counter paradigm core
+components to define with only two structure members which require
+explicit configuration:
+
+ id: unique ID used to identify signal
+ name: device-specific signal name
+ ext: optional array of Counter Signal extensions
+ num_ext: number of Counter Signal extensions specified in
+ ext
+ priv: optional private data supplied by driver
+
+struct counter_synapse
+----------------------
+
+This structure defines a Generic Counter Synapse component. To properly
+utilize this structure, action modes and an associated Signal must be
+defined:
+
+ action: current action mode
+ actions: available action modes
+ num_actions: number of action modes specified in actions
+ signal: pointer to associated signal
+
+struct counter_count_ext
+------------------------
+
+This structure defines a Generic Counter Count extension attribute.
+These attributes expose auxiliary configuration and functionality
+specific to the respective Generic Counter Count.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct counter_count
+--------------------
+
+This structure defines a Generic Counter Count 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 Synapse action condition), and a set of Synapses for the associated
+Signals:
+
+ id: unique ID used to identify Count
+ name: device-specific Count name
+ function: current function mode
+ functions: available function modes
+ num_functions: number of functions specified in @functions
+ synapses: array of synapses for initialization
+ num_synapses: number of synapses specified in @synapses
+ ext: optional array of Counter Count extensions
+ num_ext: number of Counter Count extensions specified in
+ ext
+ priv: optional private data supplied by driver
+
+struct counter_device_ext
+-------------------------
+
+This structure defines a Generic Counter extension attribute. These
+attributes expose auxiliary configuration and functionality specific to
+the respective Generic Counter.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct counter_device
+---------------------
+
+This is the main data structure for a Generic Counter device. Access to
+all respective Counts, Signals, and Synapses is possible from this
+structure. This structure requires the configuration of a name, Generic
+Counter Signals, and Generic Counter Counts:
+
+name: name of the device
+parent: optional parent device providing the counters
+signal_read: read callback for Signal attribute; may be NULL
+signal_write: write callback for Signal attribute; may be NULL
+count_read: read callback for Count attribute; may be NULL
+count_write: write callback for Count attribute; may be NULL
+function_get: function to get the current count function mode. Returns
+ 0 on success and negative error code on error. The index
+ of the respective Count's returned function mode should
+ be passed back via the function parameter.
+function_set: function to set the count function mode. function is the
+ index of the requested function mode from the respective
+ Count's functions array.
+action_get: function to get the current action mode. Returns 0 on
+ success and negative error code on error. The index of
+ the respective Signal's returned action mode should be
+ passed back via the action parameter.
+action_set: function to set the action mode. action is the index of
+ the requested action mode from the respective Synapse's
+ actions array.
+signals: array of Signals
+num_signals: number of Signals specified in @signals
+counts: array of Counts
+num_counts: number of Counts specified in @counts
+ext: optional array of Counter device extensions
+num_ext: number of Counter device extensions specified in ext
+priv: optional private data supplied by driver
+
+Registration functions
+----------------------
+
+A counter device is registered to the system by passing the respective
+initialized counter_device structure to the counter_register function;
+similarly, the counter_unregister function unregisters the respective
+Counter. The devm_counter_register and devm_counter_unregister functions
+serve as device memory-managed versions of the counter_register and
+counter_unregister functions respectively.
+
+Implementation
+==============
+
+To support a counter device, a driver must first allocate the available
+Counter Signals via counter_signal structures. These Signals should
+be stored as an array and set to the signals array member of an
+allocated counter_device structure before the Counter is registered to
+the system.
+
+Counter Counts may be allocated via counter_count structures, and
+respective Counter Signal associations (Synapses) made via
+counter_synapse structures. Associated counter_synapse structures are
+stored as an array and set to the the synapses array member of the
+respective counter_count structure. These counter_count structures are
+set to the counts array member of an allocated counter_device structure
+before the Counter is registered to the system.
+
+Driver callbacks should be provided to the counter_device structure in
+order to communicate with the device: to read and write various Signals
+and Counts, and to set and get the "action mode" and "function mode" for
+various Synapses and Counts respectively.
+
+A defined counter_device structure may be registered to the system by
+passing it to the counter_register function, and unregistered by passing
+it to the counter_unregister function. Similarly, the
+devm_counter_register and devm_counter_unregister functions may be used
+if device memory-managed registration is desired.
+
+Extension sysfs attributes can be created for auxiliary functionality
+and data by passing in defined counter_device_ext, counter_count_ext,
+and counter_signal_ext structures. In these cases, the
+counter_device_ext structure is used for global configuration of the
+respective Counter device, while the counter_count_ext and
+counter_signal_ext structures allow for auxiliary exposure and
+configuration of a specific Count or Signal respectively.
+
+Architecture
+============
+
+The counter_init function is called when the generic-counter module is
+loaded on the system. At this point, a bus_type named "counter" is
+registered and a dedicated "counter" chrdev region is allocated; these
+are removed and cleaned-up in the counter_exit function called when the
+generic-counter module is unloaded from the system.
+
+Counter devices are registered to the system via the counter_register
+function, and later removed via the counter_unregister function. The
+counter_register function establishes a unique ID for the Counter device
+and creates an associated device node under /dev called counterX, where
+X is the mentioned unique ID. A respective sysfs directory is created
+for the Counter device with a similar naming scheme:
+
+ /sys/bus/counter/devices/counterX
+
+Sysfs attributes are created within the counterX directory to expose
+functionality, configurations, and data relating to the Counts, Signals,
+and Synapses of the Counter device, as well as options and information
+for the Counter device itself.
+
+For a more detailed breakdown of the available Generic Counter interface
+sysfs attributes, please refer to the
+Documentation/ABI/testing/sys-bus-counter-generic-sysfs file.
+
+The Signals and Counts associated with the Counter device are registered
+to the system as well by the counter_register function. The
+signal_read/signal_write driver callbacks are associated to their
+respective Signal attributes, while the count_read/count_write and
+function_get/function_set driver callbacks are associated to their
+respective Count attributes; similarly, the same is true for the
+action_get/action_set driver callbacks and their respective Synapse
+attributes. If a driver callback is left undefined, then the respective
+read/write permission is left disabled for the relevant attributes.
+
+Similarly, extension sysfs attributes are created for the defined
+counter_device_ext, counter_count_ext, and counter_signal_ext
+structures that are passed in.
diff --git a/Documentation/driver-api/iio/index.rst b/Documentation/driver-api/iio/index.rst
index e5c3922d1b6f..e6c5b75c2e03 100644
--- a/Documentation/driver-api/iio/index.rst
+++ b/Documentation/driver-api/iio/index.rst
@@ -15,3 +15,4 @@ Contents:
buffers
triggers
triggered-buffers
+ generic-counter
diff --git a/MAINTAINERS b/MAINTAINERS
index 38da1bc615b3..08eba78057e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3665,6 +3665,7 @@ COUNTER INTERFACE
M: William Breathitt Gray <[email protected]>
L: [email protected]
S: Maintained
+F: Documentation/driver-api/counter/
F: Documentation/ABI/testing/sysfs-bus-counter-*
F: drivers/iio/counter/
F: include/linux/iio/counter.h
--
2.15.1
This patch introduces the Simple Counter interface. The Simple Counter
interface serves as an API to provide support for simple hardware
counter devices. The Simple Counter interface is built on top of the
Generic Counter interface.
A simple hardware counter device is a counter device that has a single
signal associated with each count value; Signals may have a value of
"low" or "high," and edge trigger the count function which either
increases or decreases the respective count value.
The Simple Counter interface provides two count function modes:
SIMPLE_COUNTER_FUNCTION_INCREASE: "increase"
SIMPLE_COUNTER_FUNCTION_DECREASE: "decrease"
The Simple Counter interface provides four action modes:
SIMPLE_COUNTER_ACTION_NONE: "none"
SIMPLE_COUNTER_ACTION_RISING_EDGE: "rising edge"
SIMPLE_COUNTER_ACTION_FALLING_EDGE: "falling edge"
SIMPLE_COUNTER_ACTION_BOTH_EDGES: "both edges"
Signals may be represented by two possible states:
SIMPLE_COUNTER_SIGNAL_LOW: "low"
SIMPLE_COUNTER_SIGNAL_HIGH: "high"
Since the Simple Counter interface utilizes the Generic Counter
interface underneath, all the expected functionality of the Generic
Counter interface such as sysfs attributes is exposed to userspace for
end user consumption. The Simple Counter interface serves as a
convenience API for supporting a common class of counter devices without
the need to manually configure the more cumbersome Generic Counter
interface for use.
To use the Simple Counter interface, create an array of
simple_counter_count structures to represent the desired counts and
signals of the counter device, allocate a simple_counter_device
structure and populate it with respective driver callbacks and the
simple_counter_count array created earlier, then register the counter by
calling the simple_counter_register function. The
simple_counter_unregister function may be used to unregistered a
previously registered counter.
Memory-managed versions of simple_counter_register and
simple_counter_unregister functions are provided by the
devm_simple_counter_register and devm_simple_counter_unregister
functions respectively.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/counter/Kconfig | 4 +-
drivers/iio/counter/Makefile | 1 +
drivers/iio/counter/simple-counter.c | 734 +++++++++++++++++++++++++++++++++++
include/linux/iio/counter.h | 199 ++++++++++
4 files changed, 937 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/counter/simple-counter.c
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 4eaf4e53c5aa..6b9a43180d2c 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -8,7 +8,9 @@ menuconfig COUNTER
help
Provides support for Counter devices. The Generic Counter API provides
rudimentary support for counters and serves as building blocks to
- create more complex counter interfaces.
+ create more complex counter interfaces. The Simple Counter API
+ provides support for simple hardware counter devices that have a
+ one-to-one mapping between their Signals and Counts.
if COUNTER
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 513c49d832d4..7450dee97446 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_COUNTER) += counter.o
counter-$(CONFIG_COUNTER) += generic-counter.o
+counter-$(CONFIG_COUNTER) += simple-counter.o
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/simple-counter.c b/drivers/iio/counter/simple-counter.c
new file mode 100644
index 000000000000..e061db0860fd
--- /dev/null
+++ b/drivers/iio/counter/simple-counter.c
@@ -0,0 +1,734 @@
+/*
+ * Simple 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.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <linux/iio/counter.h>
+
+static const char *const simple_counter_signal_level_names[] = {
+ [SIMPLE_COUNTER_SIGNAL_LOW] = "low",
+ [SIMPLE_COUNTER_SIGNAL_HIGH] = "high"
+};
+
+static ssize_t simple_counter_signal_read(struct counter_device *counter_dev,
+ struct counter_signal *counter_sig, char *buf)
+{
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_signal *const signal = counter_sig->priv;
+ int err;
+ enum simple_counter_signal_level level;
+
+ err = counter->signal_read(counter, signal, &level);
+ if (err)
+ return err;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ simple_counter_signal_level_names[level]);
+}
+
+static ssize_t simple_counter_signal_write(struct counter_device *counter_dev,
+ struct counter_signal *counter_sig, const char *buf, size_t len)
+{
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_signal *const signal = counter_sig->priv;
+ int level;
+ int err;
+
+ level = sysfs_match_string(simple_counter_signal_level_names, buf);
+ if (level < 0)
+ return level;
+
+ err = counter->signal_write(counter, signal, level);
+ if (err)
+ return err;
+
+ return len;
+}
+
+static ssize_t simple_counter_count_read(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, char *buf)
+{
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_count *const count = counter_cnt->priv;
+ int err;
+ long val;
+
+ err = counter->count_read(counter, count, &val);
+ if (err)
+ return err;
+
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
+}
+
+static ssize_t simple_counter_count_write(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, const char *buf, size_t len)
+{
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_count *const count = counter_cnt->priv;
+ int err;
+ long val;
+
+ err = kstrtol(buf, 0, &val);
+ if (err)
+ return err;
+
+ err = counter->count_write(counter, count, val);
+ if (err)
+ return err;
+
+ return len;
+}
+
+static int simple_counter_function_get(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, size_t *counter_func)
+{
+ int err;
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_count *const count = counter_cnt->priv;
+ enum simple_counter_function function;
+
+ err = counter->function_get(counter, count, &function);
+ if (err)
+ return err;
+
+ count->function = function;
+
+ *counter_func = function;
+
+ return 0;
+}
+
+static int simple_counter_function_set(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, size_t function)
+{
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_count *const count = counter_cnt->priv;
+ int err;
+
+ err = counter->function_set(counter, count, function);
+ if (err)
+ return err;
+
+ count->function = function;
+
+ return 0;
+}
+
+static int simple_counter_action_get(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
+ size_t *counter_act)
+{
+ int err;
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_count *const count = counter_cnt->priv;
+ enum simple_counter_action action;
+
+ err = counter->action_get(counter, count, &action);
+ if (err)
+ return err;
+
+ count->action = action;
+
+ *counter_act = action;
+
+ return 0;
+}
+
+static int simple_counter_action_set(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
+ size_t action)
+{
+ struct simple_counter_device *const counter = counter_dev->priv;
+ struct simple_counter_count *const count = counter_cnt->priv;
+ int err;
+
+ err = counter->action_set(counter, count, action);
+ if (err)
+ return err;
+
+ count->action = action;
+
+ return 0;
+}
+
+static ssize_t simple_counter_signal_ext_read(struct counter_device *dev,
+ struct counter_signal *signal, void *priv, char *buf)
+{
+ const struct simple_counter_signal_ext *const ext = priv;
+ struct simple_counter_device *const counter = dev->priv;
+ struct simple_counter_signal *const simple_signal = signal->priv;
+
+ return ext->read(counter, simple_signal, ext->priv, buf);
+}
+
+static ssize_t simple_counter_signal_ext_write(struct counter_device *dev,
+ struct counter_signal *signal, void *priv, const char *buf, size_t len)
+{
+ const struct simple_counter_signal_ext *const ext = priv;
+ struct simple_counter_device *const counter = dev->priv;
+ struct simple_counter_signal *const simple_signal = signal->priv;
+
+ return ext->write(counter, simple_signal, ext->priv, buf, len);
+}
+
+static int simple_counter_counter_signal_ext_register(
+ const struct simple_counter_signal *const simple_signal,
+ struct counter_signal *const signal)
+{
+ const struct simple_counter_signal_ext *const simple_ext =
+ simple_signal->ext;
+ const size_t num_ext = simple_signal->num_ext;
+ struct counter_signal_ext *ext;
+ size_t i;
+
+ /* Return early if no extensions */
+ if (!simple_ext || !num_ext)
+ return 0;
+
+ /* Allocate space for counter_signal_ext array */
+ ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
+ if (!ext)
+ return -ENOMEM;
+
+ /* Register simple_counter_signal_ext via counter_signal_ext */
+ for (i = 0; i < num_ext; i++) {
+ ext[i].name = simple_ext[i].name;
+ ext[i].read = (simple_ext[i].read) ?
+ simple_counter_signal_ext_read : NULL;
+ ext[i].write = (simple_ext[i].write) ?
+ simple_counter_signal_ext_write : NULL;
+ ext[i].priv = simple_ext + i;
+ }
+
+ /* Register Counter Signal extensions */
+ signal->ext = ext;
+ signal->num_ext = num_ext;
+
+ return 0;
+}
+
+static int simple_counter_counter_signals_register(
+ const struct simple_counter_device *const counter)
+{
+ const size_t num_counts = counter->num_counts;
+ struct counter_signal *signals;
+ const size_t num_signals = num_counts;
+ struct simple_counter_count *const simple_counts = counter->counts;
+ size_t i;
+ struct counter_signal *signal;
+ struct simple_counter_signal *simple_signal;
+ int err;
+ struct counter_device *const counter_dev = counter->counter_dev;
+
+ /* Allocate space for signals array */
+ signals = kcalloc(num_signals, sizeof(*signals), GFP_KERNEL);
+ if (!signals)
+ return -ENOMEM;
+
+ /* Configure Signals */
+ for (i = 0; i < num_signals; i++) {
+ signal = signals + i;
+ simple_signal = &simple_counts[i].signal;
+
+ signal->id = simple_signal->id;
+ signal->name = simple_signal->name;
+ signal->priv = simple_signal;
+
+ /* Register Counter Signal extensions */
+ err = simple_counter_counter_signal_ext_register(simple_signal,
+ signal);
+ if (err)
+ goto err_free_signals;
+ }
+
+ /* Register Signals to Counter device container */
+ counter_dev->signals = signals;
+ counter_dev->num_signals = num_signals;
+
+ return 0;
+
+err_free_signals:
+ while (i--)
+ kfree(signals[i].ext);
+ kfree(signals);
+ return err;
+}
+
+static const char *const simple_counter_function_names[] = {
+ [SIMPLE_COUNTER_FUNCTION_INCREASE] = "increase",
+ [SIMPLE_COUNTER_FUNCTION_DECREASE] = "decrease"
+};
+
+static const char *const simple_counter_action_names[] = {
+ [SIMPLE_COUNTER_ACTION_NONE] = "none",
+ [SIMPLE_COUNTER_ACTION_RISING_EDGE] = "rising edge",
+ [SIMPLE_COUNTER_ACTION_FALLING_EDGE] = "falling edge",
+ [SIMPLE_COUNTER_ACTION_BOTH_EDGES] = "both edges"
+};
+
+static int simple_counter_counter_synapse_register(
+ struct counter_signal *const signal, struct counter_count *const count)
+{
+ struct counter_synapse *synapse;
+
+ /* Allocate space for Counter Synapse */
+ synapse = kzalloc(sizeof(*synapse), GFP_KERNEL);
+ if (!synapse)
+ return -ENOMEM;
+
+ /* Configure Synapse */
+ synapse->signal = signal;
+ synapse->actions = simple_counter_action_names;
+ synapse->num_actions = ARRAY_SIZE(simple_counter_action_names);
+
+ /* Register Counter Synapse */
+ count->synapses = synapse;
+ count->num_synapses = 1;
+
+ return 0;
+}
+
+static ssize_t simple_counter_count_ext_read(struct counter_device *dev,
+ struct counter_count *count, void *priv, char *buf)
+{
+ const struct simple_counter_count_ext *const ext = priv;
+ struct simple_counter_device *const counter = dev->priv;
+ struct simple_counter_count *const simple_count = count->priv;
+
+ return ext->read(counter, simple_count, ext->priv, buf);
+}
+
+static ssize_t simple_counter_count_ext_write(struct counter_device *dev,
+ struct counter_count *count, void *priv, const char *buf, size_t len)
+{
+ const struct simple_counter_count_ext *const ext = priv;
+ struct simple_counter_device *const counter = dev->priv;
+ struct simple_counter_count *const simple_count = count->priv;
+
+ return ext->write(counter, simple_count, ext->priv, buf, len);
+}
+
+static int simple_counter_counter_count_ext_register(
+ const struct simple_counter_count *const simple_count,
+ struct counter_count *const count)
+{
+ const struct simple_counter_count_ext *const simple_ext =
+ simple_count->ext;
+ const size_t num_ext = simple_count->num_ext;
+ struct counter_count_ext *ext;
+ size_t i;
+
+ /* Return early if no extensions */
+ if (!simple_ext || !num_ext)
+ return 0;
+
+ /* Allocate space for Counter Count extensions array */
+ ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
+ if (!ext)
+ return -ENOMEM;
+
+ /* Register simple_counter_count_ext via counter_count_ext */
+ for (i = 0; i < num_ext; i++) {
+ ext[i].name = simple_ext[i].name;
+ ext[i].read = (simple_ext[i].read) ?
+ simple_counter_count_ext_read : NULL;
+ ext[i].write = (simple_ext[i].write) ?
+ simple_counter_count_ext_write : NULL;
+ ext[i].priv = simple_ext + i;
+ }
+
+ /* Register Counter Count extensions */
+ count->ext = ext;
+ count->num_ext = num_ext;
+
+ return 0;
+}
+
+static void simple_counter_counter_synapse_unregister(
+ const struct counter_count *const count)
+{
+ kfree(count->synapses);
+}
+
+static int simple_counter_counter_count_init(
+ struct counter_count *const count,
+ struct simple_counter_count *const simple_count,
+ struct counter_signal *const signal)
+{
+ int err;
+
+ count->id = simple_count->id;
+ count->name = simple_count->name;
+ count->functions = simple_counter_function_names;
+ count->num_functions = ARRAY_SIZE(simple_counter_function_names);
+ count->priv = simple_count;
+
+ /* Register Counter Synapse */
+ err = simple_counter_counter_synapse_register(signal, count);
+ if (err)
+ return err;
+
+ /* Register Counter Count extensions */
+ err = simple_counter_counter_count_ext_register(simple_count, count);
+ if (err)
+ goto err_unregister_synapse;
+
+ return 0;
+
+err_unregister_synapse:
+ simple_counter_counter_synapse_unregister(count);
+ return err;
+}
+
+static void simple_counter_counter_count_ext_unregister(
+ const struct counter_count *const count)
+{
+ kfree(count->ext);
+}
+
+static void simple_counter_counter_count_free(
+ const struct counter_count *const count)
+{
+ simple_counter_counter_count_ext_unregister(count);
+ simple_counter_counter_synapse_unregister(count);
+}
+
+static int simple_counter_counter_counts_register(
+ const struct simple_counter_device *const counter)
+{
+ struct counter_device *const counter_dev = counter->counter_dev;
+ struct counter_count *counts;
+ const size_t num_counts = counter->num_counts;
+ size_t i;
+ struct simple_counter_count *const simple_counts = counter->counts;
+ struct counter_signal *const signals = counter_dev->signals;
+ int err;
+
+ /* Allocate space for counts array */
+ counts = kcalloc(num_counts, sizeof(*counts), GFP_KERNEL);
+ if (!counts)
+ return -ENOMEM;
+
+ /* Initialize Counts */
+ for (i = 0; i < num_counts; i++) {
+ err = simple_counter_counter_count_init(counts + i,
+ simple_counts + i, signals + i);
+ if (err)
+ goto err_free_counts;
+ }
+
+ /* Register Counts to Counter device container */
+ counter_dev->counts = counts;
+ counter_dev->num_counts = num_counts;
+
+ return 0;
+
+err_free_counts:
+ while (i--)
+ simple_counter_counter_count_free(counts + i);
+ kfree(counts);
+ return err;
+}
+
+static void simple_counter_counter_signals_unregister(
+ const struct counter_device *const counter_dev)
+{
+ const struct counter_signal *const signals = counter_dev->signals;
+ size_t num_signals = counter_dev->num_signals;
+
+ while (num_signals--)
+ kfree(signals[num_signals].ext);
+ kfree(signals);
+}
+
+static int simple_counter_counts_register(
+ struct simple_counter_device *const counter)
+{
+ const struct simple_counter_count *const simple_counts =
+ counter->counts;
+ const size_t num_counts = counter->num_counts;
+ int err;
+
+ /* At least one Count must be defined */
+ if (!simple_counts || !num_counts) {
+ pr_err("simple-counter: Simple Counter Counts undefined\n");
+ return -EINVAL;
+ }
+
+ /* Register Counter Signals */
+ err = simple_counter_counter_signals_register(counter);
+ if (err)
+ return err;
+
+ /* Register Counter Counts */
+ err = simple_counter_counter_counts_register(counter);
+ if (err)
+ goto err_unregister_signals;
+
+ return 0;
+
+err_unregister_signals:
+ simple_counter_counter_signals_unregister(counter->counter_dev);
+ return err;
+}
+
+static ssize_t simple_counter_device_ext_read(struct counter_device *dev,
+ void *priv, char *buf)
+{
+ const struct simple_counter_device_ext *const ext = priv;
+ struct simple_counter_device *const counter = dev->priv;
+
+ return ext->read(counter, ext->priv, buf);
+}
+
+static ssize_t simple_counter_device_ext_write(struct counter_device *dev,
+ void *priv, const char *buf, size_t len)
+{
+ const struct simple_counter_device_ext *const ext = priv;
+ struct simple_counter_device *const counter = dev->priv;
+
+ return ext->write(counter, ext->priv, buf, len);
+}
+
+static int simple_counter_device_ext_register(
+ struct simple_counter_device *const counter)
+{
+ const struct simple_counter_device_ext *const simple_ext = counter->ext;
+ const size_t num_ext = counter->num_ext;
+ struct counter_device_ext *ext;
+ size_t i;
+ struct counter_device *const counter_dev = counter->counter_dev;
+
+ /* Return early if no extensions */
+ if (!simple_ext || !num_ext)
+ return 0;
+
+ /* Allocate space for Counter device extensions array */
+ ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
+ if (!ext)
+ return -ENOMEM;
+
+ /* Register simple_counter_device_ext via counter_device_ext */
+ for (i = 0; i < num_ext; i++) {
+ ext[i].name = simple_ext[i].name;
+ ext[i].read = (simple_ext[i].read) ?
+ simple_counter_device_ext_read : NULL;
+ ext[i].write = (simple_ext[i].write) ?
+ simple_counter_device_ext_write : NULL;
+ ext[i].priv = simple_ext + i;
+ }
+
+ /* Register Counter device extensions */
+ counter_dev->ext = ext;
+ counter_dev->num_ext = num_ext;
+
+ return 0;
+}
+
+static void simple_counter_counter_counts_unregister(
+ const struct counter_device *const counter)
+{
+ const struct counter_count *const counts = counter->counts;
+ size_t num_counts = counter->num_counts;
+
+ while (num_counts--)
+ simple_counter_counter_count_free(counts + num_counts);
+ kfree(counts);
+}
+
+static void simple_counter_counts_unregister(
+ const struct simple_counter_device *const counter)
+{
+ const struct counter_device *const counter_dev = counter->counter_dev;
+
+ simple_counter_counter_counts_unregister(counter_dev);
+ simple_counter_counter_signals_unregister(counter_dev);
+}
+
+/**
+ * simple_counter_register - register Simple Counter to the system
+ * @counter: pointer to Simple Counter to register
+ *
+ * This function registers a Simple Counter to the system. A sysfs "counter"
+ * directory will be created and populated with sysfs attributes correlating
+ * with the Simple Counter Signals, Synapses, and Counts respectively.
+ */
+int simple_counter_register(struct simple_counter_device *const counter)
+{
+ struct counter_device *counter_dev;
+ int err;
+
+ if (!counter)
+ return -EINVAL;
+
+ /* Allocate internal Counter container */
+ counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL);
+ if (!counter)
+ return -ENOMEM;
+ counter->counter_dev = counter_dev;
+
+ /* Configure internal Counter */
+ counter_dev->name = counter->name;
+ counter_dev->parent = counter->parent;
+ counter_dev->signal_read = (counter->signal_read) ?
+ simple_counter_signal_read : NULL;
+ counter_dev->signal_write = (counter->signal_write) ?
+ simple_counter_signal_write : NULL;
+ counter_dev->count_read = (counter->count_read) ?
+ simple_counter_count_read : NULL;
+ counter_dev->count_write = (counter->count_write) ?
+ simple_counter_count_write : NULL;
+ counter_dev->function_get = (counter->function_get) ?
+ simple_counter_function_get : NULL;
+ counter_dev->function_set = (counter->function_set) ?
+ simple_counter_function_set : NULL;
+ counter_dev->action_get = (counter->action_get) ?
+ simple_counter_action_get : NULL;
+ counter_dev->action_set = (counter->action_set) ?
+ simple_counter_action_set : NULL;
+ counter_dev->priv = counter;
+
+ /* Register Simple Counter Counts */
+ err = simple_counter_counts_register(counter);
+ if (err)
+ goto err_free_counter_dev;
+
+ /* Register Simple Counter device extension attributes */
+ err = simple_counter_device_ext_register(counter);
+ if (err)
+ goto err_unregister_counts;
+
+ /* Register internal Counter to the system */
+ err = counter_register(counter_dev);
+ if (err)
+ goto err_free_ext;
+
+ return 0;
+
+err_free_ext:
+ kfree(counter_dev->ext);
+err_unregister_counts:
+ simple_counter_counts_unregister(counter);
+err_free_counter_dev:
+ kfree(counter_dev);
+ return err;
+}
+EXPORT_SYMBOL(simple_counter_register);
+
+/**
+ * simple_counter_unregister - unregister Simple Counter from the system
+ * @counter: pointer to Simple Counter to unregister
+ *
+ * The Simple Counter is unregistered from the system; all allocated memory is
+ * freed.
+ */
+void simple_counter_unregister(struct simple_counter_device *const counter)
+{
+ struct counter_device *counter_dev;
+
+ if (!counter)
+ return;
+
+ counter_dev = counter->counter_dev;
+
+ counter_unregister(counter_dev);
+
+ kfree(counter_dev->ext);
+ simple_counter_counts_unregister(counter);
+ kfree(counter_dev);
+}
+EXPORT_SYMBOL(simple_counter_unregister);
+
+static void devm_simple_counter_unreg(struct device *dev, void *res)
+{
+ simple_counter_unregister(*(struct simple_counter_device **)res);
+}
+
+/**
+ * devm_simple_counter_register - Resource-managed simple_counter_register
+ * @dev: device to allocate simple_counter_device for
+ * @counter: pointer to Simple Counter to register
+ *
+ * Managed simple_counter_register. The Simple Counter registered with this
+ * function is automatically unregistered on driver detach. This function calls
+ * simple_counter_register internally. Refer to that function for more
+ * information.
+ *
+ * If an Simple Counter registered with this function needs to be unregistered
+ * separately, devm_simple_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_simple_counter_register(struct device *dev,
+ struct simple_counter_device *const counter)
+{
+ struct simple_counter_device **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_simple_counter_unreg, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = simple_counter_register(counter);
+ if (!ret) {
+ *ptr = counter;
+ devres_add(dev, ptr);
+ } else
+ devres_free(ptr);
+
+ return ret;
+}
+EXPORT_SYMBOL(devm_simple_counter_register);
+
+static int devm_simple_counter_match(struct device *dev, void *res, void *data)
+{
+ struct simple_counter_device **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_simple_counter_unregister - Resource-managed simple_counter_unregister
+ * @dev: device this simple_counter_device belongs to
+ * @counter: the Simple Counter associated with the device
+ *
+ * Unregister Simple Counter registered with devm_simple_counter_register.
+ */
+void devm_simple_counter_unregister(struct device *dev,
+ struct simple_counter_device *const counter)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_simple_counter_unreg,
+ devm_simple_counter_match, counter);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_simple_counter_unregister);
+
+MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
+MODULE_DESCRIPTION("Simple Counter interface");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
index 070ed8fd53fb..0967ea2a9bef 100644
--- a/include/linux/iio/counter.h
+++ b/include/linux/iio/counter.h
@@ -236,4 +236,203 @@ extern int devm_counter_register(struct device *dev,
extern void devm_counter_unregister(struct device *dev,
struct counter_device *const counter);
+struct simple_counter_device;
+struct simple_counter_signal;
+
+/**
+ * struct simple_counter_signal_ext - Simple Counter Signal extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct simple_counter_signal_ext {
+ const char *name;
+ ssize_t (*read)(struct simple_counter_device *counter,
+ struct simple_counter_signal *signal,
+ void *priv, char *buf);
+ ssize_t (*write)(struct simple_counter_device *counter,
+ struct simple_counter_signal *signal,
+ void *priv, const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct simple_counter_signal - Simple Counter Signal node
+ * @id: [DRIVER] unique ID used to identify signal
+ * @name: [DRIVER] device-specific signal name
+ * @ext: [DRIVER] optional array of Simple Counter Signal extensions
+ * @num_ext: [DRIVER] number of Simple Counter Signal extensions specified in
+ * @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct simple_counter_signal {
+ int id;
+ const char *name;
+
+ const struct simple_counter_signal_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+enum simple_counter_signal_level {
+ SIMPLE_COUNTER_SIGNAL_LOW = 0,
+ SIMPLE_COUNTER_SIGNAL_HIGH
+};
+
+struct simple_counter_count;
+
+enum simple_counter_function {
+ SIMPLE_COUNTER_FUNCTION_INCREASE = 0,
+ SIMPLE_COUNTER_FUNCTION_DECREASE
+};
+
+enum simple_counter_action {
+ SIMPLE_COUNTER_ACTION_NONE = 0,
+ SIMPLE_COUNTER_ACTION_RISING_EDGE,
+ SIMPLE_COUNTER_ACTION_FALLING_EDGE,
+ SIMPLE_COUNTER_ACTION_BOTH_EDGES
+};
+
+/**
+ * struct simple_counter_count_ext - Simple Counter Count extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct simple_counter_count_ext {
+ const char *name;
+ ssize_t (*read)(struct simple_counter_device *counter,
+ struct simple_counter_count *count, void *priv,
+ char *buf);
+ ssize_t (*write)(struct simple_counter_device *counter,
+ struct simple_counter_count *count, void *priv,
+ const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct simple_counter_count - Simple Counter Count node
+ * @id: [DRIVER] unique ID used to identify Count
+ * @name: [DRIVER] device-specific Count name
+ * @function: [DRIVER] current function mode
+ * @action: [DRIVER] current action mode
+ * @signal: [DRIVER] associated signal
+ * @ext: [DRIVER] optional array of Simple Counter Count extensions
+ * @num_ext: [DRIVER] number of Simple Counter Count extensions specified in
+ * @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct simple_counter_count {
+ int id;
+ const char *name;
+ enum simple_counter_function function;
+ enum simple_counter_action action;
+
+ struct simple_counter_signal signal;
+
+ const struct simple_counter_count_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+/**
+ * struct simple_counter_device_ext - Simple Counter device extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct simple_counter_device_ext {
+ const char *name;
+ ssize_t (*read)(struct simple_counter_device *counter,
+ void *priv, char *buf);
+ ssize_t (*write)(struct simple_counter_device *counter,
+ void *priv, const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct simple_counter_device - Simple Counter data structure
+ * @name: [DRIVER] name of the device
+ * @parent: [DRIVER] optional parent device providing the counters
+ * @counter_dev: [INTERN] internal Counter container
+ * @signal_read: [DRIVER] read callback for Signal attribute; may be
+ * NULL. Returns 0 on success and negative error code on
+ * error. The respective Signal's returned level should be
+ * passed back via the level parameter.
+ * @signal_write: [DRIVER] write callback for Signal attribute; may be
+ * NULL
+ * @count_read: [DRIVER] read callback for Count attribute; may be NULL.
+ * Returns 0 on success and negative error code on error.
+ * The respective Count's returned value should be passed
+ * back via the val parameter.
+ * @count_write: [DRIVER] write callback for Count attribute; may be NULL
+ * @function_get: [DRIVER] function to get the current count function
+ * mode. Returns 0 on success and negative error code on
+ * error. The respective Count's returned function mode
+ * should be passed back via the function parameter.
+ * @function_set: [DRIVER] function to set the count function mode
+ * @action_get: [DRIVER] function to get the current action mode.
+ * Returns 0 on success and negative error code on error.
+ * The respective Signal's returned action mode should be
+ * passed back via the action parameter.
+ * @action_set: [DRIVER] function to set the action mode
+ * @counts: [DRIVER] array of Simple Counter Counts
+ * @num_counts: [DRIVER] number of Simple Counter Counts specified in
+ * @counts
+ * @ext: [DRIVER] optional array of Simple Counter device
+ * extensions
+ * @num_ext: [DRIVER] number of Simple Counter device extensions
+ * specified in @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct simple_counter_device {
+ const char *name;
+ struct device *parent;
+ struct counter_device *counter_dev;
+
+ int (*signal_read)(struct simple_counter_device *counter,
+ struct simple_counter_signal *signal,
+ enum simple_counter_signal_level *level);
+ int (*signal_write)(struct simple_counter_device *counter,
+ struct simple_counter_signal *signal,
+ enum simple_counter_signal_level level);
+ int (*count_read)(struct simple_counter_device *counter,
+ struct simple_counter_count *count, long *val);
+ int (*count_write)(struct simple_counter_device *counter,
+ struct simple_counter_count *count, long val);
+ int (*function_get)(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_function *function);
+ int (*function_set)(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_function function);
+ int (*action_get)(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_action *action);
+ int (*action_set)(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_action action);
+
+ struct simple_counter_count *counts;
+ size_t num_counts;
+
+ const struct simple_counter_device_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+extern int simple_counter_register(struct simple_counter_device *const counter);
+extern void simple_counter_unregister(
+ struct simple_counter_device *const counter);
+extern int devm_simple_counter_register(struct device *dev,
+ struct simple_counter_device *const counter);
+extern void devm_simple_counter_unregister(struct device *dev,
+ struct simple_counter_device *const counter);
+
#endif /* _COUNTER_H_ */
--
2.15.1
This patch adds standard documentation for the userspace sysfs
attributes of the Simple Counter interface.
Signed-off-by: William Breathitt Gray <[email protected]>
---
.../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
diff --git a/Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs b/Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
new file mode 100644
index 000000000000..e1f32c64c667
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
@@ -0,0 +1,61 @@
+What: /sys/bus/counter/devices/counterX/countY
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count data of Count Y. This is a signed integer value that
+ represents the accumulated count.
+
+What: /sys/bus/counter/devices/counterX/countY_function
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count function mode of Count Y. Count function evaluation is
+ triggered by conditions specified by the countY_signalZ_action
+ attributes. Two count function modes are available: increase and
+ decrease.
+
+ Increase:
+ Accumulated count is incremented.
+
+ Decrease:
+ Accumulated count is decremented.
+
+What: /sys/bus/counter/devices/counterX/countY_max
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count Y count data maximum value.
+
+What: /sys/bus/counter/devices/counterX/countY_min
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count Y count data minimum value.
+
+What: /sys/bus/counter/devices/counterX/countY_signalZ_action
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Action mode of Count Y for Signal Z. This attribute indicates
+ the condition of Signal Z that triggers the count function
+ evaluation for Count Y. Four action modes are available: none,
+ rising edge, falling edge, and both edges.
+
+ None:
+ Signal does not trigger the count function.
+
+ Rising Edge:
+ Low state transitions to High state.
+
+ Falling Edge:
+ High state transitions to Low state.
+
+ Both Edges:
+ Any state transition.
+
+What: /sys/bus/counter/devices/counterX/signalY
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Signal data of Signal Y. This is the respective input level
+ represented by two available states: low and high.
--
2.15.1
This patch adds high-level documentation about the Simple Counter
interface.
Signed-off-by: William Breathitt Gray <[email protected]>
---
Documentation/driver-api/iio/index.rst | 1 +
Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++++++++++++++++++
2 files changed, 394 insertions(+)
create mode 100644 Documentation/driver-api/iio/simple-counter.rst
diff --git a/Documentation/driver-api/iio/index.rst b/Documentation/driver-api/iio/index.rst
index e6c5b75c2e03..60e17f16cfb8 100644
--- a/Documentation/driver-api/iio/index.rst
+++ b/Documentation/driver-api/iio/index.rst
@@ -16,3 +16,4 @@ Contents:
triggers
triggered-buffers
generic-counter
+ simple-counter
diff --git a/Documentation/driver-api/iio/simple-counter.rst b/Documentation/driver-api/iio/simple-counter.rst
new file mode 100644
index 000000000000..fa34f689e99f
--- /dev/null
+++ b/Documentation/driver-api/iio/simple-counter.rst
@@ -0,0 +1,393 @@
+========================
+Simple Counter Interface
+========================
+
+Introduction
+============
+
+The most basic counter device may be expressed as a single Count
+associated with a single Signal via a single Synapse. This is a popular
+type of Counter implemented by hardware devices, and also what commonly
+comes to mind when one thinks of a "counter device." This driver API
+provides a basic Counter interface and standard of interaction and
+exposure for these simple counter devices. The Simple Counter interface
+enables drivers to support and expose simple counter devices in a more
+apt and well-defined way, without the hassles and imprecisions of
+utilizing a more generic interface.
+
+Theory
+======
+
+The Simple Counter interface may be considered a subclass of the
+Generic Counter interface; the same paradigm core components apply:
+Counts, Signals, and Synapses. However, the Simple Counter interface
+goes a bit further and defines aspects of those core components to
+properly represent Simple Counter devices.
+
+The three core components of a Simple Counter:
+
+ COUNT
+ -----
+ A Count represents the count data for a set of Signals. The
+ count data for a Simple Counter is a signed integer representing
+ the accumulated count of Simple Signal action conditions.
+
+ A Count has a count function mode which represents the update
+ behavior for the count data. The following two count function
+ modes are possible for a Simple Count:
+
+ * Increase:
+ Accumulated count is incremented.
+ * Decrease:
+ Accumulated count is decremented.
+
+ A Simple Count has one associated Simple Signal.
+
+ SIGNAL
+ ------
+ A Signal represents a counter input data; this is the data that
+ is typically analyzed by the counter to determine the count
+ data. A Simple Signal represents a counter input line with two
+ possible states:
+
+ * Low
+ * High
+
+ A Simple Signal is associated to a Simple Count.
+
+ SYNAPSE
+ -------
+ A Synapse represents the association of a Signal with a
+ respective Count. Signal data affects respective Count data, and
+ the Synapse represents this relationship.
+
+ The Synapse action mode specifies the Signal data condition
+ which triggers the respective Count's count function evaluation
+ to update the count data. There are four possible action modes
+ for a Simple Counter:
+
+ * None:
+ Signal does not trigger the count function.
+ * Rising Edge:
+ Low state transitions to High state.
+ * Falling Edge:
+ High state transitions to Low state.
+ * Both Edges:
+ Any state transition.
+
+Paradigm
+========
+
+Simple Counter devices consist of a single Count associated with a
+single Signal via a single Synapse. Take for example a counter device
+which simply accumulates a count of rising edges on a source input line.
+
+ Count Synapse Signal
+ ----- ------- ------
++---------------------+
+| Data: Count | Rising Edge ________
+| Function: Increase | <------------- / Source \
+| | ____________
++---------------------+
+
+In this example, the Signal is a source input line with a pulsing
+voltage, while the Count is a persistent count value which is repeatedly
+incremented. The Signal is associated with the respective Count via a
+Synapse. The increase function is triggered by the Signal data condition
+specified by the Synapse -- in this case a rising edge condition on the
+voltage input line.
+
+In summary, the simple counter device existence and behavior is aptly
+represented by respective Count, Signal, and Synapse components: e.g. a
+rising edge condition triggers an increase function on an accumulating
+count datum.
+
+Userspace Interface
+===================
+
+Several sysfs attributes are generated by the Simple Counter interface,
+and reside under the /sys/bus/counter/devices/counterX directory, where
+counterX refers to the respective counter device. Please see
+Documentation/ABI/testing/sys-bus-counter-simple-sysfs for detailed
+information on each Simple Counter interface sysfs attribute.
+
+In addition, several sysfs attributes are generated by the underlying
+Generic Counter interface, and also reside under the
+/sys/bus/counter/devices/counterX directory, where counterX refers to
+the respective counter device. Please see
+Documentation/ABI/testing/sys-bus-counter-generic-sysfs for detailed
+information on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Simple Counter paradigm Counts, Signals, and Synapses of respective
+counter devices.
+
+Driver API
+==========
+
+Driver authors may utilize the Simple 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 simple counter.
+
+
+struct simple_counter_signal_ext
+--------------------------------
+
+This structure defines a Simple Counter Signal extension attribute.
+These attributes expose auxiliary configuration and functionality
+specific to the respective Simple Counter Signal.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct simple_counter_signal
+----------------------------
+
+This structure defines a Simple Counter Signal component. Typically,
+this will correlate with an input channel on a physical counter device.
+This structure is the simplest of the Simple Counter paradigm core
+components to define with only two structure members which require
+explicit configuration:
+
+ id: unique ID used to identify signal
+ name: device-specific signal name
+ ext: optional array of Simple Counter Signal
+ extensions
+ num_ext: number of Simple Counter Signal extensions
+ specified in @ext
+ priv: optional private data supplied by driver
+
+enum simple_counter_signal_level
+--------------------------------
+
+This enum defines enumeration constants to represent the possible Simple
+Signal data level states.
+
+ SIMPLE_COUNTER_SIGNAL_LOW: Low
+ SIMPLE_COUNTER_SIGNAL_HIGH: High
+
+enum simple_counter_function
+----------------------------
+
+This enum defines enumeration constants to represent the possible Simple
+Counter count function modes.
+
+ SIMPLE_COUNTER_FUNCTION_INCREASE: Increase
+ SIMPLE_COUNTER_FUNCTION_DECREASE: Decrease
+
+enum simple_counter_action
+--------------------------
+
+This enum defines enumeration constants to represent the possible Simple
+Counter action modes.
+
+ SIMPLE_COUNTER_ACTION_NONE: None
+ SIMPLE_COUNTER_ACTION_RISING_EDGE: Rising Edge
+ SIMPLE_COUNTER_ACTION_FALLING_EDGE: Falling Edge
+ SIMPLE_COUNTER_ACTION_BOTH_EDGES: Both Edges
+
+struct simple_counter_count_ext
+-------------------------------
+
+This structure defines a Simple Counter Count extension attribute. These
+attributes expose auxiliary configuration and functionality specific to
+the respective Simple Counter Count.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct simple_counter_count
+---------------------------
+
+This structure defines a Simple Counter Count 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, and the Simple Signal associated with this
+Simple Count.
+
+ id: unique ID used to identify Count
+ name: device-specific Count name
+ function: current function mode
+ action: current action mode
+ signal: associated signal
+ ext: optional array of Simple Counter Count
+ extensions
+ num_ext: number of Simple Counter Count extensions
+ specified in @ext
+ priv: optional private data supplied by driver
+
+struct simple_counter_device_ext
+--------------------------------
+
+This structure defines a Simple Counter extension attribute. These
+attributes expose auxiliary configuration and functionality specific to
+the respective Simple Counter.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct simple_counter_device
+----------------------------
+
+This is the main data structure for a Simple Counter device. Access to
+all respective Counts, Signals, and Synapses is possible from this
+structure. This structure requires the configuration of a name and
+Simple Counter Counts:
+
+ name: name of the device
+ parent: optional parent device providing the counters
+ signal_read: read callback for Signal attribute; may be NULL.
+ Returns 0 on success and negative error code on
+ error. The respective Signal's returned level
+ should be passed back via the level parameter.
+ signal_write: write callback for Signal attribute; may be NULL
+ count_read: read callback for Count attribute; may be NULL.
+ Returns 0 on success and negative error code on
+ error. The respective Count's returned value
+ should be passed back via the val parameter.
+ count_write: write callback for Count attribute; may be NULL
+ function_get: function to get the current count function mode.
+ Returns 0 on success and negative error code on
+ error. The respective Count's returned function
+ mode should be passed back via the function
+ parameter.
+ function_set: function to set the count function mode
+ action_get: function to get the current action mode. Returns
+ 0 on success and negative error code on error.
+ The respective Signal's returned action mode
+ should be passed back via the action parameter.
+ action_set: function to set the action mode
+ counts: array of Simple Counter Counts
+ num_counts: number of Simple Counter Counts specified in
+ @counts
+ ext: optional array of Simple Counter device
+ extensions
+ num_ext: number of Simple Counter device extensions
+ specified in @ext
+ priv: optional private data supplied by driver
+
+Registration functions
+----------------------
+
+A simple counter device is registered to the system by passing the
+respective initialized simple_counter_device structure to the
+simple_counter_register function; similarly, the
+simple_counter_unregister function unregisters the respective
+Simple Counter. The devm_simple_counter_register and
+devm_simple_counter_unregister functions serve as device memory-managed
+versions of the simple_counter_register and simple_counter_unregister
+functions respectively.
+
+Implementation
+==============
+
+To use the Simple Counter interface, create an array of
+simple_counter_count structures to represent the desired Counts and
+Signals of the counter device. The simple_counter_count structure has a
+Simple Signal member that represents the associated Signal, so only the
+Simple Counts need to be explicited allocated -- the associated Simple
+Signal members are simply populated with the respective desired
+definitions. The defined simple_counter_count array may then be added
+to a simple_counter_device structure for registration to the system.
+
+ Simple Count Count Signal
+ ------------ ----- ------
++---------------------+ +--------------------+
+| Data: Count | ---> | Data: Count |
+| Function: Increase | | Function: Increase |
+| | +--------------------+
+| |
++---------------------+ ________
+| Signal: Source | ----------------------------> / Source \
+| | ____________
++---------------------+
+
+Driver callbacks should be provided to the simple_counter_device
+structure in order to communicate with the device: to read and write
+various Signals and Counts, and to set and get the "action mode" and
+"function mode" for various Synapses and Counts respectively.
+
+A defined simple_counter_device structure may be registered to the
+system by passing it to the simple_counter_register function, and
+unregistered by passing it to the simple_counter_unregister function.
+Similarly, the devm_simple_counter_register and
+devm_simple_counter_unregister functions may be used if device
+memory-managed registration is desired.
+
+Extension sysfs attributes can be created for auxiliary functionality
+and data by passing in defined simple_counter_device_ext,
+simple_counter_count_ext, and simple_counter_signal_ext structures. In
+these cases, the simple_counter_device_ext structure is used for global
+configuration of the respective Counter device, while the
+simple_counter_count_ext and simple_counter_signal_ext structures allow
+for auxiliary exposure and configuration of a specific Count or Signal
+respectively.
+
+Architecture
+============
+
+The Simple Counter interface is a subclass of the Generic Counter
+interface, and the Simple Counter interface functions serve as mappings
+onto the Generic Counter interface functions to simplify and aptly
+define support for simple counter devices.
+
+In this vein, the Generic Counter interface functions are ultimately
+called when the Simple Counter interface functions are utilized. For
+more information on the architecture of the Generic Counter interface,
+please refer to the Documentation/driver-api/iio/generic-counter.rst
+file.
+
+Simple Counter devices are registered to the system via the
+simple_counter_register function, and later removed via the
+simple_counter_unregister function. The simple_counter_register function
+will allocate a counter_device structure, populate it with the required
+Generic Counter structures and data required to represent the Simple
+Counter components, and register it to the system via a counter_register
+call.
+
+ _______________________ +-------------------------+
+ / simple_counter_device \ --> | simple_counter_register |
+___________________________ +-------------------------+
+ |
+ +------------------------------+
+ |
+ V
+ ________________ +------------------+
+ / counter_device \ --> | counter_register |
+____________________ +------------------+
+
+The signal_read/signal_write driver callbacks, the
+count_read/count_write and function_get/function_set driver callbacks,
+and the action_get/action_set driver callbacks are mapped to the
+allocated internal counter_device structure via appropriate hook
+functions.
+
+ Simple Counter Remap Function Generic Counter
+ -------------- -------------- ---------------
+
+ signal_read simple_counter_signal_read signal_read
+ signal_write simple_counter_signal_write signal_write
+ count_read simple_counter_count_read count_read
+ count_write simple_counter_count_write count_write
+ function_get simple_counter_function_get function_get
+ function_set simple_counter_function_set function_set
+ action_get simple_counter_action_get action_get
+ action_set simple_counter_action_set action_set
+
+This is how Generic Counter interface sysfs attributes are inherited and
+extended by the Simple Counter interface. If a driver callback is left
+undefined, then the respective read/write permission is left disabled
+for the relevant attributes.
+
+Similarly, simple_counter_device_ext, simple_counter_count_ext, and
+simple_counter_signal_ext structures are mapped to respective
+counter_device_ext, counter_count_ext, and counter_signal_ext structures
+via appropriate hook functions, and then added to the allocated internal
+counter_device structure for registration.
--
2.15.1
This patch introduces the dummy counter driver. The dummy counter driver
serves as a reference implementation of a driver that utilizes the
Simple Counter interface.
Writing "low" and "high" to the Signal attributes allows a user to
simulate a typical Simple Counter Signal input stream for evaluation;
the Counter will evaluate the Signal data based on the respective action
mode for the associated Signal, and trigger the associated count
function specified by the respective Count's function mode. The current
Count value may be read, and the Count value preset by a write.
The Count max and min attributes serve to configure respective value
ceiling and value floor for each desired Count.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/counter/Kconfig | 15 ++
drivers/iio/counter/Makefile | 1 +
drivers/iio/counter/dummy-counter.c | 308 ++++++++++++++++++++++++++++++++++++
3 files changed, 324 insertions(+)
create mode 100644 drivers/iio/counter/dummy-counter.c
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 6b9a43180d2c..9d7dae137f9c 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -30,6 +30,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 "low" and "high" to the Signal attributes allows a user to
+ simulate a typical Simple 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) && IIO
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 7450dee97446..febd2884b474 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -9,4 +9,5 @@ counter-$(CONFIG_COUNTER) += generic-counter.o
counter-$(CONFIG_COUNTER) += simple-counter.o
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
diff --git a/drivers/iio/counter/dummy-counter.c b/drivers/iio/counter/dummy-counter.c
new file mode 100644
index 000000000000..31d7e3a7d8fa
--- /dev/null
+++ b/drivers/iio/counter/dummy-counter.c
@@ -0,0 +1,308 @@
+/*
+ * 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 Simple Counter
+ * @counts: array of accumulation counts
+ * @max: array of value ceilings for each count
+ * @min: array of value floors for each count
+ * @states: array of input line states
+ */
+struct dumcnt {
+ struct simple_counter_device counter;
+ long counts[DUMCNT_NUM_COUNTERS];
+ long max[DUMCNT_NUM_COUNTERS];
+ long min[DUMCNT_NUM_COUNTERS];
+ enum simple_counter_signal_level states[DUMCNT_NUM_COUNTERS];
+};
+
+static int dumcnt_signal_read(struct simple_counter_device *counter,
+ struct simple_counter_signal *signal,
+ enum simple_counter_signal_level *level)
+{
+ struct dumcnt *const priv = counter->priv;
+
+ *level = priv->states[signal->id];
+
+ return 0;
+}
+
+static int dumcnt_signal_write(struct simple_counter_device *counter,
+ struct simple_counter_signal *signal,
+ enum simple_counter_signal_level level)
+{
+ struct dumcnt *const priv = counter->priv;
+ const int id = signal->id;
+ const enum simple_counter_signal_level prev_state = priv->states[id];
+ struct simple_counter_count *const count = counter->counts + id;
+ unsigned int triggered = 0;
+
+ /* If no state change then just exit */
+ if (prev_state == level)
+ return 0;
+
+ priv->states[id] = level;
+
+ /* Check if Count function was triggered */
+ switch (count->action) {
+ case SIMPLE_COUNTER_ACTION_NONE:
+ return 0;
+ case SIMPLE_COUNTER_ACTION_RISING_EDGE:
+ if (!prev_state)
+ triggered = 1;
+ break;
+ case SIMPLE_COUNTER_ACTION_FALLING_EDGE:
+ if (prev_state)
+ triggered = 1;
+ break;
+ case SIMPLE_COUNTER_ACTION_BOTH_EDGES:
+ triggered = 1;
+ break;
+ }
+
+ /* Exit early if Count function was not triggered */
+ if (!triggered)
+ return 0;
+
+ /* Execute Count function */
+ switch (count->function) {
+ case SIMPLE_COUNTER_FUNCTION_INCREASE:
+ if (priv->counts[id] < priv->max[id])
+ priv->counts[id]++;
+ break;
+ case SIMPLE_COUNTER_FUNCTION_DECREASE:
+ if (priv->counts[id] > priv->min[id])
+ priv->counts[id]--;
+ break;
+ }
+
+ return 0;
+}
+
+static int dumcnt_count_read(struct simple_counter_device *counter,
+ struct simple_counter_count *count, long *val)
+{
+ struct dumcnt *const priv = counter->priv;
+
+ *val = priv->counts[count->id];
+
+ return 0;
+}
+
+static int dumcnt_count_write(struct simple_counter_device *counter,
+ struct simple_counter_count *count, long val)
+{
+ struct dumcnt *const priv = counter->priv;
+ const int id = count->id;
+
+ /* Verify that value is within configured boundaries */
+ if (val > priv->max[id] || val < priv->min[id])
+ return -EINVAL;
+
+ priv->counts[id] = val;
+
+ return 0;
+}
+
+static int dumcnt_function_get(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_function *function)
+{
+ *function = count->function;
+
+ return 0;
+}
+
+static int dumcnt_function_set(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_function function)
+{
+ return 0;
+}
+
+static int dumcnt_action_get(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_action *action)
+{
+ *action = count->action;
+
+ return 0;
+}
+
+static int dumcnt_action_set(struct simple_counter_device *counter,
+ struct simple_counter_count *count,
+ enum simple_counter_action action)
+{
+ return 0;
+}
+
+ssize_t dumcnt_count_max_read(struct simple_counter_device *counter,
+ struct simple_counter_count *count, void *priv, char *buf)
+{
+ struct dumcnt *const drv_data = counter->priv;
+
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", drv_data->max[count->id]);
+}
+
+ssize_t dumcnt_count_max_write(struct simple_counter_device *counter,
+ struct simple_counter_count *count, void *priv, const char *buf,
+ size_t len)
+{
+ int err;
+ struct dumcnt *const drv_data = counter->priv;
+
+ err = kstrtol(buf, 0, drv_data->max + count->id);
+ if (err)
+ return err;
+
+ return len;
+}
+
+ssize_t dumcnt_count_min_read(struct simple_counter_device *counter,
+ struct simple_counter_count *count, void *priv, char *buf)
+{
+ struct dumcnt *const drv_data = counter->priv;
+
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", drv_data->min[count->id]);
+}
+
+ssize_t dumcnt_count_min_write(struct simple_counter_device *counter,
+ struct simple_counter_count *count, void *priv, const char *buf,
+ size_t len)
+{
+ int err;
+ struct dumcnt *const drv_data = counter->priv;
+
+ err = kstrtol(buf, 0, drv_data->min + count->id);
+ if (err)
+ return err;
+
+ return len;
+}
+
+static const struct simple_counter_count_ext dumcnt_count_ext[] = {
+ {
+ .name = "max",
+ .read = dumcnt_count_max_read,
+ .write = dumcnt_count_max_write
+ },
+ {
+ .name = "min",
+ .read = dumcnt_count_min_read,
+ .write = dumcnt_count_min_write
+ }
+};
+
+#define DUMCNT_COUNT(_id, _cntname, _signame) { \
+ .id = _id, \
+ .name = _cntname, \
+ .signal = { \
+ .id = _id, \
+ .name = _signame \
+ }, \
+ .ext = dumcnt_count_ext, \
+ .num_ext = ARRAY_SIZE(dumcnt_count_ext) \
+}
+
+static const struct simple_counter_count dumcnt_counts[] = {
+ DUMCNT_COUNT(0, "Count A", "Signal A"),
+ DUMCNT_COUNT(1, "Count B", "Signal B")
+};
+
+static int dumcnt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct simple_counter_count *counts;
+ struct dumcnt *dumcnt;
+
+ counts = devm_kmemdup(dev, dumcnt_counts, sizeof(dumcnt_counts),
+ GFP_KERNEL);
+ if (!counts)
+ return -ENOMEM;
+
+ dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL);
+ if (!dumcnt)
+ return -ENOMEM;
+
+ dumcnt->counter.name = dev_name(dev);
+ dumcnt->counter.parent = dev;
+ dumcnt->counter.signal_read = dumcnt_signal_read;
+ dumcnt->counter.signal_write = dumcnt_signal_write;
+ dumcnt->counter.count_read = dumcnt_count_read;
+ dumcnt->counter.count_write = dumcnt_count_write;
+ dumcnt->counter.function_get = dumcnt_function_get;
+ dumcnt->counter.function_set = dumcnt_function_set;
+ dumcnt->counter.action_get = dumcnt_action_get;
+ dumcnt->counter.action_set = dumcnt_action_set;
+ dumcnt->counter.counts = counts;
+ dumcnt->counter.num_counts = ARRAY_SIZE(dumcnt_counts);
+ dumcnt->counter.priv = dumcnt;
+
+ return devm_simple_counter_register(dev, &dumcnt->counter);
+}
+
+static struct platform_device *dumcnt_device;
+
+static struct platform_driver dumcnt_driver = {
+ .driver = {
+ .name = "dummy-counter"
+ }
+};
+
+static void __exit dumcnt_exit(void)
+{
+ platform_device_unregister(dumcnt_device);
+ platform_driver_unregister(&dumcnt_driver);
+}
+
+static int __init dumcnt_init(void)
+{
+ int err;
+
+ 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");
--
2.15.1
This patch introduces the Quadrature Counter interface. The Quadrature
Counter interface serves as an API to provide support for quadrature
encoder counter devices. The Quadrature Counter interface is built on
top of the Generic Counter interface.
A quadrature encoder counter device is a counter device that has a
quadrature pair of signals associated with each count value. Signals may
have a value of "low" or "high."
Signals may be represented by two possible states:
QUAD_COUNTER_SIGNAL_LOW: "low"
QUAD_COUNTER_SIGNAL_HIGH: "high"
With quadrature encoders, there types of encoding are typically used:
X1, X2, and X4; some quadrature encoders also offer a non-quadrature
mode (typically pulse-direction encoding).
The Quadrature Counter interface provides four count function modes:
QUAD_COUNTER_FUNCTION_PULSE_DIRECTION: "pulse-direction"
QUAD_COUNTER_FUNCTION_QUADRATURE_X1: "quadrature x1"
QUAD_COUNTER_FUNCTION_QUADRATURE_X2: "quadrature x2"
QUAD_COUNTER_FUNCTION_QUADRATURE_X4: "quadrature x4"
Since the Quadrature Counter interface utilizes the Generic Counter
interface underneath, all the expected functionality of the Generic
Counter interface such as sysfs attributes is exposed to userspace for
end user consumption. The Quadrature Counter interface serves as a
convenience API for supporting a common class of counter devices without
the need to manually configure the more cumbersome Generic Counter
interface for use.
In addition to the typical sysfs attributes of the Generic Counter
interface, the Quadrature Counter interface provides "direction"
attributes for each count value. These read-only attributes provide the
current direction of their respective quadrature encoding stream.
To use the Quadrature Counter interface, first create an array of
quad_counter_count structures to represent the desired counts and
signals of the counter device; the signal_a member of a
quad_counter_count structure should define the Channel A signal of the
respective quadrature pair, and similarly the signal_b member should
define Channel B. Next, allocate a quad_counter_device structure and
populate it with the desired driver callbacks and the quad_counter_count
array created earlier. Finally, register the counter by calling the
quad_counter_register function. The quad_counter_unregister function may
be used to unregistered a previously registered counter.
Memory-managed versions of quad_counter_register and
quad_counter_unregister functions are provided by the
devm_quad_counter_register and devm_quad_counter_unregister functions
respectively.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/counter/Kconfig | 4 +-
drivers/iio/counter/Makefile | 1 +
drivers/iio/counter/quad-counter.c | 774 +++++++++++++++++++++++++++++++++++++
include/linux/iio/counter.h | 191 +++++++++
4 files changed, 969 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/counter/quad-counter.c
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 9d7dae137f9c..33fde25e5018 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -10,7 +10,9 @@ menuconfig COUNTER
rudimentary support for counters and serves as building blocks to
create more complex counter interfaces. The Simple Counter API
provides support for simple hardware counter devices that have a
- one-to-one mapping between their Signals and Counts.
+ one-to-one mapping between their Signals and Counts. The Quadrature
+ Counter API provides support for quadrature counter devices that have
+ Signals arranged as quadrature pairs associated to Counts.
if COUNTER
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index febd2884b474..55f59e566d72 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_COUNTER) += counter.o
counter-$(CONFIG_COUNTER) += generic-counter.o
+counter-$(CONFIG_COUNTER) += quad-counter.o
counter-$(CONFIG_COUNTER) += simple-counter.o
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
diff --git a/drivers/iio/counter/quad-counter.c b/drivers/iio/counter/quad-counter.c
new file mode 100644
index 000000000000..74a738e4b515
--- /dev/null
+++ b/drivers/iio/counter/quad-counter.c
@@ -0,0 +1,774 @@
+/*
+ * Quadrature 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.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <linux/iio/counter.h>
+
+static const char *const quad_counter_signal_level_names[] = {
+ [QUAD_COUNTER_SIGNAL_LOW] = "low",
+ [QUAD_COUNTER_SIGNAL_HIGH] = "high"
+};
+
+static ssize_t quad_counter_signal_read(struct counter_device *counter_dev,
+ struct counter_signal *counter_sig, char *buf)
+{
+ struct quad_counter_device *const counter = counter_dev->priv;
+ struct quad_counter_signal *const signal = counter_sig->priv;
+ int err;
+ enum quad_counter_signal_level level;
+
+ err = counter->signal_read(counter, signal, &level);
+ if (err)
+ return err;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ quad_counter_signal_level_names[level]);
+}
+
+static ssize_t quad_counter_count_read(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, char *buf)
+{
+ struct quad_counter_device *const counter = counter_dev->priv;
+ struct quad_counter_count *const count = counter_cnt->priv;
+ int err;
+ long val;
+
+ err = counter->count_read(counter, count, &val);
+ if (err)
+ return err;
+
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
+}
+
+static ssize_t quad_counter_count_write(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, const char *buf, size_t len)
+{
+ struct quad_counter_device *const counter = counter_dev->priv;
+ struct quad_counter_count *const count = counter_cnt->priv;
+ int err;
+ long val;
+
+ err = kstrtol(buf, 0, &val);
+ if (err)
+ return err;
+
+ err = counter->count_write(counter, count, val);
+ if (err)
+ return err;
+
+ return len;
+}
+
+static int quad_counter_function_get(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, size_t *counter_func)
+{
+ int err;
+ struct quad_counter_device *const counter = counter_dev->priv;
+ struct quad_counter_count *const count = counter_cnt->priv;
+ enum quad_counter_function function;
+
+ err = counter->function_get(counter, count, &function);
+ if (err)
+ return err;
+
+ count->function = function;
+
+ *counter_func = function;
+
+ return 0;
+}
+
+static int quad_counter_function_set(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, size_t function)
+{
+ struct quad_counter_device *const counter = counter_dev->priv;
+ struct quad_counter_count *const count = counter_cnt->priv;
+ int err;
+
+ err = counter->function_set(counter, count, function);
+ if (err)
+ return err;
+
+ count->function = function;
+
+ return 0;
+}
+
+enum quad_counter_action {
+ QUAD_COUNTER_ACTION_NONE = 0,
+ QUAD_COUNTER_ACTION_RISING_EDGE,
+ QUAD_COUNTER_ACTION_FALLING_EDGE,
+ QUAD_COUNTER_ACTION_BOTH_EDGES
+};
+
+static int quad_counter_action_get(struct counter_device *counter_dev,
+ struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
+ size_t *counter_act)
+{
+ int err;
+ struct quad_counter_device *const counter = counter_dev->priv;
+ struct quad_counter_count *const count = counter_cnt->priv;
+ enum quad_counter_function function;
+ enum quad_counter_direction dir;
+
+ err = counter->function_get(counter, count, &function);
+ if (err)
+ return err;
+
+ /* Default action mode */
+ *counter_act = QUAD_COUNTER_ACTION_NONE;
+
+ /* Determine action mode based on current count function mode */
+ switch (function) {
+ case QUAD_COUNTER_FUNCTION_PULSE_DIRECTION:
+ if (count->signal_a.id == counter_syn->signal->id)
+ *counter_act = QUAD_COUNTER_ACTION_RISING_EDGE;
+ break;
+ case QUAD_COUNTER_FUNCTION_QUADRATURE_X1:
+ if (count->signal_a.id == counter_syn->signal->id) {
+ err = counter->direction_get(counter, count, &dir);
+ if (err)
+ return err;
+
+ if (dir == QUAD_COUNTER_DIRECTION_FORWARD)
+ *counter_act = QUAD_COUNTER_ACTION_RISING_EDGE;
+ else
+ *counter_act = QUAD_COUNTER_ACTION_FALLING_EDGE;
+ }
+ break;
+ case QUAD_COUNTER_FUNCTION_QUADRATURE_X2:
+ if (count->signal_a.id == counter_syn->signal->id)
+ *counter_act = QUAD_COUNTER_ACTION_BOTH_EDGES;
+ break;
+ case QUAD_COUNTER_FUNCTION_QUADRATURE_X4:
+ *counter_act = QUAD_COUNTER_ACTION_BOTH_EDGES;
+ break;
+ }
+
+ return 0;
+}
+
+static ssize_t quad_counter_signal_ext_read(struct counter_device *dev,
+ struct counter_signal *signal, void *priv, char *buf)
+{
+ const struct quad_counter_signal_ext *const ext = priv;
+ struct quad_counter_device *const counter = dev->priv;
+ struct quad_counter_signal *const quad_signal = signal->priv;
+
+ return ext->read(counter, quad_signal, ext->priv, buf);
+}
+
+static ssize_t quad_counter_signal_ext_write(struct counter_device *dev,
+ struct counter_signal *signal, void *priv, const char *buf, size_t len)
+{
+ const struct quad_counter_signal_ext *const ext = priv;
+ struct quad_counter_device *const counter = dev->priv;
+ struct quad_counter_signal *const quad_signal = signal->priv;
+
+ return ext->write(counter, quad_signal, ext->priv, buf, len);
+}
+
+static int quad_counter_counter_signal_ext_register(
+ const struct quad_counter_signal *const quad_signal,
+ struct counter_signal *const signal)
+{
+ const struct quad_counter_signal_ext *const quad_ext = quad_signal->ext;
+ const size_t num_ext = quad_signal->num_ext;
+ struct counter_signal_ext *ext;
+ size_t i;
+
+ /* Exit early if no extensions */
+ if (!quad_ext || !num_ext)
+ return 0;
+
+ /* Allocate space for counter_signal_ext array */
+ ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
+ if (!ext)
+ return -ENOMEM;
+
+ /* Register quad_counter_signal_ext via counter_signal_ext */
+ for (i = 0; i < num_ext; i++) {
+ ext[i].name = quad_ext[i].name;
+ ext[i].read = (quad_ext[i].read) ?
+ quad_counter_signal_ext_read : NULL;
+ ext[i].write = (quad_ext[i].write) ?
+ quad_counter_signal_ext_write : NULL;
+ ext[i].priv = quad_ext + i;
+ }
+
+ /* Register Counter Signal extensions */
+ signal->ext = ext;
+ signal->num_ext = num_ext;
+
+ return 0;
+}
+
+static int quad_counter_counter_signals_register(
+ const struct quad_counter_device *const counter)
+{
+ struct counter_signal *signals;
+ const size_t num_counts = counter->num_counts;
+ const size_t num_signals = 2 * num_counts;
+ size_t i;
+ struct counter_signal *signal;
+ struct quad_counter_signal *quad_signal;
+ struct quad_counter_count *const counts = counter->counts;
+ int err;
+ struct counter_device *const counter_dev = counter->counter_dev;
+
+ /* Allocate space for signals array */
+ signals = kcalloc(num_signals, sizeof(*signals), GFP_KERNEL);
+ if (!signals)
+ return -ENOMEM;
+
+ /* Configure Signals */
+ for (i = 0; i < num_signals; i++) {
+ signal = signals + i;
+ if (i % 2)
+ quad_signal = &counts[i / 2].signal_b;
+ else
+ quad_signal = &counts[i / 2].signal_a;
+
+ signal->id = quad_signal->id;
+ signal->name = quad_signal->name;
+ signal->priv = quad_signal;
+
+ /* Register Counter Signal extensions */
+ err = quad_counter_counter_signal_ext_register(quad_signal,
+ signal);
+ if (err)
+ goto err_free_signals;
+ }
+
+ /* Register Signals to Counter device container */
+ counter_dev->signals = signals;
+ counter_dev->num_signals = num_signals;
+
+ return 0;
+
+err_free_signals:
+ while (i--)
+ kfree(signals[i].ext);
+ kfree(signals);
+ return err;
+}
+
+static const char *const quad_counter_function_names[] = {
+ [QUAD_COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
+ [QUAD_COUNTER_FUNCTION_QUADRATURE_X1] = "quadrature x1",
+ [QUAD_COUNTER_FUNCTION_QUADRATURE_X2] = "quadrature x2",
+ [QUAD_COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
+};
+
+static const char *const quad_counter_action_names[] = {
+ [QUAD_COUNTER_ACTION_NONE] = "none",
+ [QUAD_COUNTER_ACTION_RISING_EDGE] = "rising edge",
+ [QUAD_COUNTER_ACTION_FALLING_EDGE] = "falling edge",
+ [QUAD_COUNTER_ACTION_BOTH_EDGES] = "both edges"
+};
+
+static int quad_counter_counter_synapses_register(
+ struct counter_signal *const signals, struct counter_count *const count)
+{
+ struct counter_synapse *synapses;
+ const size_t num_synapses = 2;
+ size_t i;
+
+ /* Allocate space for Counter Synapses */
+ synapses = kcalloc(num_synapses, sizeof(*synapses), GFP_KERNEL);
+ if (!synapses)
+ return -ENOMEM;
+
+ /* Configure Synapses */
+ for (i = 0; i < num_synapses; i++) {
+ synapses[i].signal = signals + i;
+ synapses[i].actions = quad_counter_action_names;
+ synapses[i].num_actions = ARRAY_SIZE(quad_counter_action_names);
+ }
+
+ /* Register Counter Synapses */
+ count->synapses = synapses;
+ count->num_synapses = num_synapses;
+
+ return 0;
+}
+
+static const char *const quad_counter_direction_names[] = {
+ [QUAD_COUNTER_DIRECTION_FORWARD] = "forward",
+ [QUAD_COUNTER_DIRECTION_BACKWARD] = "backward"
+};
+
+static ssize_t quad_counter_direction_read(struct counter_device *dev,
+ struct counter_count *count, void *priv, char *buf)
+{
+ struct quad_counter_device *const counter = dev->priv;
+ struct quad_counter_count *const quad_count = count->priv;
+ int err;
+ enum quad_counter_direction direction;
+
+ err = counter->direction_get(counter, quad_count, &direction);
+ if (err)
+ return err;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ quad_counter_direction_names[direction]);
+}
+
+static ssize_t quad_counter_count_ext_read(struct counter_device *dev,
+ struct counter_count *count, void *priv, char *buf)
+{
+ const struct quad_counter_count_ext *const ext = priv;
+ struct quad_counter_device *const counter = dev->priv;
+ struct quad_counter_count *const quad_count = count->priv;
+
+ return ext->read(counter, quad_count, ext->priv, buf);
+}
+
+static ssize_t quad_counter_count_ext_write(struct counter_device *dev,
+ struct counter_count *count, void *priv, const char *buf, size_t len)
+{
+ const struct quad_counter_count_ext *const ext = priv;
+ struct quad_counter_device *const counter = dev->priv;
+ struct quad_counter_count *const quad_count = count->priv;
+
+ return ext->write(counter, quad_count, ext->priv, buf, len);
+}
+
+static int quad_counter_counter_count_ext_register(
+ const struct quad_counter_device *const counter,
+ const struct quad_counter_count *const quad_count,
+ struct counter_count *const count)
+{
+ size_t num_ext = 0;
+ const struct quad_counter_count_ext *const quad_ext = quad_count->ext;
+ const size_t quad_num_ext = quad_count->num_ext;
+ struct counter_count_ext *ext;
+ size_t ext_i = 0;
+ size_t i;
+
+ /* Count number of extensions */
+ if (counter->direction_get)
+ num_ext++;
+ if (quad_ext)
+ num_ext += quad_num_ext;
+
+ /* Return early if no extensions */
+ if (!num_ext)
+ return 0;
+
+ /* Allocate space for Counter Count extensions array */
+ ext = kcalloc(num_ext, sizeof(*ext), GFP_KERNEL);
+ if (!ext)
+ return -ENOMEM;
+
+ /* Register direction extension */
+ if (counter->direction_get) {
+ ext[ext_i].name = "direction";
+ ext[ext_i].read = quad_counter_direction_read;
+
+ ext_i++;
+ }
+
+ /* Register driver Quadrature Counter Count extensions */
+ for (i = 0; i < quad_num_ext; i++) {
+ ext[ext_i + i].name = quad_ext[i].name;
+ ext[ext_i + i].read = (quad_ext[i].read) ?
+ quad_counter_count_ext_read : NULL;
+ ext[ext_i + i].write = (quad_ext[i].write) ?
+ quad_counter_count_ext_write : NULL;
+ ext[ext_i + i].priv = quad_ext + i;
+ }
+ ext_i += quad_num_ext;
+
+ /* Register Counter Count extensions */
+ count->ext = ext;
+ count->num_ext = num_ext;
+
+ return 0;
+}
+
+static void quad_counter_counter_synapses_unregister(
+ const struct counter_count *const count)
+{
+ kfree(count->synapses);
+}
+
+static int quad_counter_counter_count_init(struct counter_count *const count,
+ struct quad_counter_count *const quad_count,
+ struct counter_signal *const signals,
+ const struct quad_counter_device *const counter)
+{
+ int err;
+
+ count->id = quad_count->id;
+ count->name = quad_count->name;
+ count->functions = quad_counter_function_names;
+ count->num_functions = ARRAY_SIZE(quad_counter_function_names);
+ count->priv = quad_count;
+
+ /* Register Counter Synapses */
+ err = quad_counter_counter_synapses_register(signals, count);
+ if (err)
+ return -ENOMEM;
+
+ /* Register Quadrature Counter Count extensions */
+ err = quad_counter_counter_count_ext_register(counter, quad_count,
+ count);
+ if (err)
+ goto err_unregister_synapses;
+
+ return 0;
+
+err_unregister_synapses:
+ quad_counter_counter_synapses_unregister(count);
+ return err;
+}
+
+static void quad_counter_counter_count_ext_unregister(
+ const struct counter_count *const count)
+{
+ kfree(count->ext);
+}
+
+static void quad_counter_counter_count_free(
+ const struct counter_count *const count)
+{
+ quad_counter_counter_count_ext_unregister(count);
+ quad_counter_counter_synapses_unregister(count);
+}
+
+static int quad_counter_counter_counts_register(
+ const struct quad_counter_device *const counter)
+{
+ struct counter_device *const counter_dev = counter->counter_dev;
+ struct counter_count *counts;
+ const size_t num_counts = counter->num_counts;
+ size_t i;
+ struct quad_counter_count *const quad_counts = counter->counts;
+ struct counter_signal *const signals = counter_dev->signals;
+ int err;
+
+ /* Allocate space for counts array */
+ counts = kcalloc(num_counts, sizeof(*counts), GFP_KERNEL);
+ if (!counts)
+ return -ENOMEM;
+
+ /* Initialize Counts */
+ for (i = 0; i < num_counts; i++) {
+ err = quad_counter_counter_count_init(counts + i,
+ quad_counts + i, signals + 2 * i, counter);
+ if (err)
+ goto err_free_counts;
+ }
+
+ /* Register Counts to Counter device container */
+ counter_dev->counts = counts;
+ counter_dev->num_counts = num_counts;
+
+ return 0;
+
+err_free_counts:
+ while (i--)
+ quad_counter_counter_count_free(counts + i);
+ kfree(counts);
+ return err;
+}
+
+static void quad_counter_counter_signals_unregister(
+ const struct counter_device *const counter_dev)
+{
+ const struct counter_signal *const signals = counter_dev->signals;
+ size_t num_signals = counter_dev->num_signals;
+
+ while (num_signals--)
+ kfree(signals[num_signals].ext);
+ kfree(signals);
+}
+
+static int quad_counter_counts_register(
+ struct quad_counter_device *const counter)
+{
+ const struct quad_counter_count *const quad_counts = counter->counts;
+ const size_t num_counts = counter->num_counts;
+ int err;
+
+ /* At least one Count must be defined */
+ if (!quad_counts || !num_counts) {
+ pr_err("quad-counter: Quadrature Counter Counts undefined\n");
+ return -EINVAL;
+ }
+
+ /* Allocate Counter Signals */
+ err = quad_counter_counter_signals_register(counter);
+ if (err)
+ return err;
+
+ /* Allocate Counter Counts */
+ err = quad_counter_counter_counts_register(counter);
+ if (err)
+ goto err_unregister_signals;
+
+ return 0;
+
+err_unregister_signals:
+ quad_counter_counter_signals_unregister(counter->counter_dev);
+ return err;
+}
+
+static ssize_t quad_counter_device_ext_read(struct counter_device *dev,
+ void *priv, char *buf)
+{
+ const struct quad_counter_device_ext *const ext = priv;
+ struct quad_counter_device *const counter = dev->priv;
+
+ return ext->read(counter, ext->priv, buf);
+}
+
+static ssize_t quad_counter_device_ext_write(struct counter_device *dev,
+ void *priv, const char *buf, size_t len)
+{
+ const struct quad_counter_device_ext *const ext = priv;
+ struct quad_counter_device *const counter = dev->priv;
+
+ return ext->write(counter, ext->priv, buf, len);
+}
+
+static int quad_counter_device_ext_register(
+ struct quad_counter_device *const counter)
+{
+ const struct quad_counter_device_ext *const quad_ext = counter->ext;
+ const size_t num_ext = counter->num_ext;
+ struct counter_device_ext *ext;
+ size_t i;
+ struct counter_device *const counter_dev = counter->counter_dev;
+
+ /* Return early if no extensions */
+ if (!quad_ext || !num_ext)
+ return 0;
+
+ /* Allocate space for counter_device_ext array */
+ ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
+ if (!ext)
+ return -ENOMEM;
+
+ /* Register quad_counter_device_ext via counter_device_ext */
+ for (i = 0; i < num_ext; i++) {
+ ext[i].name = quad_ext[i].name;
+ ext[i].read = (quad_ext[i].read) ?
+ quad_counter_device_ext_read : NULL;
+ ext[i].write = (quad_ext[i].write) ?
+ quad_counter_device_ext_write : NULL;
+ ext[i].priv = quad_ext + i;
+ }
+
+ /* Register Counter device extensions */
+ counter_dev->ext = ext;
+ counter_dev->num_ext = num_ext;
+
+ return 0;
+}
+
+static void quad_counter_counter_counts_unregister(
+ const struct counter_device *const counter_dev)
+{
+ const struct counter_count *const counts = counter_dev->counts;
+ size_t num_counts = counter_dev->num_counts;
+
+ while (num_counts--)
+ quad_counter_counter_count_free(counts + num_counts);
+ kfree(counts);
+}
+
+static void quad_counter_counts_unregister(
+ const struct quad_counter_device *const counter)
+{
+ const struct counter_device *const counter_dev = counter->counter_dev;
+
+ quad_counter_counter_counts_unregister(counter_dev);
+ quad_counter_counter_signals_unregister(counter_dev);
+}
+
+/**
+ * quad_counter_register - register Quadrature Counter to the system
+ * @counter: pointer to Quadrature Counter to register
+ *
+ * This function registers a Quadrature Counter to the system. A sysfs "counter"
+ * directory will be created and populated with sysfs attributes correlating
+ * with the Quadrature Counter Signals, Synapses, and Counts respectively.
+ */
+int quad_counter_register(struct quad_counter_device *const counter)
+{
+ struct counter_device *counter_dev;
+ int err;
+
+ if (!counter)
+ return -EINVAL;
+
+ /* Allocate internal Counter container */
+ counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL);
+ if (!counter)
+ return -ENOMEM;
+ counter->counter_dev = counter_dev;
+
+ /* Configure internal Counter */
+ counter_dev->name = counter->name;
+ counter_dev->parent = counter->parent;
+ counter_dev->signal_read = (counter->signal_read) ?
+ quad_counter_signal_read : NULL;
+ counter_dev->count_read = (counter->count_read) ?
+ quad_counter_count_read : NULL;
+ counter_dev->count_write = (counter->count_write) ?
+ quad_counter_count_write : NULL;
+ counter_dev->function_get = (counter->function_get) ?
+ quad_counter_function_get : NULL;
+ counter_dev->function_set = (counter->function_set) ?
+ quad_counter_function_set : NULL;
+ counter_dev->action_get = (counter->function_get &&
+ counter->direction_get) ? quad_counter_action_get : NULL;
+ counter_dev->priv = counter;
+
+ /* Register Quadrature Counter Counts */
+ err = quad_counter_counts_register(counter);
+ if (err)
+ goto err_free_counter_dev;
+
+ /* Register Quadrature Counter device extension attributes */
+ err = quad_counter_device_ext_register(counter);
+ if (err)
+ goto err_unregister_counts;
+
+ /* Register internal Counter to the system */
+ err = counter_register(counter_dev);
+ if (err)
+ goto err_free_ext;
+
+ return 0;
+
+err_free_ext:
+ kfree(counter_dev->ext);
+err_unregister_counts:
+ quad_counter_counts_unregister(counter);
+err_free_counter_dev:
+ kfree(counter_dev);
+ return err;
+}
+EXPORT_SYMBOL(quad_counter_register);
+
+/**
+ * quad_counter_unregister - unregister Quadrature Counter from the system
+ * @counter: pointer to Quadrature Counter to unregister
+ *
+ * The Quadrature Counter is unregistered from the system; all allocated memory
+ * is freed.
+ */
+void quad_counter_unregister(struct quad_counter_device *const counter)
+{
+ struct counter_device *counter_dev;
+
+ if (!counter)
+ return;
+
+ counter_dev = counter->counter_dev;
+
+ counter_unregister(counter_dev);
+
+ kfree(counter_dev->ext);
+ quad_counter_counts_unregister(counter);
+ kfree(counter_dev);
+}
+EXPORT_SYMBOL(quad_counter_unregister);
+
+static void devm_quad_counter_unreg(struct device *dev, void *res)
+{
+ quad_counter_unregister(*(struct quad_counter_device **)res);
+}
+
+/**
+ * devm_quad_counter_register - Resource-managed quad_counter_register
+ * @dev: device to allocate quad_counter_device for
+ * @counter: pointer to Quadrature Counter to register
+ *
+ * Managed quad_counter_register. The Quadrature Counter registered with this
+ * function is automatically unregistered on driver detach. This function calls
+ * quad_counter_register internally. Refer to that function for more
+ * information.
+ *
+ * If an Quadrature Counter registered with this function needs to be
+ * unregistered separately, devm_quad_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_quad_counter_register(struct device *dev,
+ struct quad_counter_device *const counter)
+{
+ struct quad_counter_device **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_quad_counter_unreg, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = quad_counter_register(counter);
+ if (!ret) {
+ *ptr = counter;
+ devres_add(dev, ptr);
+ } else
+ devres_free(ptr);
+
+ return ret;
+}
+EXPORT_SYMBOL(devm_quad_counter_register);
+
+static int devm_quad_counter_match(struct device *dev, void *res, void *data)
+{
+ struct quad_counter_device **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_quad_counter_unregister - Resource-managed quad_counter_unregister
+ * @dev: device this quad_counter_device belongs to
+ * @counter: the Quadrature Counter associated with the device
+ *
+ * Unregister Quadrature Counter registered with devm_quad_counter_register.
+ */
+void devm_quad_counter_unregister(struct device *dev,
+ struct quad_counter_device *const counter)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_quad_counter_unreg,
+ devm_quad_counter_match, counter);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_quad_counter_unregister);
+
+MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
+MODULE_DESCRIPTION("Quadrature Counter interface");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
index 0967ea2a9bef..a6f0f9130377 100644
--- a/include/linux/iio/counter.h
+++ b/include/linux/iio/counter.h
@@ -435,4 +435,195 @@ extern int devm_simple_counter_register(struct device *dev,
extern void devm_simple_counter_unregister(struct device *dev,
struct simple_counter_device *const counter);
+struct quad_counter_device;
+struct quad_counter_signal;
+
+/**
+ * struct quad_counter_signal_ext - Quadrature Counter Signal extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct quad_counter_signal_ext {
+ const char *name;
+ ssize_t (*read)(struct quad_counter_device *counter,
+ struct quad_counter_signal *signal, void *priv,
+ char *buf);
+ ssize_t (*write)(struct quad_counter_device *counter,
+ struct quad_counter_signal *signal, void *priv,
+ const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct quad_counter_signal - Quadrature Counter Signal node
+ * @id: [DRIVER] unique ID used to identify signal
+ * @name: [DRIVER] device-specific signal name
+ * @ext: [DRIVER] optional array of Quadrature Counter Signal extensions
+ * @num_ext: [DRIVER] number of Quadrature Counter Signal extensions
+ * specified in @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct quad_counter_signal {
+ int id;
+ const char *name;
+
+ const struct quad_counter_signal_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+enum quad_counter_signal_level {
+ QUAD_COUNTER_SIGNAL_LOW = 0,
+ QUAD_COUNTER_SIGNAL_HIGH
+};
+
+struct quad_counter_count;
+
+enum quad_counter_function {
+ QUAD_COUNTER_FUNCTION_PULSE_DIRECTION = 0,
+ QUAD_COUNTER_FUNCTION_QUADRATURE_X1,
+ QUAD_COUNTER_FUNCTION_QUADRATURE_X2,
+ QUAD_COUNTER_FUNCTION_QUADRATURE_X4
+};
+
+enum quad_counter_direction {
+ QUAD_COUNTER_DIRECTION_FORWARD = 0,
+ QUAD_COUNTER_DIRECTION_BACKWARD
+};
+
+/**
+ * struct quad_counter_count_ext - Quadrature Counter Count extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct quad_counter_count_ext {
+ const char *name;
+ ssize_t (*read)(struct quad_counter_device *counter,
+ struct quad_counter_count *count, void *priv,
+ char *buf);
+ ssize_t (*write)(struct quad_counter_device *counter,
+ struct quad_counter_count *count, void *priv,
+ const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct quad_counter_count - Quadrature Counter Count node
+ * @id: [DRIVER] unique ID used to identify Count
+ * @name: [DRIVER] device-specific Count name
+ * @function: [DRIVER] current function mode
+ * @direction: [DRIVER] current direction state
+ * @signal_a: [DRIVER] associated quadrature A signal
+ * @signal_b: [DRIVER] associated quadrature B signal
+ * @ext: [DRIVER] optional array of Quadrature Counter Count extensions
+ * @num_ext: [DRIVER] number of Quadrature Counter Count extensions specified
+ * in @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct quad_counter_count {
+ int id;
+ const char *name;
+ enum quad_counter_function function;
+ enum quad_counter_direction direction;
+
+ struct quad_counter_signal signal_a;
+ struct quad_counter_signal signal_b;
+
+ const struct quad_counter_count_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+/**
+ * struct quad_counter_device_ext - Quadrature Counter device extension
+ * @name: [DRIVER] attribute name
+ * @read: [DRIVER] read callback for this attribute; may be NULL
+ * @write: [DRIVER] write callback for this attribute; may be NULL
+ * @priv: [DRIVER] data private to the driver
+ */
+struct quad_counter_device_ext {
+ const char *name;
+ ssize_t (*read)(struct quad_counter_device *counter, void *priv,
+ char *buf);
+ ssize_t (*write)(struct quad_counter_device *counter,
+ void *priv, const char *buf, size_t len);
+ void *priv;
+};
+
+/**
+ * struct quad_counter_device - Quadrature Counter data structure
+ * @name: [DRIVER] name of the device
+ * @parent: [DRIVER] optional parent device providing the counters
+ * @counter_dev: [INTERN] internal Counter container
+ * @signal_read: [DRIVER] read callback for Signal attribute; may be
+ * NULL. Returns 0 on success and negative error code on
+ * error. The respective Signal's returned level should be
+ * passed back via the level parameter.
+ * @count_read: [DRIVER] read callback for Count attribute; may be NULL.
+ * Returns 0 on success and negative error code on error.
+ * The respective Count's returned value should be passed
+ * back via the val parameter.
+ * @count_write: [DRIVER] write callback for Count attribute; may be NULL
+ * @function_get: [DRIVER] function to get the current count function
+ * mode. Returns 0 on success and negative error code on
+ * error. The respective Count's returned function mode
+ * should be passed back via the function parameter.
+ * @function_set: [DRIVER] function to set the count function mode
+ * @direction_get: [DRIVER] function to get the current direction. Returns
+ * 0 on success and negative error code on error. The
+ * respective Count's returned direction should be passed
+ * back via the direction parameter.
+ * @counts: [DRIVER] array of Quadrature Counter Counts
+ * @num_counts: [DRIVER] number of Quadrature Counter Counts specified
+ * in @counts
+ * @ext: [DRIVER] optional array of Quadrature Counter device
+ * extensions
+ * @num_ext: [DRIVER] number of Quadrature Counter device extensions
+ * specified in @ext
+ * @priv: [DRIVER] optional private data supplied by driver
+ */
+struct quad_counter_device {
+ const char *name;
+ struct device *parent;
+ struct counter_device *counter_dev;
+
+ int (*signal_read)(struct quad_counter_device *counter,
+ struct quad_counter_signal *signal,
+ enum quad_counter_signal_level *level);
+ int (*count_read)(struct quad_counter_device *counter,
+ struct quad_counter_count *count, long *val);
+ int (*count_write)(struct quad_counter_device *counter,
+ struct quad_counter_count *count, long val);
+ int (*function_get)(struct quad_counter_device *counter,
+ struct quad_counter_count *count,
+ enum quad_counter_function *function);
+ int (*function_set)(struct quad_counter_device *counter,
+ struct quad_counter_count *count,
+ enum quad_counter_function function);
+ int (*direction_get)(struct quad_counter_device *counter,
+ struct quad_counter_count *count,
+ enum quad_counter_direction *direction);
+
+ struct quad_counter_count *counts;
+ size_t num_counts;
+
+ const struct quad_counter_device_ext *ext;
+ size_t num_ext;
+
+ void *priv;
+};
+
+extern int quad_counter_register(struct quad_counter_device *const counter);
+extern void quad_counter_unregister(struct quad_counter_device *const counter);
+extern int devm_quad_counter_register(struct device *dev,
+ struct quad_counter_device *const counter);
+extern void devm_quad_counter_unregister(struct device *dev,
+ struct quad_counter_device *const counter);
+
#endif /* _COUNTER_H_ */
--
2.15.1
This patch adds standard documentation for the userspace sysfs
attributes of the Quadrature Counter interface.
Signed-off-by: William Breathitt Gray <[email protected]>
---
.../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
diff --git a/Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs b/Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
new file mode 100644
index 000000000000..69d1a3e4a1a5
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
@@ -0,0 +1,76 @@
+What: /sys/bus/counter/devices/counterX/countY
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count data of Count Y. This is a signed integer value that
+ represents the position. Typically, this is determined by
+ hardware evaluation of the quadrature encoding input signals.
+
+What: /sys/bus/counter/devices/counterX/countY_direction
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count direction of Count Y. Typically, this is determined by
+ hardware evaluation of the quadrature encoding input signals.
+ Two count directions are available: forward and backward.
+
+What: /sys/bus/counter/devices/counterX/countY_function
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Count function mode of Count Y. Count function evaluation is
+ triggered by conditions specified by the countY_signalZ_action
+ attributes. Four count function modes are available:
+ pulse-direction, quadrature x1, quadrature x2, quadrature x4.
+
+ Pulse-Direction:
+ Rising edges on quadrature pair signal A updates the
+ respective count. The input level of quadrature pair
+ signal B determines direction.
+
+ Quadrature x1:
+ If direction is forward, rising edges on quadrature pair
+ signal A updates the respective count; if the direction
+ is backward, falling edges on quadrature pair signal A
+ updates the respective count. Quadrature encoding
+ determines the direction.
+
+ Quadrature x2:
+ Any state transition on quadrature pair signal A updates
+ the respective count. Quadrature encoding determines the
+ direction.
+
+ Quadrature x4:
+ Any state transition on either quadrature pair signals
+ updates the respective count. Quadrature encoding
+ determines the direction.
+
+What: /sys/bus/counter/devices/counterX/countY_signalZ_action
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Action mode of Count Y for Signal Z. This attribute indicates
+ the condition of Signal Z that triggers the count function
+ evaluation for Count Y. Four action modes are available: none,
+ rising edge, falling edge, and both edges.
+
+ None:
+ Signal does not trigger the count function. In
+ Pulse-Direction count function mode, this Signal is
+ evaluated as Direction.
+
+ Rising Edge:
+ Low state transitions to High state.
+
+ Falling Edge:
+ High state transitions to Low state.
+
+ Both Edges:
+ Any state transition.
+
+What: /sys/bus/counter/devices/counterX/signalY
+KernelVersion: 4.16
+Contact: [email protected]
+Description:
+ Signal data of Signal Y. This is the respective input level
+ represented by two available states: low and high.
--
2.15.1
This patch adds high-level documentation about the Quadrature Counter
interface.
Signed-off-by: William Breathitt Gray <[email protected]>
---
Documentation/driver-api/iio/index.rst | 1 +
Documentation/driver-api/iio/quad-counter.rst | 444 ++++++++++++++++++++++++++
2 files changed, 445 insertions(+)
create mode 100644 Documentation/driver-api/iio/quad-counter.rst
diff --git a/Documentation/driver-api/iio/index.rst b/Documentation/driver-api/iio/index.rst
index 60e17f16cfb8..db1e2d7e7b87 100644
--- a/Documentation/driver-api/iio/index.rst
+++ b/Documentation/driver-api/iio/index.rst
@@ -17,3 +17,4 @@ Contents:
triggered-buffers
generic-counter
simple-counter
+ quad-counter
diff --git a/Documentation/driver-api/iio/quad-counter.rst b/Documentation/driver-api/iio/quad-counter.rst
new file mode 100644
index 000000000000..4800feafd6ba
--- /dev/null
+++ b/Documentation/driver-api/iio/quad-counter.rst
@@ -0,0 +1,444 @@
+============================
+Quadrature Counter Interface
+============================
+
+Introduction
+============
+
+A common use for counter devices is position tracking. There are several
+protocols for the communication of position changes, but one of the most
+popular is the quadrature encoding system utilized commonly by rotary
+encoder and linear encoder devices.
+
+A quadrature counter device is a counter device that tracks positions
+based on pairs of quadrature encoder inputs. A Quadrature Counter device
+may be expressed as consisting of single Counts each associated with a
+pair of Signals via respective Synapses.
+
+This driver API provides a basic Counter interface and standard of
+interaction and exposure for these quadrature counter devices. The
+Quadrature Counter interface enables drivers to support and expose
+quadrature counter devices in a more apt and well-defined way, without
+the hassles and imprecisions of utilizing a more generic interface.
+
+Theory
+======
+
+The Quadrature Counter interface may be considered a subclass of the
+Generic Counter interface; the same paradigm core components apply:
+Counts, Signals, and Synapses. However, the Quadrature Counter interface
+goes a bit further and defines aspects of those core components to
+properly represent Quadrature Counter devices.
+
+The three core components of a Quadrature Counter:
+
+ COUNT
+ -----
+ A Count represents the count data for a set of Signals. The
+ count data for a Quadrature Counter is a signed integer
+ representing the position.
+
+ A Count has a count function mode which represents the update
+ behavior for the count data. The following four count function
+ modes are possible for a Quadrature Count:
+
+ * Pulse-Direction:
+ Rising edges on quadrature pair signal A updates
+ the respective count. The input level of
+ quadrature pair signal B determines direction.
+ * Quadrature x1:
+ If direction is forward, rising edges on
+ quadrature pair signal A updates the respective
+ count; if the direction is backward, falling
+ edges on quadrature pair signal A updates the
+ respective count. Quadrature encoding determines
+ the direction.
+ * Quadrature x2:
+ Any state transition on quadrature pair signal A
+ updates the respective count. Quadrature
+ encoding determines the direction.
+ * Quadrature x4:
+ Any state transition on either quadrature pair
+ signals updates the respective count. Quadrature
+ encoding determines the direction.
+
+ A Quadrature Count has a pair associated Quadrature Signals.
+
+ SIGNAL
+ ------
+ A Signal represents a counter input data; this is the data that
+ is typically analyzed by the counter to determine the count
+ data. A Quadrature Signal represents a quadrature counter input
+ line with two possible states:
+
+ * Low
+ * High
+
+ A Quadrature Signal is associated to a Quadrature Count.
+
+ SYNAPSE
+ -------
+ A Synapse represents the association of a Signal with a
+ respective Count. Signal data affects respective Count data, and
+ the Synapse represents this relationship.
+
+ The Synapse action mode specifies the Signal data condition
+ which triggers the respective Count's count function evaluation
+ to update the count data. There are four possible action modes
+ for a Quadrature Counter:
+
+ * None:
+ Signal does not trigger the count function. In
+ Pulse-Direction count function mode, this Signal
+ is evaluated as Direction.
+ * Rising Edge:
+ Low state transitions to High state.
+ * Falling Edge:
+ High state transitions to Low state.
+ * Both Edges:
+ Any state transition.
+
+Paradigm
+========
+
+Quadrature Counter devices consist of a single Count associated with a
+pair of Signals via respective Synapses. Take for example a quadrature
+counter device which tracks position via the evaluation of a pair of
+quadrature encoder lines.
+
+ Count Synapse 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 Count: 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 Count, Signals, and Synapses simply represent this
+hardware behavior and functionality.
+
+In addition, a quadrature encoder counter device operating in a
+non-quadrature Pulse-Direction mode could have one input line dedicated
+for movement and a second input line dedicated for direction.
+
+ Count Synapse Signal
+ ----- ------- ------
++---------------------------+
+| Data: Position | Rising Edge ___
+| Function: Pulse-Direction | <------------- / A \ (Movement)
+| | _______
+| |
+| | None ___
+| | <------------- / B \ (Direction)
+| | _______
++---------------------------+
+
+Only Signal A triggers the "Pulse-Direction" update function, but the
+instantaneous state of Signal B is still required in order to know the
+direction so that the position data may be properly updated. Ultimately,
+both Signals are associated to the same Count via two respective
+Synapses, but only one Synapse has an active action mode condition which
+triggers the respective count function while the other is left with a
+"None" condition action mode to indicate its respective Signal's
+availability for state evaluation despite its non-triggering mode.
+
+Userspace Interface
+===================
+
+Several sysfs attributes are generated by the Quadrature Counter
+interface, and reside under the /sys/bus/counter/devices/counterX
+directory, where counterX refers to the respective counter device.
+Please see Documentation/ABI/testing/sys-bus-counter-quadrature-sysfs
+for detailed information on each Quadrature Counter interface sysfs
+attribute.
+
+In addition, several sysfs attributes are generated by the underlying
+Generic Counter interface, and also reside under the
+/sys/bus/counter/devices/counterX directory, where counterX refers to
+the respective counter device. Please see
+Documentation/ABI/testing/sys-bus-counter-generic-sysfs for detailed
+information on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Quadrature Counter paradigm Counts, Signals, and Synapses of
+respective counter devices.
+
+Driver API
+==========
+
+Driver authors may utilize the Quadrature 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 quadrature counter.
+
+
+struct quad_counter_signal_ext
+------------------------------
+
+This structure defines a Quadrature Counter Signal extension attribute.
+These attributes expose auxiliary configuration and functionality
+specific to the respective Quadrature Counter Signal.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct quad_counter_signal
+--------------------------
+
+This structure defines a Quadrature Counter Signal component. Typically,
+this will correlate with a quadrature encoding line input channel on a
+physical counter device. This structure is the simplest of the
+Quadrature Counter paradigm core components to define with only two
+structure members which require explicit configuration:
+
+ id: unique ID used to identify signal
+ name: device-specific signal name
+ ext: optional array of Quadrature Counter Signal
+ extensions
+ num_ext: number of Quadrature Counter Signal extensions
+ specified in @ext
+ priv: optional private data supplied by driver
+
+enum quad_counter_signal_level
+------------------------------
+
+This enum defines enumeration constants to represent the possible
+Quadrature Signal data level states.
+
+ QUAD_COUNTER_SIGNAL_LOW: Low
+ QUAD_COUNTER_SIGNAL_HIGH: High
+
+enum quad_counter_function
+--------------------------
+
+This enum defines enumeration constants to represent the possible
+Quadrature Counter count function modes.
+
+ QUAD_COUNTER_FUNCTION_PULSE_DIRECTION: Pulse-Direction
+ QUAD_COUNTER_FUNCTION_QUADRATURE_X1: Quadrature x1
+ QUAD_COUNTER_FUNCTION_QUADRATURE_X2: Quadrature x2
+ QUAD_COUNTER_FUNCTION_QUADRATURE_X4: Quadrature x4
+
+enum quad_counter_direction
+--------------------------
+
+This enum defines enumeration constants to represent the possible
+Quadrature Counter directions.
+
+ QUAD_COUNTER_DIRECTION_FORWARD: Forward
+ QUAD_COUNTER_DIRECTION_BACKWARD: Backward
+
+struct quad_counter_count_ext
+-------------------------------
+
+This structure defines a Quadrature Counter Count extension attribute.
+These attributes expose auxiliary configuration and functionality
+specific to the respective Quadrature Counter Count.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct quad_counter_count
+-------------------------
+
+This structure defines a Quadrature Counter Count component. Typically,
+this will correlate with the read data (the "position" value) provided
+by a physical counter device. This structure requires the explicit
+configuration of an ID, name, and the pair of Quadrature Signals
+associated with this Quadrature Count.
+
+ id: unique ID used to identify Count
+ name: device-specific Count name
+ function: current function mode
+ direction: current direction state
+ signal_a: associated quadrature A signal
+ signal_b: associated quadrature B signal
+ ext: optional array of Quadrature Counter Count
+ extensions
+ num_ext: number of Quadrature Counter Count extensions
+ specified in @ext
+ priv: optional private data supplied by driver
+
+struct quad_counter_device_ext
+------------------------------
+
+This structure defines a Quadrature Counter extension attribute. These
+attributes expose auxiliary configuration and functionality specific to
+the respective Quadrature Counter.
+
+ name: attribute name
+ read: read callback for this attribute; may be NULL
+ write: write callback for this attribute; may be NULL
+ priv: data private to the driver
+
+struct quad_counter_device
+--------------------------
+
+This is the main data structure for a Quadrature Counter device. Access
+to all respective Counts, Signals, and Synapses is possible from this
+structure. This structure requires the configuration of a name and
+Quadrature Counter Counts:
+
+ name: name of the device
+ parent: optional parent device providing the counters
+ signal_read: read callback for Signal attribute; may be NULL.
+ Returns 0 on success and negative error code on
+ error. The respective Signal's returned level
+ should be passed back via the level parameter.
+ count_read: read callback for Count attribute; may be NULL.
+ Returns 0 on success and negative error code on
+ error. The respective Count's returned value
+ should be passed back via the val parameter.
+ count_write: write callback for Count attribute; may be NULL
+ function_get: function to get the current count function mode.
+ Returns 0 on success and negative error code on
+ error. The respective Count's returned function
+ mode should be passed back via the function
+ parameter.
+ function_set: function to set the count function mode
+ direction_get: function to get the current direction. Returns 0
+ on success and negative error code on error. The
+ respective Count's returned direction should be
+ passed back via the direction parameter.
+ counts: array of Quadrature Counter Counts
+ num_counts: number of Quadrature Counter Counts specified in
+ @counts
+ ext: optional array of Quadrature Counter device
+ extensions
+ num_ext: number of Quadrature Counter device extensions
+ specified in @ext
+ priv: optional private data supplied by driver
+
+Registration functions
+----------------------
+
+A quadrature counter device is registered to the system by passing the
+respective initialized quad_counter_device structure to the
+quad_counter_register function; similarly, the quad_counter_unregister
+function unregisters the respective Quadrature Counter. The
+devm_quad_counter_register and devm_quad_counter_unregister functions
+serve as device memory-managed versions of the quad_counter_register and
+quad_counter_unregister functions respectively.
+
+Implementation
+==============
+
+To use the Quadrature Counter interface, create an array of
+quad_counter_count structures to represent the desired Counts and
+Signals of the quadrature counter device; the signal_a member of a
+quad_counter_count structure should define the Channel A signal of the
+respective quadrature encoding pair, and similarly the signal_b member
+should define Channel B. The defined quad_counter_count array may then
+be added to a quad_counter_device structure for registration to the
+system.
+
+ Quad Count Count Signal
+ ---------- ----- ------
++-------------------------+ +-------------------------+
+| Data: Position | -> | Data: Position |
+| Function: Quadrature x4 | | Function: Quadrature x4 |
+| | +-------------------------+
+| |
++-------------------------+ ________
+| Signal: Quad A | ----------------------------> / Quad A \
+| | ____________
+| |
++-------------------------+ ________
+| Signal: Quad B | ----------------------------> / Quad B \
+| | ____________
++-------------------------+
+
+Driver callbacks should be provided to the quad_counter_device structure
+in order to communicate with the device: to read and write various
+Counts, to read various Signals, to get and set the "function mode" for
+various Counts, and to get the direction for various Counts.
+
+A defined quad_counter_device structure may be registered to the system
+by passing it to the quad_counter_register function, and unregistered by
+passing it to the quad_counter_unregister function. Similarly, the
+devm_quad_counter_register and devm_quad_counter_unregister functions
+may be used if device memory-managed registration is desired.
+
+Extension sysfs attributes can be created for auxiliary functionality
+and data by passing in defined quad_counter_device_ext,
+quad_counter_count_ext, and quad_counter_signal_ext structures. In
+these cases, the quad_counter_device_ext structure is used for global
+configuration of the respective Counter device, while the
+quad_counter_count_ext and quad_counter_signal_ext structures allow
+for auxiliary exposure and configuration of a specific Count or Signal
+respectively.
+
+Architecture
+============
+
+The Quadrature Counter interface is a subclass of the Generic Counter
+interface, and the Quadrature Counter interface functions serve as
+mappings onto the Generic Counter interface functions to simplify and
+aptly define support for quadrature counter devices.
+
+In this vein, the Generic Counter interface functions are ultimately
+called when the Quadrature Counter interface functions are utilized. For
+more information on the architecture of the Generic Counter interface,
+please refer to the Documentation/driver-api/iio/generic-counter.rst
+file.
+
+Quadrature Counter devices are registered to the system via the
+quad_counter_register function, and later removed via the
+quad_counter_unregister function. The quad_counter_register function
+will allocate a counter_device structure, populate it with the required
+Generic Counter structures and data required to represent the Quadrature
+Counter components, and register it to the system via a counter_register
+call.
+
+ _____________________ +-----------------------+
+ / quad_counter_device \ --> | quad_counter_register |
+_________________________ +-----------------------+
+ |
+ +------------------------------+
+ |
+ V
+ ________________ +------------------+
+ / counter_device \ --> | counter_register |
+____________________ +------------------+
+
+The signal_read driver callback, the count_read/count_write and
+function_get/function_set driver callbacks, and the direction_get driver
+callbacks are mapped to the allocated internal counter_device structure
+via appropriate hook functions.
+
+ Quad Counter Remap Function Generic Counter
+ ------------ -------------- ---------------
+
+ signal_read quad_counter_signal_read signal_read
+ count_read quad_counter_count_read count_read
+ count_write quad_counter_count_write count_write
+ function_get quad_counter_function_get function_get
+ function_set quad_counter_function_set function_set
+ direction_get quad_counter_action_get action_get
+
+This is how Generic Counter interface sysfs attributes are inherited and
+extended by the Quadrature Counter interface. If a driver callback is
+left undefined, then the respective read/write permission is left
+disabled for the relevant attributes. The quad_counter_action_get
+function in particular depends on both direction_get and function_get --
+so if either driver callback is undefined, the internal counter_device
+action_get function pointer is left as NULL.
+
+Similarly, quad_counter_device_ext, quad_counter_count_ext, and
+quad_counter_signal_ext structures are mapped to respective
+counter_device_ext, counter_count_ext, and counter_signal_ext structures
+via appropriate hook functions, and then added to the allocated internal
+counter_device structure for registration.
--
2.15.1
This patch adds support for the Quadrature 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.
Quadrature Counter Counts are created for the eight quadrature channel
counts, and their respective quadrature A and B signals are associated
via the respective quad_counter_count structure.
The new Quadrature Counter interface sysfs attributes are intended to
expose the same functionality and data available via the existing
104-QUAD-8 IIO device interface; the Quadrature Counter interface serves
to provide the respective functionality and data in a standard way
expected of quadrature counter devices.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/counter/104-quad-8.c | 257 +++++++++++++++++++++++++++++++++++++--
1 file changed, 247 insertions(+), 10 deletions(-)
diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c
index b56985078d8c..3a82503525f5 100644
--- a/drivers/iio/counter/104-quad-8.c
+++ b/drivers/iio/counter/104-quad-8.c
@@ -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 quad_counter_device
* @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 quad_counter_device counter;
unsigned int preset[QUAD8_NUM_COUNTERS];
unsigned int count_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
@@ -527,24 +531,233 @@ static const struct iio_chan_spec quad8_channels[] = {
QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
};
+static int quad8_signal_read(struct quad_counter_device *counter,
+ struct quad_counter_signal *signal,
+ enum quad_counter_signal_level *level)
+{
+ const struct quad8_iio *const priv = counter->priv;
+ unsigned int state;
+
+ /* Only Index signal levels can be read */
+ if (signal->id < 16)
+ return -EINVAL;
+
+ state = inb(priv->base + 0x16) & BIT(signal->id - 16);
+
+ *level = (state) ? QUAD_COUNTER_SIGNAL_HIGH : QUAD_COUNTER_SIGNAL_LOW;
+
+ return 0;
+}
+
+static int quad8_count_read(struct quad_counter_device *counter,
+ struct quad_counter_count *count, long *val)
+{
+ const struct quad8_iio *const priv = counter->priv;
+ const int base_offset = priv->base + 2 * count->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 |= (long)inb(base_offset) << (8 * i);
+
+ return 0;
+}
+
+static int quad8_count_write(struct quad_counter_device *counter,
+ struct quad_counter_count *count, long val)
+{
+ const struct quad8_iio *const priv = counter->priv;
+ const int base_offset = priv->base + 2 * count->id;
+ int i;
+
+ /* Only 24-bit values are supported */
+ if (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[count->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_function_get(struct quad_counter_device *counter,
+ struct quad_counter_count *count,
+ enum quad_counter_function *function)
+{
+ const struct quad8_iio *const priv = counter->priv;
+ const int id = count->id;
+ const unsigned int quadrature_mode = priv->quadrature_mode[id];
+ const unsigned int scale = priv->quadrature_scale[id];
+
+ if (quadrature_mode)
+ switch (scale) {
+ case 0:
+ *function = QUAD_COUNTER_FUNCTION_QUADRATURE_X1;
+ break;
+ case 1:
+ *function = QUAD_COUNTER_FUNCTION_QUADRATURE_X2;
+ break;
+ case 2:
+ *function = QUAD_COUNTER_FUNCTION_QUADRATURE_X4;
+ break;
+ }
+ else
+ *function = QUAD_COUNTER_FUNCTION_PULSE_DIRECTION;
+
+ return 0;
+}
+
+static int quad8_function_set(struct quad_counter_device *counter,
+ struct quad_counter_count *count, enum quad_counter_function function)
+{
+ struct quad8_iio *const priv = counter->priv;
+ const int id = count->id;
+ unsigned int *const quadrature_mode = priv->quadrature_mode + id;
+ unsigned int *const scale = priv->quadrature_scale + id;
+ unsigned int mode_cfg = priv->count_mode[id] << 1;
+ unsigned int *const synchronous_mode = priv->synchronous_mode + id;
+ const unsigned int idr_cfg = priv->index_polarity[id] << 1;
+ const int base_offset = priv->base + 2 * id + 1;
+
+ if (function == QUAD_COUNTER_FUNCTION_PULSE_DIRECTION) {
+ *quadrature_mode = 0;
+
+ /* Quadrature scaling only available in quadrature mode */
+ *scale = 0;
+
+ /* Synchronous function not supported in non-quadrature mode */
+ if (*synchronous_mode) {
+ *synchronous_mode = 0;
+ /* Disable synchronous function mode */
+ outb(0x60 | idr_cfg, base_offset);
+ }
+ } else {
+ *quadrature_mode = 1;
+
+ switch (function) {
+ case QUAD_COUNTER_FUNCTION_QUADRATURE_X1:
+ *scale = 0;
+ mode_cfg |= 0x8;
+ break;
+ case QUAD_COUNTER_FUNCTION_QUADRATURE_X2:
+ *scale = 1;
+ mode_cfg |= 0x10;
+ break;
+ case QUAD_COUNTER_FUNCTION_QUADRATURE_X4:
+ *scale = 2;
+ mode_cfg |= 0x18;
+ break;
+ }
+ }
+
+ /* Load mode configuration to Counter Mode Register */
+ outb(0x20 | mode_cfg, base_offset);
+
+ return 0;
+}
+
+static int quad8_direction_get(struct quad_counter_device *counter,
+ struct quad_counter_count *count,
+ enum quad_counter_direction *direction)
+{
+ const struct quad8_iio *const priv = counter->priv;
+ unsigned int ud_flag;
+ const unsigned int flag_addr = priv->base + 2 * count->id + 1;
+
+ /* U/D flag: nonzero = up, zero = down */
+ ud_flag = inb(flag_addr) & BIT(5);
+
+ *direction = (ud_flag) ? QUAD_COUNTER_DIRECTION_FORWARD :
+ QUAD_COUNTER_DIRECTION_BACKWARD;
+
+ return 0;
+}
+
+#define QUAD8_COUNT(_id, _cntname, _siganame, _sigbname) { \
+ .id = _id, \
+ .name = _cntname, \
+ .signal_a = { \
+ .id = 2 * _id, \
+ .name = _siganame \
+ }, \
+ .signal_b = { \
+ .id = 2 * _id + 1, \
+ .name = _sigbname \
+ } \
+}
+
+static const struct quad_counter_count quad8_counts[] = {
+ QUAD8_COUNT(0, "Channel 1 Count", "Channel 1 Quadrature A",
+ "Channel 1 Quadrature B"),
+ QUAD8_COUNT(1, "Channel 2 Count", "Channel 2 Quadrature A",
+ "Channel 2 Quadrature B"),
+ QUAD8_COUNT(2, "Channel 3 Count", "Channel 3 Quadrature A",
+ "Channel 3 Quadrature B"),
+ QUAD8_COUNT(3, "Channel 4 Count", "Channel 4 Quadrature A",
+ "Channel 4 Quadrature B"),
+ QUAD8_COUNT(4, "Channel 5 Count", "Channel 5 Quadrature A",
+ "Channel 5 Quadrature B"),
+ QUAD8_COUNT(5, "Channel 6 Count", "Channel 6 Quadrature A",
+ "Channel 6 Quadrature B"),
+ QUAD8_COUNT(6, "Channel 7 Count", "Channel 7 Quadrature A",
+ "Channel 7 Quadrature B"),
+ QUAD8_COUNT(7, "Channel 8 Count", "Channel 8 Quadrature A",
+ "Channel 8 Quadrature B")
+};
+
static int quad8_probe(struct device *dev, unsigned int id)
{
struct iio_dev *indio_dev;
- struct quad8_iio *priv;
+ struct quad_counter_count *counts;
+ struct quad8_iio *quad8iio;
int i, j;
unsigned int base_offset;
+ int err;
- 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;
}
+ /* Allocate IIO device; this also allocates driver data structure */
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*quad8iio));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ /* Initialize IIO device */
indio_dev->info = &quad8_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
@@ -552,8 +765,26 @@ static int quad8_probe(struct device *dev, unsigned int id)
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
- priv = iio_priv(indio_dev);
- priv->base = base[id];
+ /* Instantiate Quadrature Counter Counts */
+ counts = devm_kmemdup(dev, quad8_counts, sizeof(quad8_counts),
+ GFP_KERNEL);
+ if (!counts)
+ return -ENOMEM;
+
+ /* Initialize Quadrature Counter device and driver data */
+ quad8iio = iio_priv(indio_dev);
+ quad8iio->counter.name = dev_name(dev);
+ quad8iio->counter.parent = dev;
+ quad8iio->counter.signal_read = quad8_signal_read;
+ quad8iio->counter.count_read = quad8_count_read;
+ quad8iio->counter.count_write = quad8_count_write;
+ quad8iio->counter.function_get = quad8_function_get;
+ quad8iio->counter.function_set = quad8_function_set;
+ quad8iio->counter.direction_get = quad8_direction_get;
+ quad8iio->counter.counts = counts;
+ quad8iio->counter.num_counts = ARRAY_SIZE(quad8_counts);
+ quad8iio->counter.priv = quad8iio;
+ quad8iio->base = base[id];
/* Reset all counters and disable interrupt function */
outb(0x01, base[id] + 0x11);
@@ -579,7 +810,13 @@ 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);
+ /* Register IIO device */
+ err = devm_iio_device_register(dev, indio_dev);
+ if (err)
+ return err;
+
+ /* Register Quadrature Counter device */
+ return devm_quad_counter_register(dev, &quad8iio->counter);
}
static struct isa_driver quad8_driver = {
--
2.15.1
On Thu, 14 Dec 2017 15:50:29 -0500
William Breathitt Gray <[email protected]> wrote:
> Introduction
> ============
>
> Apologies for going silent these past couple months just to return with
> a patchset over 3000 lines larger than the last -- I should have been
> releasing intermediate versions along the way so shame on me!
:) Sometimes it's better to wait until you are moderately happy with it
yourself!
> The
> Counter system has effectively been rewritten anew, so I believe very
> little of the code in the previous versions of this patchset remain.
> However, the Generic Counter paradigm has pretty much remained the same
> so the theory should be familar. Regardless, I realize I'm dropping off
> this patchset near the winter holidays so I don't expect a review until
> well into January -- I'm just releasing this now before I myself head
> off on an end of year sabbatical.
It's at least a few hours into January so here goes before life gets
properly busy again.
>
> The most significant difference between this version and the previous,
> as well as part of the reason for the implementation code changes, is
> the complete separation of the Generic Counter system from IIO. I
> decided it was improper to build the Generic Counter system on top of
> IIO core: it was leading to ugly code, convulted hacks, and forced
> restrictions on the Generic Counter interface in order to appease the
> architecture of the IIO system. Most importantly, the IIO core code that
> was leveraged by the Generic Counter was so minor (essentially just the
> sysfs attribute support) that it did not justify the extensive
> hoop-jumping performed to make the code work.
>
> So this patchset introduces the Generic Counter interface without the
> dependence on IIO code. This now gives the Generic Counter system the
> freedom to aptly represent counter devices without implementation
> compatibility concerns regarding other high-level subsystems.
>
> This also makes sense ontologically I believe because whereas the IIO
> system appears more focused on representing the industrial I/O of a
> device and their configuration directly, the Generic Counter system is
> more concerned with the abstract representation of that counter device
> and the relationships and configurations within which define its
> operation at a high-level; a counter driver could in theory separately
> support both the high-level Generic Counter representation of the device
> as a whole (what are we counting conceptually, how much are we counting,
> etc.), as well as the low-level IIO representation of the individual
> inputs and outputs on that device (are the signals differential, do
> certain signals have current requirements, etc.).
I think there are concepts that over time may blur the lines more
but agree with the basic point. I'm just planning to nick all your
good ideas if they will improve IIO in turn.
>
> Overview
> ========
>
> This patchset may be divided into three main groups:
>
> * Generic Counter
> * Simple Counter
> * Quadrature Counter
>
> Each group begins with a patch introducing the implementation of the
> interface system, followed afterwards by documentation patches. I
> recommend reading through the documentation patches first to familiarize
> your with the interface itself before jumping into the source code for
> the implementation.
>
> The Simple Counter and Quadrature Counter groups also have example
> driver code in the dummy-counter and 104-quad-8 patches respectively.
> The Simple Counter and Quadrature Counter systems themselves being
> subclasses of the Generic Counter may serve as example driver code for
> the Generic Counter interface -- though I may end up adding an explicit
> Generic Counter example in a later patch to the dummy-counter for easier
> reference.
>
> Since the Generic Counter system no longer depends on IIO, I moved all
> Counter related source code to the drivers/iio/counter/ directory to
> keep everything contained together. In addition, with the IIO Kconfig
> dependency removed, the COUNTER menu appear now appears at the same
> level as the IIO menu:
>
> -> Device drivers
> -> Counter Support (COUNTER [=m])
>
> I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
> order to match the Kconfig heirarchy or to keep it where it is to match
> the legacy IIO counter location established when we first added the
> 104-QUAD-8 driver.
I would move it out entirely - otherwise things are just confusing.
You 'could' sit it in IIO (as in put it under the top level menu option)
if you would prefer but I don't thing that really makes sense.
>
> Paradigm updates
> ================
>
> The Generic Counter paradigm has essentially remained the same from the
> previous patch, but I have made some minor updates. In particular, I've
> finally settled on a naming convention for the core components of a
> Counter:
>
> COUNT
> -----
> A Count represents the count data for a set of Signals. A Count
> has a count function mode (e.g. "increase" or "quadrature x4")
> which represents the update behavior for the count data. A Count
> also has a set of one or more associated Signals.
>
> This component was called "Value" in the previous patches. I believe
> "Count" is a more direct name for this data, and it also matches how
> datasheets and people commonly refer to this information in
> documentation.
Agreed - better name.
>
> SIGNAL
> ------
> A Signal represents a counter input data; this is the data that
> is typically analyzed by the counter to determine the count
> data. A Signal may be associated to one or more Counts.
>
> The naming for this component has not changed since the previous
> patches. I believe "Signal" is a fitting enough name for the input
> data, as well as matching the common nomenclature for existing counter
> devices.
>
> SYNAPSE
> -------
> A Synapse represents the association of a Signal with a
> respective Count. Signal data affects respective Count data, and
> the Synapse represents this relationship. The Synapse action
> mode (e.g. "rising edge" or "double pulse") specifies the Signal
> data condition which triggers the respective Count's count
> function evaluation to update the count data. It is possible for
> the Synapse action mode to be "none" if a Signal is associated
> with a Count but does not trigger the count function (e.g. the
> direction signal line for a Pulse-Direction encoding counter).
>
> This component was called "Trigger" in the previous patches. I do not
> believe "Trigger" was a good name for two main reasons: it could easily
> be confused for the existing IIO trigger concept, and most importantly
> it does not convey the connection association aspect of the
> Count-Signal relationship.
An alternative here would be to use MAP as a number of similar
'connection' type arrangements in the kernel do. It doesn't really
imply the 'how' element though so perhaps a new term is indeed better.
>
> I settled on the "Synapse" name both due to etymology -- from Greek
> _sunapsis_ meaning "conjunction" -- as well as a biological analogy:
> just as neurons connect and fire communication over synapses, so does a
> Counter Signal connect and fire communication to a Counter Count over a
> Counter Synapse.
>
> Following the same naming convention and analogy, what was previously
> called trigger_mode is now known as action_mode, named in reference to
> action potential -- the condition in a neuron which triggers a fire
> communication over a synapse, just as a Counter Signal condition
> specified in the action_mode of a Counter Synapse triggers the count
> function evaluation for a Counter Count.
>
> Counter classes descriptions
> ============================
>
> The Generic Counter interface is the most general interface for
> supporting counter devices; if it qualifies as a Counter, then it can be
> represented by the Generic Counter interface. Unfortunately, the
> flexibility of the interface does result in a more cumbersome
> integration for driver authors: much of the components must be manually
> configured by the author, which can be a tedious task for large and
> complex counter devices.
>
> To this end, two subclasses of the Generic Counter interface as
> introduced in this patchset: the Simple Counter interface, and the
> Quadrature Counter interface. Both of these interfaces inherit the
> Generic Counter paradigm, and may be seen as extensions to the interface
> which restrict the components to a respective specific class of counter
> devices in order to provide a more apt interface for such devices.
>
> Simple Counter
> --------------
> Simple Counters are devices that count edge pulses on an input
> line (e.g. tally counters).
>
> Since the relationship between Signals and Counts is known to be
> one-to-one, a simple_counter_count structure already contains
> the associated Signal member for the respective Count. A driver
> author no longer needs to worry about allocating a separate
> Signal and Synapse, nor about configuring the association
> between the respective Count and Signal; the Simple Counter
> interface abstracts away such details.
>
> Furthermore, since the device type is known, component
> properties may be further defined and restricted: Count data is
> a signed integer, Signal data "low" and "high" state is set via
> enumeration constants, and so are count function and action mode
> restricted to well-defined "increase"/"decrease" and
> "none"/"rising edge"/"falling edge"/"both edges" enumeration
> constants respectively.
I do wonder a little on whether this is too restrictive to actually
represent many devices.
>
> Quadrature Counter
> ------------------
> Quadrature Counters are devices that track position based on
> quadrature pair signals (e.g. rotary encoder counters).
>
> Since the relationship between Signals and Counts is known to be
> a quadrature pair of Signals to each Count, a quad_counter_count
> structure already contains the associated Signal members for the
> respective Count. A driver author no longer needs to worry about
> allocating separate Signals and Synapses for each quadrature
> pair, nor about configuring the association between the
> respective Count and Signals; the Quadrature Counter interface
> abstracts away such details.
>
> Furthermore, since the device type is known, component
> properties may be further defined and restricted: Count data is
> a signed integer, Signal data "low" and "high" state is set via
> enumeration constants, and so is count function mode restricted
> to well-defined enumeration constants to represent modes such as
> "pulse-direction" and "quadrature x4" for example.
Pulse direction is definitely not a quadrature counter... Maybe this needs
a rename to dual-signal-counter or similar?
Another classic case here would be increment / decrement counters where
a signal is used for each operation (counting items between two light gates
- used a lot in tracking products in the production industry).
>
> Note how driver authors no longer need to interact with Synapses
> directly when utilizing the Simple Counter and Quadrature Counter
> interfaces. This should make it easier too for authors to add support
> since they don't need to fully understand the underlying Counter
> paradigm in order to take advantage of the interfaces -- just define the
> Counts and Signals, and they're ready to go.
>
> Even more so, the Quadrature Counter interface takes it a step further
> and doesn't require action_modes to be explicitly set -- rather they are
> implicitly determined internally by the system based on the direction
> and function mode. Abstractions like these should make the Counter
> interface system as a whole robust enough to handle the diverse classes
> of counter devices out in the real world.
>
> Compilation warnings
> ====================
>
> There are three main compilation warnings which pop for this patchset.
> I've inspected these warnings and none are errors, however they do
> require some explanation.
>
> * 104-quad-8: warning: enumeration value
> ‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
> switch
>
> The first warning is rather simple to explain: the
> QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
> statement's else condition, so an explicit case condition is not
> necessary. I can add a default case line to pacify the compiler, but
> since it would be empty the effort seems frivolous.
Do it anyway and put a comment of /* Should not get here */
Suppressing false warnings is useful from a code maintenance point of view.
> In some sense as
> well, a default case may make the switch logic less clear by implying
> the possibility of additional cases which are not possible in the
> context of that code path.
>
> * simple-counter: warning: assignment discards ‘const’ qualifier
> from pointer target type
> * quad-counter: warning: assignment discards ‘const’ qualifier
> from pointer target type
>
> The second warning comes from the mapping of
> simple_counter_device_ext/quad_counter_device_ext,
> simple_counter_count_ext/quad_counter_count_ext, and
> simple_counter_signal_ext/quad_counter_signal_ext to the internal
> Counter counter_device_ext, counter_count_ext, and counter_signal_ext
> structures respectively.
>
> The priv member of the counter_device_ext, counter_count_ext, or
> counter_signal_ext is leveraged to pass the respective
> simple_counter_device_ext/quad_counter_device_ext,
> simple_counter_count_ext/quad_counter_count_ext, or
> simple_counter_signal_ext/quad_counter_signal_ext structure to their
> respective read/write callback. The priv member is generic on purpose to
> allow any desired data to be passed; the supplied read/write callbacks
> should know the datatype of the passed-in priv argument so they cast it
> appropriately to access their expected data.
>
> As such, the 'const' qualifier of the structures are thus discarded but
> subsequently cast back when the respective registered callback functions
> are called. Since this is the intended use case of the priv member -- to
> generically pass driver data for later recast -- I don't believe this
> warning needs to be rectified.
All warnings need to be rectified. Sorry but this noise will do two things:
1) Get you a patch every few weeks from someone fixing it.
2) Potentially make real warnings harder to see.
Sometimes we have to play games to work around them, but such is life.
>
> * generic-counter: warning: passing argument 5 of
> ‘counter_attribute_create’ discards ‘const’ qualifier
> from pointer target type
> * generic-counter: warning: passing argument 6 of
> ‘counter_attribute_create’ discards ‘const’ qualifier
> from pointer target type
>
> The third warnings comes from a similar situation to the second warning:
> a 'const' argument is passed generically via 'void *' for later recast.
> In this cast, I decided to create a generic function called
> counter_attribute_create in order to simplify the sysfs attribute
> registration code in the generic-counter.c file.
>
> The counter_attribute_create function takes in read and write callbacks,
> as well as two optional generic data arguments to be stored as 'void *'
> (the component and component_data parameters). Using this setup allows
> the counter_attribute_create function to be the sole function necessary
> to create a desired Generic Counter sysfs attribute: read and write
> callbacks are passed along with relevant Counter component and data
> generically, which can be cast back later inside those read and write
> functions to match the expected datatype.
>
> Using a generic counter_attribute_create function reduces duplicate
> code, but it does result in many superfluous compilation warnings. I can
> define new attribute_create functions specific to each type of sysfs
> attribute in order to pacify the warnings, but that seems to be such a
> waste to increase the amount of code with duplications that are
> unnecessary. What would you recommend; should I attempt to pacify these
> warnings or leave them be?
You must fix them I'm afraid.
>
> Known TODO items
> ================
>
> Although I've added the interface documentation files with rst file
> extensions, I still need to familiarize myself with Sphinx markup
> constructs to take advantage of the language. For example, I've copied
> verbatim several structure definitions into the documentation directly,
> but I believe this would be better left dynamically generated by using
> the relevant markup syntax. I'll try to clean up the documentation then
> once I've brushed up on Sphinx.
>
> As noted in a previous patchset version, the signal_write callback
> should be removed from the interface; there are few if any cases where
> it makese sense to have a signal_write callback since Signals are
> always considered inputs in the context of the Counter paradigm.
>
> I've retained the signal_write callback in this version since I'm unsure
> how to implement the dummy-counter Signal source. Benjamin Gaignard
> suggested implementing dummy-counter as a gpio-counter which could use
> gpio to provide a software quadratic counter. Is this the path I should
> take?
It would certainly work well and be simple enough for easy understanding.
Also, it might be a useful driver in it's own right.
>
> Furthermore, the dummy-counter driver defines its own single
> platform_device which restricts it to loading only a single instance.
> I can fix this to allow multiple instances in the next patchset version
> -- as suggested, I'll check out industrialio-sw-device.c for reference.
>
> Right now the dummy-counter driver only has example code for the Simple
> Counter interface. It may be prudent to add example code for the Generic
> Counter and Quadrature Counter interfaces too. I think dummy-counter
> should serve as the reference driver implementation for all the Counter
> interfaces, so that driver authors have an example of how to integrate
> the particular interface they desire.
Such a driver is useful, but it doesn't add much if you have another,
only slightly more complex real driver that also does the job.
Perhaps do them all as gpio based drivers for example?
>
> Finally, I only added very basic support for the Quadrature Counter
> interface in the 104-QUAD-8 driver. It's possible to support all
> existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
> corresponding quad_counter_device_ext, quad_counter_count_ext, and
> quad_counter_signal_ext structures, such that only the
> /sys/bus/counter/devices/counterX/ directory needs to be accessed to
> interact with the 104-QUAD-8 device. I'll try to add support for those
> remaining sysfs attributes in the next patchset version.
>
> If I missed anything from the last patchset version review just remind
> me again and I'll add it to my TODO list. ;)
You are seriously optimistic if you think we can remember!
Jonathan
>
> William Breathitt Gray (11):
> iio: Introduce the Generic Counter interface
> counter: Documentation: Add Generic Counter sysfs documentation
> docs: Add Generic Counter interface documentation
> counter: Introduce the Simple Counter interface
> counter: Documentation: Add Simple Counter sysfs documentation
> docs: Add Simple Counter interface documentation
> counter: Add dummy counter driver
> counter: Introduce the Quadrature Counter interface
> counter: Documentation: Add Quadrature Counter sysfs documentation
> docs: Add Quadrature Counter interface documentation
> counter: 104-quad-8: Add Quadrature Counter interface support
>
> .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
> .../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
> .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
> Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
> Documentation/driver-api/iio/index.rst | 3 +
> Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
> Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
> MAINTAINERS | 9 +
> drivers/iio/Kconfig | 3 +-
> drivers/iio/counter/104-quad-8.c | 257 +++++-
> drivers/iio/counter/Kconfig | 35 +-
> drivers/iio/counter/Makefile | 6 +
> drivers/iio/counter/dummy-counter.c | 308 +++++++
> drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
> drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
> drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
> include/linux/iio/counter.h | 629 +++++++++++++
> 17 files changed, 5216 insertions(+), 15 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
> create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
> create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
> create mode 100644 Documentation/driver-api/iio/generic-counter.rst
> create mode 100644 Documentation/driver-api/iio/quad-counter.rst
> create mode 100644 Documentation/driver-api/iio/simple-counter.rst
> create mode 100644 drivers/iio/counter/dummy-counter.c
> create mode 100644 drivers/iio/counter/generic-counter.c
> create mode 100644 drivers/iio/counter/quad-counter.c
> create mode 100644 drivers/iio/counter/simple-counter.c
> create mode 100644 include/linux/iio/counter.h
>
On Thu, 14 Dec 2017 15:50:58 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch adds standard documentation for the userspace sysfs
> attributes of the Generic Counter interface.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
> ---
> .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++++++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 74 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs b/Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
> new file mode 100644
> index 000000000000..3b1c3c4498d1
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
> @@ -0,0 +1,73 @@
> +What: /sys/bus/counter/devices/counterX/countY
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Count data of Count Y. Typically, this is an accumulated count
> + value.
> +
> +What: /sys/bus/counter/devices/counterX/countY_function
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Count function mode of Count Y; count function evaluation is
> + triggered by conditions specified by the countY_signalZ_action
> + attributes.
List possible values here or in the available. This document should fully
define the interface.
> +
> +What: /sys/bus/counter/devices/counterX/countY_function_available
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Discrete set of available count function modes for the
> + configuration of the respective Count Y are listed in this file.
> +
> +What: /sys/bus/counter/devices/counterX/countY_name
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Read-only attribute that indicates the device-specific name of
> + Count Y.
It's specific to both the device and the count so make that clear.
Also give a description of where it comes from. Who makes this up and how
do they do it?
> +
> +What: /sys/bus/counter/devices/counterX/countY_signalZ_action
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Action mode of Count Y for Signal Z. This attribute indicates
> + the condition of Signal Z that triggers the count function
> + evaluation for Count Y.
> +
> +What: /sys/bus/counter/devices/counterX/countY_signalZ_action_available
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Discrete set of available action modes are listed in this file
> + for the configuration of the respective Synapse associating
> + Signal Z to Count Y.
Again, all options need to be explicitly listed.
> +
> +What: /sys/bus/counter/devices/counterX/counter_name
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Read-only attribute that indicates the device-specific name of
> + the Counter.
How is this decided? (actually the IIO docs are weak on this as well I think
- I should fix that as we frequently end up in a mess over this).
> +
> +What: /sys/bus/counter/devices/counterX/signalY
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Signal data of Signal Y. Typically, this is an input line level
> + state.
I would avoid 'typically'. Just expand this text to cover new options when
they occur.
> +
> +What: /sys/bus/counter/devices/counterX/signalY_name
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Read-only attribute that indicates the device-specific name of
> + Signal Y.
Again, decided how?
> +
> +What: /sys/bus/counter/devices/counterX/countY_synapses
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + List of Synapses associating Signals to Count Y. The columns
> + represent the following in the respective order: Signal ID,
> + Signal name, and current action mode.
This doesn't really conform to the sysfs golden rule of one attribute one
thing.
An alternative I would look into would be to have countY_synapses
directory containing a number of files to indicate this.
Signal name looks redundant to me though as it can be established easily
from the ID (I think). So I would have
countY_synapses/synapse_signalX_action and the action in there.
Not totally sure how this would work out in reality but it would at least
mean software only had to read the one it cared about rather than parse
a table.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 07dd7b933bfa..38da1bc615b3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3665,6 +3665,7 @@ COUNTER INTERFACE
> M: William Breathitt Gray <[email protected]>
> L: [email protected]
> S: Maintained
> +F: Documentation/ABI/testing/sysfs-bus-counter-*
> F: drivers/iio/counter/
> F: include/linux/iio/counter.h
>
On Thu, 14 Dec 2017 15:51:16 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch adds high-level documentation about the Generic Counter
> interface.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
> ---
> Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++++++++++++++++
> Documentation/driver-api/iio/index.rst | 1 +
> MAINTAINERS | 1 +
> 3 files changed, 436 insertions(+)
> create mode 100644 Documentation/driver-api/iio/generic-counter.rst
>
> diff --git a/Documentation/driver-api/iio/generic-counter.rst b/Documentation/driver-api/iio/generic-counter.rst
> new file mode 100644
> index 000000000000..219c571e90ce
> --- /dev/null
> +++ b/Documentation/driver-api/iio/generic-counter.rst
> @@ -0,0 +1,434 @@
> +=========================
> +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 tally counters, 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:
> +
> + COUNT
> + -----
> + A Count represents the count data for a set of Signals. A Count
> + has a count function mode (e.g. "increase" or "quadrature x4")
> + which represents the update behavior for the count data. A Count
> + also has a set of one or more associated Signals.
> +
> + SIGNAL
> + ------
> + A Signal represents a counter input data; this is the data that
> + is typically analyzed by the counter to determine the count
> + data. A Signal may be associated to one or more Counts.
I would give and example - input wire from a rotary encoder (perhaps)
> +
> + SYNAPSE
> + -------
> + A Synapse represents the association of a Signal with a
> + respective Count. Signal data affects respective Count data, and
> + the Synapse represents this relationship. The Synapse action
> + mode (e.g. "rising edge" or "double pulse") specifies the Signal
> + data condition which triggers the respective Count's count
> + function evaluation to update the count data. It is possible for
> + the Synapse action mode to be "none" if a Signal is associated
> + with a Count but does not trigger the count function (e.g. the
> + direction signal line for a Pulse-Direction encoding counter).
> +
> +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 Counts
> +each associated to a set of Signals, whose respective Synapse instances
> +represent the count function update conditions for the associated
> +Counts.
> +
> +Paradigm
> +========
> +
> +The most basic counter device may be expressed as a single Count
> +associated with a single Signal via a single Synapse. Take for example
> +a counter device which simply accumulates a count of rising edges on a
> +source input line.
> +
> + Count Synapse Signal
> + ----- ------- ------
> ++---------------------+
> +| Data: Count | Rising Edge ________
> +| Function: Increase | <------------- / Source \
> +| | ____________
> ++---------------------+
> +
> +In this example, the Signal is a source input line with a pulsing
> +voltage, while the Count is a persistent count value which is repeatedly
> +incremented. The Signal is associated with the respective Count via a
> +Synapse. The increase function is triggered by the Signal data condition
> +specified by the Synapse -- in this case a rising edge condition on the
> +voltage input line. In summary, the counter device existence and
> +behavior is aptly represented by respective Count, Signal, and Synapse
> +components: a rising edge condition triggers an increase function on an
> +accumulating count datum.
> +
> +A counter device is not limited to a single Signal; in fact, in theory
> +many number of Signals may be associated with even a single Count. For
many Signals
> +example, a quadrature encoder counter device can keep track of position
> +based on the states of two input lines.
> +
> + Count Synapse Signal
> + ----- ------- ------
> ++-------------------------+
> +| Data: Position | Both Edges ___
> +| Function: Quadrature x4 | <------------ / A \
> +| | _______
> +| |
> +| | Both Edges ___
> +| | <------------ / B \
> +| | _______
> ++-------------------------+
Perhaps add the external encoder itself to the asci art? Where are
the signals coming from. It's not implausible down the line that you'll
have to have explicit representation for that hardware. Could be
a resolve to encoder convertor for example with its own control registers.
> +
> +In this example, two Signals (quadrature encoder lines A and B) are
> +associated to a single Count: 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 Count, Signals, and Synapses simply represent this
> +hardware behavior and functionality.
> +
> +Signals associated to the same Count can have differing Synapse action
> +mode conditions. For example, a quadrature encoder counter device
> +operating in a non-quadrature Pulse-Direction mode could have one input
> +line dedicated for movement and a second input line dedicated for
> +direction.
> +
> + Count Synapse Signal
> + ----- ------- ------
> ++---------------------------+
> +| Data: Position | Rising Edge ___
> +| Function: Pulse-Direction | <------------- / A \ (Movement)
> +| | _______
> +| |
> +| | None ___
> +| | <------------- / B \ (Direction)
> +| | _______
> ++---------------------------+
> +
> +Only Signal A triggers the "Pulse-Direction" update function, but the
> +instantaneous state of Signal B is still required in order to know the
> +direction so that the position data may be properly updated. Ultimately,
> +both Signals are associated to the same Count via two respective
> +Synapses, but only one Synapse has an active action mode condition which
> +triggers the respective count function while the other is left with a
> +"None" condition action mode to indicate its respective Signal's
> +availability for state evaluation despite its non-triggering mode.
> +
> +The flexibility of Synapses allows for the representation of counters
> +whose respective Count Signal relationships would be difficult to
> +represent as isolated inputs. For example a tri-color LED color counter
> +can easily be represented with three Synapses:
> +
> + Count Synapse Signal
> + ----- ------- ------
> ++------------------+
> +| Data: Count | Both Edges _______
> +| Function: Purple | <------------ / Red \
> +| | ___________
> +| |
> +| | Both Edges _______
> +| | <------------ / Green \
> +| | ___________
> +| |
> +| | Both Edges _______
> +| | <------------ / Blue \
> +| | ___________
> ++------------------+
> +
> +This counter accumulates the number of times the tri-color LED has
> +turned to purple. By utilizing the Synapses to associate the three color
> +Signals to the single Count, the count function evaluation relationship
> +becomes apparent: combined color is checked when an individual color is
> +turned on or off, and if only Red and Blue are active then Count datum
> +is incremented to indicate the color has now turned to Purple.
This one has somewhat drifted away from common cases. I'm not sure it
is necessary to understanding so I'd be tempted to drop it as the only
potential this and the one below have right now is to distract from
the code we are actually interested in getting reviewed.
> +
> +Although the examples thus far have been representations of physical
> +devices, this is not a necessity. A counter simply represent the
> +evaluation (Count) of input data (Signals) triggered by specific
> +conditions (Synapses). 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.
> +
> + Count Synapse 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 Synapse action modes consist of the various
> +expressed sequence tags (ESTs), and the Count is a list of diseases
> +discovered (in this particular example the function is evaluating for
> +possible cancers). Note how the Signal in this example does not
> +represent a physical voltage line, nor does the Synapse action mode
> +represent a physical voltage line state change, nor does the Count
> +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, Synapse, and Count 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 (e.g. an accumulated
> +count of rising edges).
> +
> +Userspace Interface
> +===================
> +
> +Several sysfs attributes are generated by the Generic Counter interface,
> +and reside under the /sys/bus/counter/devices/counterX directory, where
> +counterX refers to the respective counter device. Please see
> +Documentation/ABI/testing/sys-bus-counter-generic-sysfs for detailed
> +information on each Generic Counter interface sysfs attribute.
I would loose the generic-sysfs off that filename.
sys-bus-counter itself means it covers the generic stuff.
> +
> +Through these sysfs attributes, programs and scripts may interact with
> +the Generic Counter paradigm Counts, Signals, and Synapses 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 counter_signal_ext
> +-------------------------
> +
> +This structure defines a Generic Counter Signal extension attribute.
> +These attributes expose auxiliary configuration and functionality
> +specific to the respective Generic Counter Signal.
> +
> + name: Attribute name
> + read: Read callback for this attribute; may be NULL
> + write: Write callback for this attribute; may be NULL
> + priv: Data private to the driver
> +
> +struct counter_signal
> +---------------------
> +
> +This structure defines a Generic Counter Signal component. Typically,
> +this will correlate with an input channel on a physical counter device.
> +This structure is the simplest of the Generic Counter paradigm core
> +components to define with only two structure members which require
> +explicit configuration:
> +
> + id: unique ID used to identify signal
> + name: device-specific signal name
Again, guidance needed on what this actually should be. It's an important
bit of the ABI so best to document what the form of these things should be.
> + ext: optional array of Counter Signal extensions
> + num_ext: number of Counter Signal extensions specified in
> + ext
> + priv: optional private data supplied by driver
> +
> +struct counter_synapse
> +----------------------
> +
> +This structure defines a Generic Counter Synapse component. To properly
> +utilize this structure, action modes and an associated Signal must be
> +defined:
> +
> + action: current action mode
> + actions: available action modes
I would make the names more distinct as action vs actions will not be easy
to read in the code.
> + num_actions: number of action modes specified in actions
> + signal: pointer to associated signal
> +
> +struct counter_count_ext
> +------------------------
> +
> +This structure defines a Generic Counter Count extension attribute.
> +These attributes expose auxiliary configuration and functionality
> +specific to the respective Generic Counter Count.
> +
> + name: attribute name
> + read: read callback for this attribute; may be NULL
> + write: write callback for this attribute; may be NULL
> + priv: data private to the driver
> +
> +struct counter_count
> +--------------------
> +
> +This structure defines a Generic Counter Count 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 Synapse action condition), and a set of Synapses for the associated
> +Signals:
> +
> + id: unique ID used to identify Count
> + name: device-specific Count name
> + function: current function mode
current_function or similar will make it more distinct from just the plural.
> + functions: available function modes
> + num_functions: number of functions specified in @functions
> + synapses: array of synapses for initialization
> + num_synapses: number of synapses specified in @synapses
> + ext: optional array of Counter Count extensions
> + num_ext: number of Counter Count extensions specified in
> + ext
> + priv: optional private data supplied by driver
> +
> +struct counter_device_ext
> +-------------------------
> +
> +This structure defines a Generic Counter extension attribute. These
> +attributes expose auxiliary configuration and functionality specific to
> +the respective Generic Counter.
> +
> + name: attribute name
> + read: read callback for this attribute; may be NULL
> + write: write callback for this attribute; may be NULL
> + priv: data private to the driver
> +
> +struct counter_device
> +---------------------
> +
> +This is the main data structure for a Generic Counter device. Access to
> +all respective Counts, Signals, and Synapses is possible from this
> +structure. This structure requires the configuration of a name, Generic
> +Counter Signals, and Generic Counter Counts:
> +
> +name: name of the device
> +parent: optional parent device providing the counters
> +signal_read: read callback for Signal attribute; may be NULL
> +signal_write: write callback for Signal attribute; may be NULL
> +count_read: read callback for Count attribute; may be NULL
> +count_write: write callback for Count attribute; may be NULL
> +function_get: function to get the current count function mode. Returns
> + 0 on success and negative error code on error. The index
> + of the respective Count's returned function mode should
> + be passed back via the function parameter.
> +function_set: function to set the count function mode. function is the
> + index of the requested function mode from the respective
> + Count's functions array.
> +action_get: function to get the current action mode. Returns 0 on
> + success and negative error code on error. The index of
> + the respective Signal's returned action mode should be
> + passed back via the action parameter.
> +action_set: function to set the action mode. action is the index of
> + the requested action mode from the respective Synapse's
> + actions array.
> +signals: array of Signals
> +num_signals: number of Signals specified in @signals
> +counts: array of Counts
> +num_counts: number of Counts specified in @counts
> +ext: optional array of Counter device extensions
> +num_ext: number of Counter device extensions specified in ext
> +priv: optional private data supplied by driver
> +
> +Registration functions
> +----------------------
> +
> +A counter device is registered to the system by passing the respective
> +initialized counter_device structure to the counter_register function;
> +similarly, the counter_unregister function unregisters the respective
> +Counter. The devm_counter_register and devm_counter_unregister functions
> +serve as device memory-managed versions of the counter_register and
> +counter_unregister functions respectively.
> +
> +Implementation
> +==============
> +
> +To support a counter device, a driver must first allocate the available
> +Counter Signals via counter_signal structures. These Signals should
> +be stored as an array and set to the signals array member of an
> +allocated counter_device structure before the Counter is registered to
> +the system.
> +
> +Counter Counts may be allocated via counter_count structures, and
> +respective Counter Signal associations (Synapses) made via
> +counter_synapse structures. Associated counter_synapse structures are
> +stored as an array and set to the the synapses array member of the
> +respective counter_count structure. These counter_count structures are
> +set to the counts array member of an allocated counter_device structure
> +before the Counter is registered to the system.
> +
> +Driver callbacks should be provided to the counter_device structure in
> +order to communicate with the device: to read and write various Signals
> +and Counts, and to set and get the "action mode" and "function mode" for
> +various Synapses and Counts respectively.
> +
> +A defined counter_device structure may be registered to the system by
> +passing it to the counter_register function, and unregistered by passing
> +it to the counter_unregister function. Similarly, the
> +devm_counter_register and devm_counter_unregister functions may be used
> +if device memory-managed registration is desired.
> +
> +Extension sysfs attributes can be created for auxiliary functionality
> +and data by passing in defined counter_device_ext, counter_count_ext,
> +and counter_signal_ext structures. In these cases, the
> +counter_device_ext structure is used for global configuration of the
> +respective Counter device, while the counter_count_ext and
> +counter_signal_ext structures allow for auxiliary exposure and
> +configuration of a specific Count or Signal respectively.
> +
> +Architecture
> +============
> +
> +The counter_init function is called when the generic-counter module is
> +loaded on the system. At this point, a bus_type named "counter" is
> +registered and a dedicated "counter" chrdev region is allocated; these
> +are removed and cleaned-up in the counter_exit function called when the
> +generic-counter module is unloaded from the system.
What's the chrdev for?
> +
> +Counter devices are registered to the system via the counter_register
> +function, and later removed via the counter_unregister function. The
> +counter_register function establishes a unique ID for the Counter device
> +and creates an associated device node under /dev called counterX, where
> +X is the mentioned unique ID. A respective sysfs directory is created
> +for the Counter device with a similar naming scheme:
> +
> + /sys/bus/counter/devices/counterX
> +
> +Sysfs attributes are created within the counterX directory to expose
> +functionality, configurations, and data relating to the Counts, Signals,
> +and Synapses of the Counter device, as well as options and information
> +for the Counter device itself.
> +
> +For a more detailed breakdown of the available Generic Counter interface
> +sysfs attributes, please refer to the
> +Documentation/ABI/testing/sys-bus-counter-generic-sysfs file.
> +
> +The Signals and Counts associated with the Counter device are registered
> +to the system as well by the counter_register function. The
> +signal_read/signal_write driver callbacks are associated to their
> +respective Signal attributes, while the count_read/count_write and
> +function_get/function_set driver callbacks are associated to their
> +respective Count attributes; similarly, the same is true for the
> +action_get/action_set driver callbacks and their respective Synapse
> +attributes. If a driver callback is left undefined, then the respective
> +read/write permission is left disabled for the relevant attributes.
> +
> +Similarly, extension sysfs attributes are created for the defined
> +counter_device_ext, counter_count_ext, and counter_signal_ext
> +structures that are passed in.
> diff --git a/Documentation/driver-api/iio/index.rst b/Documentation/driver-api/iio/index.rst
> index e5c3922d1b6f..e6c5b75c2e03 100644
> --- a/Documentation/driver-api/iio/index.rst
> +++ b/Documentation/driver-api/iio/index.rst
> @@ -15,3 +15,4 @@ Contents:
> buffers
> triggers
> triggered-buffers
> + generic-counter
I would give it a category to itself and not put it under
the iio docs (unless you decide to host in IIO)
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 38da1bc615b3..08eba78057e4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3665,6 +3665,7 @@ COUNTER INTERFACE
> M: William Breathitt Gray <[email protected]>
> L: [email protected]
> S: Maintained
> +F: Documentation/driver-api/counter/
> F: Documentation/ABI/testing/sysfs-bus-counter-*
> F: drivers/iio/counter/
> F: include/linux/iio/counter.h
On Thu, 14 Dec 2017 15:50:43 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch introduces the 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 where possible rather than the Generic Counter interface
> directly.
>
> In the context of the Generic Counter interface, a counter is defined as
> a device that reports one or more "counter counts" based on the state
> changes of one or more "counter signals" as evaluated by a defined
> "counter function."
>
> Driver callbacks should be provided to communicate with the device: to
> read and write various Signals and Counts, and to set and get the
> "action mode" and "function mode" for various Synapses and Counts
> respectively.
>
> To support a counter device, a driver must first allocate the available
> Counter Signals via counter_signal structures. These Signals should
> be stored as an array and set to the signals array member of an
> allocated counter_device structure before the Counter is registered to
> the system.
>
> Counter Counts may be allocated via counter_count structures, and
> respective Counter Signal associations (Synapses) made via
> counter_synapse structures. Associated counter_synapse structures are
> stored as an array and set to the the synapses array member of the
> respective counter_count structure. These counter_count structures are
> set to the counts array member of an allocated counter_device structure
> before the Counter is registered to the system.
>
> A counter device is registered to the system by passing the respective
> initialized counter_device structure to the counter_register function;
> similarly, the counter_unregister function unregisters the respective
> Counter. The devm_counter_register and devm_counter_unregister functions
> serve as device memory-managed versions of the counter_register and
> counter_unregister functions respectively.
A few minor comments inline but looks basically sound to me.
Jonathan
>
> Signed-off-by: William Breathitt Gray <[email protected]>
> ---
> MAINTAINERS | 7 +
> drivers/iio/Kconfig | 3 +-
> drivers/iio/counter/Kconfig | 16 +-
> drivers/iio/counter/Makefile | 3 +
> drivers/iio/counter/generic-counter.c | 992 ++++++++++++++++++++++++++++++++++
> include/linux/iio/counter.h | 239 ++++++++
> 6 files changed, 1255 insertions(+), 5 deletions(-)
> create mode 100644 drivers/iio/counter/generic-counter.c
> create mode 100644 include/linux/iio/counter.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 36f76be322a3..07dd7b933bfa 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3661,6 +3661,13 @@ W: http://www.fi.muni.cz/~kas/cosa/
> S: Maintained
> F: drivers/net/wan/cosa*
>
> +COUNTER INTERFACE
> +M: William Breathitt Gray <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/counter/
> +F: include/linux/iio/counter.h
> +
> CPMAC ETHERNET DRIVER
> M: Florian Fainelli <[email protected]>
> L: [email protected]
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index b3c8c6ef0dff..62a923aeb462 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -73,7 +73,6 @@ source "drivers/iio/adc/Kconfig"
> source "drivers/iio/amplifiers/Kconfig"
> source "drivers/iio/chemical/Kconfig"
> source "drivers/iio/common/Kconfig"
> -source "drivers/iio/counter/Kconfig"
> source "drivers/iio/dac/Kconfig"
> source "drivers/iio/dummy/Kconfig"
> source "drivers/iio/frequency/Kconfig"
> @@ -95,3 +94,5 @@ source "drivers/iio/proximity/Kconfig"
> source "drivers/iio/temperature/Kconfig"
>
> endif # IIO
> +
> +source "drivers/iio/counter/Kconfig"
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index 474e1ac4e7c0..4eaf4e53c5aa 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -3,11 +3,18 @@
> #
> # When adding new entries keep the list in alphabetical order
>
> -menu "Counters"
> +menuconfig COUNTER
> + tristate "Counter support"
> + help
> + Provides support for Counter devices. The Generic Counter API provides
> + rudimentary support for counters and serves as building blocks to
> + create more complex counter interfaces.
> +
> +if COUNTER
>
> config 104_QUAD_8
> tristate "ACCES 104-QUAD-8 driver"
> - depends on PC104 && X86 && ISA_BUS_API
> + depends on PC104 && X86 && ISA_BUS_API && IIO
> help
> Say yes here to build support for the ACCES 104-QUAD-8 quadrature
> encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
> @@ -23,11 +30,12 @@ config 104_QUAD_8
>
> config STM32_LPTIMER_CNT
> tristate "STM32 LP Timer encoder counter driver"
> - depends on MFD_STM32_LPTIMER || COMPILE_TEST
> + depends on (MFD_STM32_LPTIMER || COMPILE_TEST) && IIO
> help
> Select this option to enable STM32 Low-Power Timer quadrature encoder
> and counter driver.
>
> To compile this driver as a module, choose M here: the
> module will be called stm32-lptimer-cnt.
> -endmenu
> +
> +endif # COUNTER
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 1b9a896eb488..513c49d832d4 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -4,5 +4,8 @@
>
> # When adding new entries keep the list in alphabetical order
>
> +obj-$(CONFIG_COUNTER) += counter.o
> +counter-$(CONFIG_COUNTER) += generic-counter.o
> +
> obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
> obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/generic-counter.c b/drivers/iio/counter/generic-counter.c
> new file mode 100644
> index 000000000000..0efd36ee2118
> --- /dev/null
> +++ b/drivers/iio/counter/generic-counter.c
> @@ -0,0 +1,992 @@
> +/*
> + * Generic 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.
> + */
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/fs.h>
> +#include <linux/gfp.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/kdev_t.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/sysfs.h>
> +#include <linux/types.h>
> +
> +#include <linux/iio/counter.h>
> +
> +struct counter_device_attr {
> + struct device_attribute dev_attr;
> + struct list_head l;
> + void *component;
> + void *component_data;
> +};
> +
> +static int counter_attribute_create(const char *const prefix,
> + const char *const name,
> + ssize_t (*show)(struct device *dev, struct device_attribute *attr,
> + char *buf),
> + ssize_t (*store)(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t len),
> + void *const component, void *const component_data,
> + struct counter_device_state *const device_state)
> +{
> + struct counter_device_attr *counter_attr;
> + struct device_attribute *dev_attr;
> + struct counter_device_attr *t;
> + int err;
> + struct list_head *const attr_list = &device_state->attr_list;
> +
> + /* Allocate a Counter device attribute */
> + counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
> + if (!counter_attr)
> + return -ENOMEM;
> + dev_attr = &counter_attr->dev_attr;
> +
> + sysfs_attr_init(&dev_attr->attr);
> +
> + /* Configure device attribute */
> + dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", prefix, name);
> + if (!dev_attr->attr.name) {
> + err = -ENOMEM;
> + goto err_free_counter_attr;
> + }
> + if (show) {
> + dev_attr->attr.mode |= 0444;
> + dev_attr->show = show;
> + }
> + if (store) {
> + dev_attr->attr.mode |= 0200;
> + dev_attr->store = store;
> + }
> +
> + /* Check for duplicate name */
> + list_for_each_entry(t, attr_list, l)
> + if (!strcmp(t->dev_attr.attr.name, dev_attr->attr.name)) {
> + dev_err(&device_state->dev,
> + "tried to double register : %s\n",
> + t->dev_attr.attr.name);
> + err = -EBUSY;
> + goto err_free_attr_name;
> + }
I think there is no actual need to deal with the duplicate
here. It will cause an error when the sysfs attributes are actually
created. I'd leave it until there to run this sanity check as this
represents a bug anyway so no real need to check for it early.
> +
> + /* Store associated Counter component with attribute */
> + counter_attr->component = component;
> + counter_attr->component_data = component_data;
> +
> + /* Keep track of the attribute for later cleanup */
> + list_add(&counter_attr->l, attr_list);
> + device_state->num_attr++;
> +
> + return 0;
> +
> +err_free_attr_name:
> + kfree(dev_attr->attr.name);
> +err_free_counter_attr:
> + kfree(counter_attr);
> + return err;
> +}
> +
> +#define to_counter_attr(_dev_attr) \
> + container_of(_dev_attr, struct counter_device_attr, dev_attr)
> +
> +static ssize_t counter_signal_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_signal *const signal = to_counter_attr(attr)->component;
> +
> + return counter->signal_read(counter, signal, buf);
> +}
> +
> +static ssize_t counter_signal_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_signal *const signal = to_counter_attr(attr)->component;
> +
> + return counter->signal_write(counter, signal, buf, len);
> +}
> +
> +static ssize_t counter_device_attr_name_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const char *const name = to_counter_attr(attr)->component;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n", name);
> +}
> +
> +static ssize_t counter_signal_ext_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const struct counter_signal_ext *const ext = devattr->component_data;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_signal *const signal = devattr->component;
> +
> + return ext->read(counter, signal, ext->priv, buf);
> +}
> +
> +static ssize_t counter_signal_ext_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const struct counter_signal_ext *const ext = devattr->component_data;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_signal *const signal = devattr->component;
> +
> + return ext->write(counter, signal, ext->priv, buf, len);
> +}
> +
> +static int counter_signal_ext_register(const char *const prefix,
> + struct counter_signal *const signal,
> + struct counter_device_state *const device_state)
> +{
> + const size_t num_ext = signal->num_ext;
> + size_t i;
> + const struct counter_signal_ext *ext;
> + int err;
> +
> + /* Return early if no extensions */
> + if (!signal->ext || !num_ext)
> + return 0;
> +
> + /* Create an attribute for each extension */
> + for (i = 0 ; i < num_ext; i++) {
> + ext = signal->ext + i;
> + err = counter_attribute_create(prefix, ext->name,
> + (ext->read) ? counter_signal_ext_show : NULL,
> + (ext->write) ? counter_signal_ext_store : NULL,
> + signal, ext, device_state);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int counter_signal_attributes_create(const char *const signal_attr_name,
> + const struct counter_device *const counter,
> + struct counter_signal *const signal)
> +{
> + struct counter_device_state *const device_state = counter->device_state;
> + int err;
> + const char *prefix;
> +
> + /* Create main Signal attribute */
> + err = counter_attribute_create("", signal_attr_name,
> + (counter->signal_read) ? counter_signal_show : NULL,
> + (counter->signal_write) ? counter_signal_store : NULL,
> + signal, NULL, device_state);
> + if (err)
> + return err;
> +
> + prefix = kasprintf(GFP_KERNEL, "%s_", signal_attr_name);
> + if (!prefix)
> + return -ENOMEM;
> +
> + /* Create Signal name attribute */
> + if (signal->name) {
> + err = counter_attribute_create(prefix, "name",
> + counter_device_attr_name_show, NULL, signal->name, NULL,
> + device_state);
> + if (err)
> + goto err_free_prefix;
> + }
> +
> + /* Register Signal extension attributes */
> + err = counter_signal_ext_register(prefix, signal, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + kfree(prefix);
> +
> + return 0;
> +
> +err_free_prefix:
> + kfree(prefix);
> + return err;
> +}
> +
> +static int counter_signals_register(const struct counter_device *const counter)
> +{
> + const size_t num_signals = counter->num_signals;
> + struct counter_device_state *const device_state = counter->device_state;
> + struct device *const dev = &device_state->dev;
> + size_t i;
> + struct counter_signal *signal;
> + const char *name;
> + int err;
> +
> + /* At least one Signal must be defined */
> + if (!counter->signals || !num_signals) {
> + dev_err(dev, "Signals undefined\n");
> + return -EINVAL;
> + }
> +
> + /* Register each Signal */
> + for (i = 0; i < num_signals; i++) {
> + signal = counter->signals + i;
> +
> + /* Generate Signal attribute name */
> + name = kasprintf(GFP_KERNEL, "signal%d", signal->id);
> + if (!name)
> + return -ENOMEM;
> +
> + /* Create all attributes associated with Signal */
> + err = counter_signal_attributes_create(name, counter, signal);
> + if (err)
> + goto err_free_name;
> +
> + kfree(name);
> + }
> +
> + return 0;
> +
> +err_free_name:
> + kfree(name);
> + return err;
> +}
> +
> +static ssize_t counter_action_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + int err;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = devattr->component_data;
> + struct counter_synapse *const synapse = devattr->component;
> + size_t action;
> +
> + err = counter->action_get(counter, count, synapse, &action);
> + if (err)
> + return err;
> +
> + synapse->action = action;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n", synapse->actions[action]);
> +}
> +
> +static ssize_t counter_action_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + struct counter_synapse *const synapse = devattr->component;
> + size_t action;
> + const size_t num_actions = synapse->num_actions;
> + int err;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = devattr->component_data;
> +
> + /* Find requested action mode */
> + for (action = 0; action < num_actions; action++)
> + if (sysfs_streq(buf, synapse->actions[action]))
> + break;
> + /* If requested action mode not found */
> + if (action >= num_actions)
> + return -EINVAL;
> +
> + err = counter->action_set(counter, count, synapse, action);
> + if (err)
> + return err;
> +
> + synapse->action = action;
> +
> + return len;
> +}
> +
> +static ssize_t counter_device_attr_available_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const char *const *const modes = devattr->component;
> + const size_t num_modes = *(size_t *)devattr->component_data;
> + ssize_t len = 0;
> + size_t i;
> +
> + for (i = 0; i < num_modes; i++)
> + len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", modes[i]);
> +
> + return len;
> +}
> +
> +static int counter_synapses_register(const struct counter_device *const counter,
> + struct counter_count *const count, const char *const count_attr_name)
> +{
> + struct counter_device_state *const device_state = counter->device_state;
> + const size_t num_synapses = count->num_synapses;
> + struct device *const dev = &device_state->dev;
> + size_t i;
> + struct counter_synapse *synapse;
> + const char *prefix;
> + int err;
> +
> + /* At least one Synapse must be defined */
> + if (!count->synapses || !num_synapses) {
> + dev_err(dev, "Count '%d' Synapses undefined\n", count->id);
> + return -EINVAL;
> + }
> +
> + /* Register each Synapse */
> + for (i = 0; i < num_synapses; i++) {
> + synapse = count->synapses + i;
> +
> + /* Ensure all Synapses have a defined Signal */
> + if (!synapse->signal) {
> + dev_err(dev,
> + "Count '%d' Synapse '%zu' Signal undefined\n",
> + count->id, i);
> + return -EINVAL;
> + }
> +
> + /* At least one action mode must be defined for each Synapse */
> + if (!synapse->actions || !synapse->num_actions) {
> + dev_err(dev,
> + "Count '%d' Signal '%d' action modes undefined\n",
> + count->id, synapse->signal->id);
> + return -EINVAL;
> + }
> +
> + /* Generate attribute prefix */
> + prefix = kasprintf(GFP_KERNEL, "%s_signal%d_", count_attr_name,
> + synapse->signal->id);
> + if (!prefix)
> + return -ENOMEM;
> +
> + /* Create action attribute */
> + err = counter_attribute_create(prefix, "action",
> + (counter->action_get) ? counter_action_show : NULL,
> + (counter->action_set) ? counter_action_store : NULL,
> + synapse, count, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + /* Create action_available attribute */
> + err = counter_attribute_create(prefix, "action_available",
> + counter_device_attr_available_show, NULL,
> + synapse->actions, &synapse->num_actions, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + kfree(prefix);
> + }
> +
> + return 0;
> +
> +err_free_prefix:
> + kfree(prefix);
> + return err;
> +}
> +
> +static ssize_t counter_count_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = to_counter_attr(attr)->component;
> +
> + return counter->count_read(counter, count, buf);
> +}
> +
> +static ssize_t counter_count_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = to_counter_attr(attr)->component;
> +
> + return counter->count_write(counter, count, buf, len);
> +}
> +
> +static ssize_t counter_function_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int err;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = to_counter_attr(attr)->component;
> + size_t function;
> +
> + err = counter->function_get(counter, count, &function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n", count->functions[function]);
> +}
> +
> +static ssize_t counter_function_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + struct counter_count *const count = to_counter_attr(attr)->component;
> + const size_t num_functions = count->num_functions;
> + size_t function;
> + int err;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> +
> + /* Find requested Count function mode */
> + for (function = 0; function < num_functions; function++)
> + if (sysfs_streq(buf, count->functions[function]))
> + break;
> + /* Return error if requested Count function mode not found */
> + if (function >= num_functions)
> + return -EINVAL;
> +
> + err = counter->function_set(counter, count, function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + return len;
> +}
> +
> +static ssize_t counter_count_synapses_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + size_t i;
> + struct counter_count *const count = to_counter_attr(attr)->component;
> + const struct counter_synapse *synapse;
> + ssize_t len = 0;
> +
> + /* Print out a list of every Signal association to Count */
> + for (i = 0; i < count->num_synapses; i++) {
> + synapse = count->synapses + i;
> + len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
> + synapse->signal->id, synapse->signal->name,
> + synapse->actions[synapse->action]);
> + if (len >= PAGE_SIZE)
> + return -ENOMEM;
> + }
> +
> + return len;
> +}
> +
> +static ssize_t counter_count_ext_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const struct counter_count_ext *const ext = devattr->component_data;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = devattr->component;
> +
> + return ext->read(counter, count, ext->priv, buf);
> +}
> +
> +static ssize_t counter_count_ext_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const struct counter_count_ext *const ext = devattr->component_data;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_count *const count = devattr->component;
> +
> + return ext->write(counter, count, ext->priv, buf, len);
> +}
> +
> +static int counter_count_ext_register(const char *const prefix,
> + struct counter_count *const count,
> + struct counter_device_state *const device_state)
> +{
> + const size_t num_ext = count->num_ext;
> + size_t i;
> + const struct counter_count_ext *ext;
> + int err;
> +
> + /* Return early if no extensions */
> + if (!count->ext || !num_ext)
> + return 0;
> +
> + /* Create an attribute for each extension */
> + for (i = 0 ; i < num_ext; i++) {
> + ext = count->ext + i;
> + err = counter_attribute_create(prefix, ext->name,
> + (ext->read) ? counter_count_ext_show : NULL,
> + (ext->write) ? counter_count_ext_store : NULL,
> + count, ext, device_state);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int counter_count_attributes_create(const char *const count_attr_name,
> + const struct counter_device *const counter,
> + struct counter_count *const count)
> +{
> + struct counter_device_state *const device_state = counter->device_state;
> + int err;
> + const char *prefix;
> +
> + /* Create main Count attribute */
> + err = counter_attribute_create("", count_attr_name,
> + (counter->count_read) ? counter_count_show : NULL,
> + (counter->count_write) ? counter_count_store : NULL,
> + count, NULL, device_state);
> + if (err)
> + return err;
> +
> + prefix = kasprintf(GFP_KERNEL, "%s_", count_attr_name);
> + if (!prefix)
> + return -ENOMEM;
> +
> + /* Create Count function attribute */
> + err = counter_attribute_create(prefix, "function",
> + (counter->function_get) ? counter_function_show : NULL,
> + (counter->function_set) ? counter_function_store : NULL,
> + count, NULL, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + /* Create Count function_available attribute */
> + err = counter_attribute_create(prefix, "function_available",
> + counter_device_attr_available_show, NULL, count->functions,
> + &count->num_functions, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + /* Create Count synapses attribute */
> + err = counter_attribute_create(prefix, "synapses",
> + counter_count_synapses_show, NULL, count, NULL, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + /* Create Count name attribute */
> + if (count->name) {
> + err = counter_attribute_create(prefix, "name",
> + counter_device_attr_name_show, NULL, count->name, NULL,
> + device_state);
> + if (err)
> + goto err_free_prefix;
> + }
> +
> + /* Register Count extension attributes */
> + err = counter_count_ext_register(prefix, count, device_state);
> + if (err)
> + goto err_free_prefix;
> +
> + kfree(prefix);
> +
> + return 0;
> +
> +err_free_prefix:
> + kfree(prefix);
> + return err;
> +}
> +
> +static int counter_counts_register(const struct counter_device *const counter)
> +{
> + const size_t num_counts = counter->num_counts;
> + struct device *const dev = &counter->device_state->dev;
> + size_t i;
> + struct counter_count *count;
> + const char *name;
> + int err;
> +
> + /* At least one Count must be defined */
> + if (!counter->counts || !num_counts) {
> + dev_err(dev, "Counts undefined\n");
> + return -EINVAL;
> + }
> +
> + /* Register each Count */
> + for (i = 0; i < num_counts; i++) {
> + count = counter->counts + i;
> +
> + /* At least one function mode must be defined for each Count */
> + if (!count->functions || !count->num_functions) {
> + dev_err(dev, "Count '%d' function modes undefined\n",
> + count->id);
> + return -EINVAL;
> + }
> +
> + /* Generate attribute name */
> + name = kasprintf(GFP_KERNEL, "count%d", count->id);
> + if (!name)
> + return -ENOMEM;
> +
> + /* Register the Synapses associated with each Count */
> + err = counter_synapses_register(counter, count, name);
> + if (err)
> + goto err_free_name;
> +
> + /* Create all attributes associated with Count */
> + err = counter_count_attributes_create(name, counter, count);
> + if (err)
> + goto err_free_name;
> +
> + kfree(name);
> + }
> +
> + return 0;
> +
> +err_free_name:
> + kfree(name);
> + return err;
> +}
> +
> +static struct bus_type counter_bus_type = {
> + .name = "counter"
> +};
> +
> +static dev_t counter_devt;
> +
> +#define COUNTER_DEV_MAX 256
> +
> +static int __init counter_init(void)
> +{
> + int err;
> +
> + err = bus_register(&counter_bus_type);
> + if (err) {
> + pr_err("counter: could not register Counter bus type\n");
> + return err;
> + }
> +
> + err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
> + if (err) {
> + pr_err("counter: failed to allocate char dev region\n");
> + bus_unregister(&counter_bus_type);
> + return err;
Drop this and return err below
> + }
> +
> + return 0;
> +}
> +
> +static void __exit counter_exit(void)
> +{
> + if (counter_devt)
> + unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
> + bus_unregister(&counter_bus_type);
> +}
> +
> +static void free_counter_device_state_attr_list(struct list_head *attr_list)
> +{
> + struct counter_device_attr *p, *n;
> +
> + list_for_each_entry_safe(p, n, attr_list, l) {
> + kfree(p->dev_attr.attr.name);
> + list_del(&p->l);
> + kfree(p);
> + }
> +}
> +
> +/* Provides a unique ID for each counter device */
> +static DEFINE_IDA(counter_ida);
> +
> +static void counter_device_release(struct device *dev)
> +{
> + struct counter_device *const counter = dev_get_drvdata(dev);
> + struct counter_device_state *const device_state = counter->device_state;
> +
> + kfree(device_state->attr_group.attrs);
> + free_counter_device_state_attr_list(&device_state->attr_list);
> + ida_simple_remove(&counter_ida, device_state->id);
> + kfree(device_state);
> +}
> +
> +static struct device_type counter_device_type = {
> + .name = "counter_device",
> + .release = counter_device_release
> +};
> +
> +static int counter_chrdev_open(struct inode *inode, struct file *filp)
> +{
> + struct counter_device_state *const device_state = container_of(
> + inode->i_cdev, struct counter_device_state, chrdev);
> + struct device *const dev = &device_state->dev;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> +
> + get_device(dev);
> + filp->private_data = counter;
> +
> + return 0;
> +}
> +
> +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> +{
> + struct counter_device_state *const device_state = container_of(
> + inode->i_cdev, struct counter_device_state, chrdev);
> +
> + put_device(&device_state->dev);
> +
> + return 0;
> +}
> +
> +static const struct file_operations counter_fileops = {
> + .owner = THIS_MODULE,
> + .open = counter_chrdev_open,
> + .release = counter_chrdev_release
> +};
> +
> +static ssize_t counter_device_ext_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const struct counter_device_ext *const ext = devattr->component_data;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> +
> + return ext->read(counter, ext->priv, buf);
> +}
> +
> +static ssize_t counter_device_ext_store(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t len)
> +{
> + const struct counter_device_attr *const devattr = to_counter_attr(attr);
> + const struct counter_device_ext *const ext = devattr->component_data;
> + struct counter_device *const counter = dev_get_drvdata(dev);
> +
> + return ext->write(counter, ext->priv, buf, len);
> +}
> +
> +static int counter_device_ext_register(struct counter_device *const counter)
> +{
> + const size_t num_ext = counter->num_ext;
> + size_t i;
> + const struct counter_device_ext *ext;
> + int err;
> +
> + /* Return early if no extensions */
> + if (!counter->ext || !num_ext)
> + return 0;
> +
> + /* Create an attribute for each extension */
> + for (i = 0 ; i < num_ext; i++) {
> + ext = counter->ext + i;
> + err = counter_attribute_create("", ext->name,
> + (ext->read) ? counter_device_ext_show : NULL,
> + (ext->write) ? counter_device_ext_store : NULL,
> + NULL, ext, counter->device_state);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t counter_name_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct counter_device *const counter = dev_get_drvdata(dev);
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", counter->name);
> +}
> +
> +static DEVICE_ATTR_RO(counter_name);
> +
> +/**
> + * counter_register - register Counter to the system
> + * @counter: pointer to Counter to register
> + *
> + * This function registers a Counter to the system. A sysfs "counter" directory
> + * will be created and populated with sysfs attributes correlating with the
> + * Counter Signals, Synapses, and Counts respectively.
> + */
> +int counter_register(struct counter_device *const counter)
> +{
> + int err;
> + struct counter_device_state *device_state;
> + struct counter_device_attr *p;
> + size_t i = 0;
> +
> + if (!counter)
> + return -EINVAL;
> +
> + /* Allocate internal state container for Counter device */
> + device_state = kzalloc(sizeof(*device_state), GFP_KERNEL);
> + if (!device_state)
> + return -ENOMEM;
> + counter->device_state = device_state;
> +
> + /* Acquire unique ID */
> + device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
> + if (device_state->id < 0) {
> + err = device_state->id;
> + goto err_free_device_state;
> + }
> +
> + /* Configure device structure for Counter */
> + device_state->dev.type = &counter_device_type;
> + device_state->dev.bus = &counter_bus_type;
> + if (counter->parent) {
> + device_state->dev.parent = counter->parent;
> + device_state->dev.of_node = counter->parent->of_node;
> + }
> + dev_set_name(&device_state->dev, "counter%d", device_state->id);
> + device_initialize(&device_state->dev);
> + dev_set_drvdata(&device_state->dev, counter);
> + device_state->dev.devt = MKDEV(MAJOR(counter_devt), device_state->id);
> +
> + /* Initialize attribute list */
> + INIT_LIST_HEAD(&device_state->attr_list);
> +
> + /* Verify Signals are valid and register */
> + err = counter_signals_register(counter);
> + if (err)
> + goto err_free_attributes;
> +
> + /* Verify Counts and respective Synapses are valid and register */
> + err = counter_counts_register(counter);
> + if (err)
> + goto err_free_attributes;
> +
> + /* Register Counter device extension attributes */
> + err = counter_device_ext_register(counter);
> + if (err)
> + goto err_free_attributes;
> +
> + /* Account for name attribute */
> + if (counter->name)
> + device_state->num_attr++;
> +
> + /* Allocate space for attribute pointers in attribute group */
> + device_state->attr_group.attrs = kcalloc(device_state->num_attr + 1,
> + sizeof(*device_state->attr_group.attrs), GFP_KERNEL);
> + if (!device_state->attr_group.attrs) {
> + err = -ENOMEM;
> + goto err_free_attributes;
> + }
> +
> + /* Add attribute pointers to attribute group */
> + list_for_each_entry(p, &device_state->attr_list, l)
> + device_state->attr_group.attrs[i++] = &p->dev_attr.attr;
> + if (counter->name)
> + device_state->attr_group.attrs[i] = &dev_attr_counter_name.attr;
> +
> + /* Associate attributes with device */
> + device_state->groups[0] = &device_state->attr_group;
> + device_state->dev.groups = device_state->groups;
> +
> + /* Initialize associated character device */
> + cdev_init(&device_state->chrdev, &counter_fileops);
Right now you can open and close it but nothing else. I'd get rid of the
cdev entirely unless I'm missing a later use.
> + device_state->chrdev.owner = THIS_MODULE;
> + err = cdev_device_add(&device_state->chrdev, &device_state->dev);
> + if (err)
> + goto err_free_attr_group_attrs;
> +
> + return 0;
> +
> +err_free_attr_group_attrs:
> + kfree(counter->device_state->attr_group.attrs);
> +err_free_attributes:
> + free_counter_device_state_attr_list(&counter->device_state->attr_list);
> + ida_simple_remove(&counter_ida, counter->device_state->id);
> +err_free_device_state:
> + kfree(counter->device_state);
> + return err;
> +}
> +EXPORT_SYMBOL(counter_register);
> +
> +/**
> + * counter_unregister - unregister Counter from the system
> + * @counter: pointer to Counter to unregister
> + *
> + * The Counter is unregistered from the system; all allocated memory is freed.
> + */
> +void counter_unregister(struct counter_device *const counter)
> +{
> + struct cdev *cdev;
> + struct device *dev;
> +
> + if (counter) {
> + cdev = &counter->device_state->chrdev;
> + dev = &counter->device_state->dev;
> +
> + cdev_device_del(cdev, dev);
> + put_device(dev);
> + }
> +}
> +EXPORT_SYMBOL(counter_unregister);
> +
> +static void devm_counter_unreg(struct device *dev, void *res)
> +{
> + counter_unregister(*(struct counter_device **)res);
> +}
> +
> +/**
> + * devm_counter_register - Resource-managed counter_register
> + * @dev: device to allocate counter_device for
> + * @counter: pointer to Counter to register
> + *
> + * Managed counter_register. The Counter registered with this function is
> + * automatically unregistered on driver detach. This function calls
> + * counter_register internally. Refer to that function for more information.
> + *
> + * If an Counter registered with this function needs to be unregistered
> + * separately, devm_counter_unregister must be used.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int devm_counter_register(struct device *dev,
> + struct counter_device *const counter)
> +{
> + struct counter_device **ptr;
> + int ret;
> +
> + ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr)
> + return -ENOMEM;
> +
> + ret = counter_register(counter);
> + if (!ret) {
> + *ptr = counter;
> + devres_add(dev, ptr);
> + } else
> + devres_free(ptr);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(devm_counter_register);
> +
> +static int devm_counter_match(struct device *dev, void *res, void *data)
> +{
> + struct counter_device **r = res;
> +
> + if (!r || !*r) {
> + WARN_ON(!r || !*r);
> + return 0;
> + }
> +
> + return *r == data;
> +}
> +
> +/**
> + * devm_counter_unregister - Resource-managed counter_unregister
> + * @dev: device this counter_device belongs to
> + * @counter: the Counter associated with the device
> + *
> + * Unregister Counter registered with devm_counter_register.
> + */
> +void devm_counter_unregister(struct device *dev,
> + struct counter_device *const counter)
> +{
> + int rc;
> +
> + rc = devres_release(dev, devm_counter_unreg,
> + devm_counter_match, counter);
> + WARN_ON(rc);
> +}
> +EXPORT_SYMBOL(devm_counter_unregister);
> +
> +subsys_initcall(counter_init);
> +module_exit(counter_exit);
> +
> +MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
> +MODULE_DESCRIPTION("Generic Counter interface");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
> new file mode 100644
> index 000000000000..070ed8fd53fb
> --- /dev/null
> +++ b/include/linux/iio/counter.h
> @@ -0,0 +1,239 @@
> +/*
> + * 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 _COUNTER_H_
> +#define _COUNTER_H_
> +
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/sysfs.h>
> +#include <linux/types.h>
> +
> +struct counter_device;
> +struct counter_signal;
> +
> +/**
> + * struct counter_signal_ext - Counter Signal extensions
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct counter_signal_ext {
> + const char *name;
> + ssize_t (*read)(struct counter_device *counter,
> + struct counter_signal *signal, void *priv,
> + char *buf);
> + ssize_t (*write)(struct counter_device *counter,
> + struct counter_signal *signal, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct counter_signal - Counter Signal node
> + * @id: [DRIVER] unique ID used to identify signal
> + * @name: [DRIVER] device-specific signal name
> + * @ext: [DRIVER] optional array of Counter Signal extensions
> + * @num_ext: [DRIVER] number of Counter Signal extensions specified in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct counter_signal {
> + int id;
> + const char *name;
> +
> + const struct counter_signal_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +/**
> + * struct counter_synapse - Counter Synapse node
> + * @action: [DRIVER] current action mode
> + * @actions: [DRIVER] available action modes
> + * @num_actions: [DRIVER] number of action modes specified in @actions
> + * @signal: [DRIVER] pointer to associated signal
> + */
> +struct counter_synapse {
> + size_t action;
> + const char *const *actions;
> + size_t num_actions;
> + struct counter_signal *signal;
> +};
> +
> +struct counter_count;
> +
> +/**
> + * struct counter_count_ext - Counter Count extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct counter_count_ext {
> + const char *name;
> + ssize_t (*read)(struct counter_device *counter,
> + struct counter_count *count, void *priv,
> + char *buf);
> + ssize_t (*write)(struct counter_device *counter,
> + struct counter_count *count, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct counter_count - Counter Count node
> + * @id: [DRIVER] unique ID used to identify Count
Again, drop [DRIVER]
> + * @name: [DRIVER] device-specific Count name
> + * @function: [DRIVER] current function mode
> + * @functions: [DRIVER] available function modes
> + * @num_functions: [DRIVER] number of functions specified in @functions
> + * @synapses: [DRIVER] array of synapses for initialization
> + * @num_synapses: [DRIVER] number of synapses specified in @synapses
> + * @ext: [DRIVER] optional array of Counter Count extensions
> + * @num_ext: [DRIVER] number of Counter Count extensions specified in
> + * @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct counter_count {
> + int id;
My personal view is that trying to align these always just leads to
it going wrong. I'd not do it, but it is personal taste so up to you!
> + const char *name;
> + size_t function;
> + const char *const *functions;
> + size_t num_functions;
> +
> + struct counter_synapse *synapses;
> + size_t num_synapses;
> +
> + const struct counter_count_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +/**
> + * struct counter_device_state - internal state container for a Counter device
> + * @id: unique ID used to identify the Counter
> + * @dev: internal device structure
> + * @chrdev: associated character device
> + * @attr_list: list to keep track of created Counter sysfs attributes
> + * @attr_group: Counter sysfs attributes group
> + * @groups: attribute groups
> + * @num_attr: number of Counter sysfs attributes
> + */
> +struct counter_device_state {
> + int id;
> + struct device dev;
> + struct cdev chrdev;
So far in the description I haven't seen any use of a chrdev.
Perhaps it will become clear later!
> + struct list_head attr_list;
> + struct attribute_group attr_group;
> + const struct attribute_group *groups[2];
Why 2? Docs should say at least.
> + size_t num_attr;
> +};
> +
> +/**
> + * struct counter_device_ext - Counter device extension
> + * @name: [DRIVER] attribute name
Drop the [DRIVER] tag - it's not standard and doesn't add anything here!
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct counter_device_ext {
> + const char *name;
> + ssize_t (*read)(struct counter_device *counter, void *priv,
> + char *buf);
> + ssize_t (*write)(struct counter_device *counter, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct counter_device - Counter data structure
> + * @name: [DRIVER] name of the device
> + * @parent: [DRIVER] optional parent device providing the counters
> + * @device_state: [INTERN] internal device state container
Given I made up this convention (or stole it from somewhere) I feel justified
in saying, don't use it here. Just mark the one item that is for
internal use only.
> + * @signal_read: [DRIVER] read callback for Signal attribute; may be NULL
> + * @signal_write: [DRIVER] write callback for Signal attribute; may be
> + * NULL
> + * @count_read: [DRIVER] read callback for Count attribute; may be NULL
> + * @count_write: [DRIVER] write callback for Count attribute; may be NULL
> + * @function_get: [DRIVER] function to get the current count function
> + * mode. Returns 0 on success and negative error code on
> + * error. The index of the respective Count's returned
> + * function mode should be passed back via the function
> + * parameter.
> + * @function_set: [DRIVER] function to set the count function mode.
> + * function is the index of the requested function mode
> + * from the respective Count's functions array.
> + * @action_get: [DRIVER] function to get the current action mode.
> + * Returns 0 on success and negative error code on error.
> + * The index of the respective Signal's returned action
> + * mode should be passed back via the action parameter.
> + * @action_set: [DRIVER] function to set the action mode. action is the
> + * index of the requested action mode from the respective
> + * Synapse's actions array.
> + * @signals: [DRIVER] array of Signals
> + * @num_signals: [DRIVER] number of Signals specified in @signals
> + * @counts: [DRIVER] array of Counts
> + * @num_counts: [DRIVER] number of Counts specified in @counts
> + * @ext: [DRIVER] optional array of Counter device extensions
> + * @num_ext: [DRIVER] number of Counter device extensions specified
> + * in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct counter_device {
> + const char *name;
> + struct device *parent;
> + struct counter_device_state *device_state;
> +
> + ssize_t (*signal_read)(struct counter_device *counter,
> + struct counter_signal *signal, char *buf);
> + ssize_t (*signal_write)(struct counter_device *counter,
> + struct counter_signal *signal, const char *buf,
> + size_t len);
> + ssize_t (*count_read)(struct counter_device *counter,
> + struct counter_count *count, char *buf);
> + ssize_t (*count_write)(struct counter_device *counter,
> + struct counter_count *count, const char *buf,
> + size_t len);
> + int (*function_get)(struct counter_device *counter,
> + struct counter_count *count, size_t *function);
> + int (*function_set)(struct counter_device *counter,
> + struct counter_count *count, size_t function);
> + int (*action_get)(struct counter_device *counter,
> + struct counter_count *count,
> + struct counter_synapse *synapse, size_t *action);
> + int (*action_set)(struct counter_device *counter,
> + struct counter_count *count,
> + struct counter_synapse *synapse, size_t action);
> +
> + struct counter_signal *signals;
> + size_t num_signals;
> + struct counter_count *counts;
> + size_t num_counts;
> +
> + const struct counter_device_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +extern int counter_register(struct counter_device *const counter);
> +extern void counter_unregister(struct counter_device *const counter);
> +extern int devm_counter_register(struct device *dev,
> + struct counter_device *const counter);
> +extern void devm_counter_unregister(struct device *dev,
> + struct counter_device *const counter);
> +
> +#endif /* _COUNTER_H_ */
On Thu, 14 Dec 2017 15:51:30 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch introduces the Simple Counter interface. The Simple Counter
> interface serves as an API to provide support for simple hardware
> counter devices. The Simple Counter interface is built on top of the
> Generic Counter interface.
>
> A simple hardware counter device is a counter device that has a single
> signal associated with each count value; Signals may have a value of
> "low" or "high," and edge trigger the count function which either
> increases or decreases the respective count value.
>
> The Simple Counter interface provides two count function modes:
>
> SIMPLE_COUNTER_FUNCTION_INCREASE: "increase"
> SIMPLE_COUNTER_FUNCTION_DECREASE: "decrease"
>
> The Simple Counter interface provides four action modes:
>
> SIMPLE_COUNTER_ACTION_NONE: "none"
How does this one make sense for a simple counter?
Presumably really just means "off"
> SIMPLE_COUNTER_ACTION_RISING_EDGE: "rising edge"
> SIMPLE_COUNTER_ACTION_FALLING_EDGE: "falling edge"
> SIMPLE_COUNTER_ACTION_BOTH_EDGES: "both edges"
>
> Signals may be represented by two possible states:
>
> SIMPLE_COUNTER_SIGNAL_LOW: "low"
> SIMPLE_COUNTER_SIGNAL_HIGH: "high"
>
> Since the Simple Counter interface utilizes the Generic Counter
> interface underneath, all the expected functionality of the Generic
> Counter interface such as sysfs attributes is exposed to userspace for
> end user consumption. The Simple Counter interface serves as a
> convenience API for supporting a common class of counter devices without
> the need to manually configure the more cumbersome Generic Counter
> interface for use.
>
> To use the Simple Counter interface, create an array of
> simple_counter_count structures to represent the desired counts and
> signals of the counter device, allocate a simple_counter_device
> structure and populate it with respective driver callbacks and the
> simple_counter_count array created earlier, then register the counter by
> calling the simple_counter_register function. The
> simple_counter_unregister function may be used to unregistered a
> previously registered counter.
>
> Memory-managed versions of simple_counter_register and
> simple_counter_unregister functions are provided by the
> devm_simple_counter_register and devm_simple_counter_unregister
> functions respectively.
A few little items inline.
My main concern here is it's a lot of code for a relatively small amount
of functionality. I can't immediately see how to make a large difference
on that though.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
> ---
> drivers/iio/counter/Kconfig | 4 +-
> drivers/iio/counter/Makefile | 1 +
> drivers/iio/counter/simple-counter.c | 734 +++++++++++++++++++++++++++++++++++
> include/linux/iio/counter.h | 199 ++++++++++
> 4 files changed, 937 insertions(+), 1 deletion(-)
> create mode 100644 drivers/iio/counter/simple-counter.c
>
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index 4eaf4e53c5aa..6b9a43180d2c 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -8,7 +8,9 @@ menuconfig COUNTER
> help
> Provides support for Counter devices. The Generic Counter API provides
> rudimentary support for counters and serves as building blocks to
> - create more complex counter interfaces.
> + create more complex counter interfaces. The Simple Counter API
> + provides support for simple hardware counter devices that have a
> + one-to-one mapping between their Signals and Counts.
>
> if COUNTER
>
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 513c49d832d4..7450dee97446 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -6,6 +6,7 @@
>
> obj-$(CONFIG_COUNTER) += counter.o
> counter-$(CONFIG_COUNTER) += generic-counter.o
> +counter-$(CONFIG_COUNTER) += simple-counter.o
>
> obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
> obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/simple-counter.c b/drivers/iio/counter/simple-counter.c
> new file mode 100644
> index 000000000000..e061db0860fd
> --- /dev/null
> +++ b/drivers/iio/counter/simple-counter.c
> @@ -0,0 +1,734 @@
> +/*
> + * Simple 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.
> + */
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +
> +#include <linux/iio/counter.h>
> +
> +static const char *const simple_counter_signal_level_names[] = {
> + [SIMPLE_COUNTER_SIGNAL_LOW] = "low",
> + [SIMPLE_COUNTER_SIGNAL_HIGH] = "high"
> +};
> +
> +static ssize_t simple_counter_signal_read(struct counter_device *counter_dev,
> + struct counter_signal *counter_sig, char *buf)
> +{
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_signal *const signal = counter_sig->priv;
> + int err;
> + enum simple_counter_signal_level level;
> +
> + err = counter->signal_read(counter, signal, &level);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n",
> + simple_counter_signal_level_names[level]);
> +}
> +
> +static ssize_t simple_counter_signal_write(struct counter_device *counter_dev,
> + struct counter_signal *counter_sig, const char *buf, size_t len)
> +{
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_signal *const signal = counter_sig->priv;
> + int level;
> + int err;
> +
> + level = sysfs_match_string(simple_counter_signal_level_names, buf);
> + if (level < 0)
> + return level;
> +
> + err = counter->signal_write(counter, signal, level);
> + if (err)
> + return err;
> +
> + return len;
> +}
> +
> +static ssize_t simple_counter_count_read(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, char *buf)
> +{
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_count *const count = counter_cnt->priv;
> + int err;
> + long val;
> +
> + err = counter->count_read(counter, count, &val);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
> +}
> +
> +static ssize_t simple_counter_count_write(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, const char *buf, size_t len)
> +{
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_count *const count = counter_cnt->priv;
> + int err;
> + long val;
> +
> + err = kstrtol(buf, 0, &val);
> + if (err)
> + return err;
> +
> + err = counter->count_write(counter, count, val);
> + if (err)
> + return err;
> +
> + return len;
> +}
> +
> +static int simple_counter_function_get(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, size_t *counter_func)
> +{
> + int err;
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_count *const count = counter_cnt->priv;
> + enum simple_counter_function function;
> +
> + err = counter->function_get(counter, count, &function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + *counter_func = function;
> +
> + return 0;
> +}
> +
> +static int simple_counter_function_set(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, size_t function)
> +{
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_count *const count = counter_cnt->priv;
> + int err;
> +
> + err = counter->function_set(counter, count, function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + return 0;
> +}
> +
> +static int simple_counter_action_get(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
> + size_t *counter_act)
> +{
> + int err;
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_count *const count = counter_cnt->priv;
> + enum simple_counter_action action;
> +
> + err = counter->action_get(counter, count, &action);
> + if (err)
> + return err;
> +
> + count->action = action;
> +
> + *counter_act = action;
> +
> + return 0;
> +}
> +
> +static int simple_counter_action_set(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
> + size_t action)
> +{
> + struct simple_counter_device *const counter = counter_dev->priv;
> + struct simple_counter_count *const count = counter_cnt->priv;
> + int err;
> +
> + err = counter->action_set(counter, count, action);
> + if (err)
> + return err;
> +
> + count->action = action;
> +
> + return 0;
> +}
> +
> +static ssize_t simple_counter_signal_ext_read(struct counter_device *dev,
> + struct counter_signal *signal, void *priv, char *buf)
> +{
> + const struct simple_counter_signal_ext *const ext = priv;
> + struct simple_counter_device *const counter = dev->priv;
> + struct simple_counter_signal *const simple_signal = signal->priv;
> +
> + return ext->read(counter, simple_signal, ext->priv, buf);
> +}
> +
> +static ssize_t simple_counter_signal_ext_write(struct counter_device *dev,
> + struct counter_signal *signal, void *priv, const char *buf, size_t len)
> +{
> + const struct simple_counter_signal_ext *const ext = priv;
> + struct simple_counter_device *const counter = dev->priv;
> + struct simple_counter_signal *const simple_signal = signal->priv;
> +
> + return ext->write(counter, simple_signal, ext->priv, buf, len);
> +}
> +
> +static int simple_counter_counter_signal_ext_register(
> + const struct simple_counter_signal *const simple_signal,
> + struct counter_signal *const signal)
> +{
> + const struct simple_counter_signal_ext *const simple_ext =
> + simple_signal->ext;
> + const size_t num_ext = simple_signal->num_ext;
> + struct counter_signal_ext *ext;
> + size_t i;
> +
> + /* Return early if no extensions */
> + if (!simple_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for counter_signal_ext array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register simple_counter_signal_ext via counter_signal_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = simple_ext[i].name;
> + ext[i].read = (simple_ext[i].read) ?
> + simple_counter_signal_ext_read : NULL;
> + ext[i].write = (simple_ext[i].write) ?
> + simple_counter_signal_ext_write : NULL;
> + ext[i].priv = simple_ext + i;
> + }
> +
> + /* Register Counter Signal extensions */
> + signal->ext = ext;
> + signal->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static int simple_counter_counter_signals_register(
> + const struct simple_counter_device *const counter)
> +{
> + const size_t num_counts = counter->num_counts;
> + struct counter_signal *signals;
> + const size_t num_signals = num_counts;
> + struct simple_counter_count *const simple_counts = counter->counts;
> + size_t i;
> + struct counter_signal *signal;
> + struct simple_counter_signal *simple_signal;
> + int err;
> + struct counter_device *const counter_dev = counter->counter_dev;
> +
> + /* Allocate space for signals array */
> + signals = kcalloc(num_signals, sizeof(*signals), GFP_KERNEL);
> + if (!signals)
> + return -ENOMEM;
> +
> + /* Configure Signals */
> + for (i = 0; i < num_signals; i++) {
> + signal = signals + i;
> + simple_signal = &simple_counts[i].signal;
> +
> + signal->id = simple_signal->id;
> + signal->name = simple_signal->name;
> + signal->priv = simple_signal;
> +
> + /* Register Counter Signal extensions */
> + err = simple_counter_counter_signal_ext_register(simple_signal,
> + signal);
> + if (err)
> + goto err_free_signals;
> + }
> +
> + /* Register Signals to Counter device container */
> + counter_dev->signals = signals;
> + counter_dev->num_signals = num_signals;
> +
> + return 0;
> +
> +err_free_signals:
> + while (i--)
> + kfree(signals[i].ext);
> + kfree(signals);
> + return err;
> +}
> +
> +static const char *const simple_counter_function_names[] = {
> + [SIMPLE_COUNTER_FUNCTION_INCREASE] = "increase",
> + [SIMPLE_COUNTER_FUNCTION_DECREASE] = "decrease"
> +};
> +
> +static const char *const simple_counter_action_names[] = {
> + [SIMPLE_COUNTER_ACTION_NONE] = "none",
> + [SIMPLE_COUNTER_ACTION_RISING_EDGE] = "rising edge",
> + [SIMPLE_COUNTER_ACTION_FALLING_EDGE] = "falling edge",
> + [SIMPLE_COUNTER_ACTION_BOTH_EDGES] = "both edges"
> +};
> +
> +static int simple_counter_counter_synapse_register(
> + struct counter_signal *const signal, struct counter_count *const count)
> +{
> + struct counter_synapse *synapse;
> +
> + /* Allocate space for Counter Synapse */
> + synapse = kzalloc(sizeof(*synapse), GFP_KERNEL);
> + if (!synapse)
> + return -ENOMEM;
> +
> + /* Configure Synapse */
> + synapse->signal = signal;
> + synapse->actions = simple_counter_action_names;
> + synapse->num_actions = ARRAY_SIZE(simple_counter_action_names);
> +
> + /* Register Counter Synapse */
> + count->synapses = synapse;
> + count->num_synapses = 1;
> +
> + return 0;
> +}
> +
> +static ssize_t simple_counter_count_ext_read(struct counter_device *dev,
> + struct counter_count *count, void *priv, char *buf)
> +{
> + const struct simple_counter_count_ext *const ext = priv;
> + struct simple_counter_device *const counter = dev->priv;
> + struct simple_counter_count *const simple_count = count->priv;
> +
> + return ext->read(counter, simple_count, ext->priv, buf);
> +}
> +
> +static ssize_t simple_counter_count_ext_write(struct counter_device *dev,
> + struct counter_count *count, void *priv, const char *buf, size_t len)
> +{
> + const struct simple_counter_count_ext *const ext = priv;
> + struct simple_counter_device *const counter = dev->priv;
> + struct simple_counter_count *const simple_count = count->priv;
> +
> + return ext->write(counter, simple_count, ext->priv, buf, len);
> +}
> +
> +static int simple_counter_counter_count_ext_register(
> + const struct simple_counter_count *const simple_count,
> + struct counter_count *const count)
> +{
> + const struct simple_counter_count_ext *const simple_ext =
> + simple_count->ext;
> + const size_t num_ext = simple_count->num_ext;
> + struct counter_count_ext *ext;
> + size_t i;
> +
> + /* Return early if no extensions */
> + if (!simple_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for Counter Count extensions array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register simple_counter_count_ext via counter_count_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = simple_ext[i].name;
> + ext[i].read = (simple_ext[i].read) ?
> + simple_counter_count_ext_read : NULL;
> + ext[i].write = (simple_ext[i].write) ?
> + simple_counter_count_ext_write : NULL;
> + ext[i].priv = simple_ext + i;
> + }
> +
> + /* Register Counter Count extensions */
> + count->ext = ext;
> + count->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static void simple_counter_counter_synapse_unregister(
> + const struct counter_count *const count)
> +{
> + kfree(count->synapses);
> +}
> +
> +static int simple_counter_counter_count_init(
> + struct counter_count *const count,
> + struct simple_counter_count *const simple_count,
> + struct counter_signal *const signal)
> +{
> + int err;
> +
> + count->id = simple_count->id;
> + count->name = simple_count->name;
> + count->functions = simple_counter_function_names;
> + count->num_functions = ARRAY_SIZE(simple_counter_function_names);
> + count->priv = simple_count;
> +
> + /* Register Counter Synapse */
> + err = simple_counter_counter_synapse_register(signal, count);
> + if (err)
> + return err;
> +
> + /* Register Counter Count extensions */
> + err = simple_counter_counter_count_ext_register(simple_count, count);
> + if (err)
> + goto err_unregister_synapse;
> +
> + return 0;
> +
> +err_unregister_synapse:
> + simple_counter_counter_synapse_unregister(count);
> + return err;
> +}
> +
> +static void simple_counter_counter_count_ext_unregister(
> + const struct counter_count *const count)
> +{
> + kfree(count->ext);
> +}
> +
> +static void simple_counter_counter_count_free(
> + const struct counter_count *const count)
> +{
> + simple_counter_counter_count_ext_unregister(count);
> + simple_counter_counter_synapse_unregister(count);
> +}
> +
> +static int simple_counter_counter_counts_register(
> + const struct simple_counter_device *const counter)
> +{
> + struct counter_device *const counter_dev = counter->counter_dev;
> + struct counter_count *counts;
> + const size_t num_counts = counter->num_counts;
> + size_t i;
> + struct simple_counter_count *const simple_counts = counter->counts;
> + struct counter_signal *const signals = counter_dev->signals;
> + int err;
> +
> + /* Allocate space for counts array */
> + counts = kcalloc(num_counts, sizeof(*counts), GFP_KERNEL);
> + if (!counts)
> + return -ENOMEM;
> +
> + /* Initialize Counts */
> + for (i = 0; i < num_counts; i++) {
> + err = simple_counter_counter_count_init(counts + i,
> + simple_counts + i, signals + i);
> + if (err)
> + goto err_free_counts;
> + }
> +
> + /* Register Counts to Counter device container */
> + counter_dev->counts = counts;
> + counter_dev->num_counts = num_counts;
> +
> + return 0;
> +
> +err_free_counts:
> + while (i--)
> + simple_counter_counter_count_free(counts + i);
> + kfree(counts);
> + return err;
> +}
> +
> +static void simple_counter_counter_signals_unregister(
> + const struct counter_device *const counter_dev)
> +{
> + const struct counter_signal *const signals = counter_dev->signals;
> + size_t num_signals = counter_dev->num_signals;
> +
> + while (num_signals--)
> + kfree(signals[num_signals].ext);
> + kfree(signals);
> +}
> +
> +static int simple_counter_counts_register(
> + struct simple_counter_device *const counter)
> +{
> + const struct simple_counter_count *const simple_counts =
> + counter->counts;
> + const size_t num_counts = counter->num_counts;
> + int err;
> +
> + /* At least one Count must be defined */
> + if (!simple_counts || !num_counts) {
> + pr_err("simple-counter: Simple Counter Counts undefined\n");
> + return -EINVAL;
> + }
> +
> + /* Register Counter Signals */
> + err = simple_counter_counter_signals_register(counter);
> + if (err)
> + return err;
> +
> + /* Register Counter Counts */
> + err = simple_counter_counter_counts_register(counter);
> + if (err)
> + goto err_unregister_signals;
> +
> + return 0;
> +
> +err_unregister_signals:
> + simple_counter_counter_signals_unregister(counter->counter_dev);
> + return err;
> +}
> +
> +static ssize_t simple_counter_device_ext_read(struct counter_device *dev,
> + void *priv, char *buf)
> +{
> + const struct simple_counter_device_ext *const ext = priv;
> + struct simple_counter_device *const counter = dev->priv;
> +
> + return ext->read(counter, ext->priv, buf);
> +}
> +
> +static ssize_t simple_counter_device_ext_write(struct counter_device *dev,
> + void *priv, const char *buf, size_t len)
> +{
> + const struct simple_counter_device_ext *const ext = priv;
> + struct simple_counter_device *const counter = dev->priv;
> +
> + return ext->write(counter, ext->priv, buf, len);
> +}
> +
> +static int simple_counter_device_ext_register(
> + struct simple_counter_device *const counter)
> +{
> + const struct simple_counter_device_ext *const simple_ext = counter->ext;
> + const size_t num_ext = counter->num_ext;
> + struct counter_device_ext *ext;
> + size_t i;
> + struct counter_device *const counter_dev = counter->counter_dev;
> +
> + /* Return early if no extensions */
> + if (!simple_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for Counter device extensions array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register simple_counter_device_ext via counter_device_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = simple_ext[i].name;
> + ext[i].read = (simple_ext[i].read) ?
> + simple_counter_device_ext_read : NULL;
> + ext[i].write = (simple_ext[i].write) ?
> + simple_counter_device_ext_write : NULL;
> + ext[i].priv = simple_ext + i;
> + }
> +
> + /* Register Counter device extensions */
> + counter_dev->ext = ext;
> + counter_dev->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static void simple_counter_counter_counts_unregister(
> + const struct counter_device *const counter)
> +{
> + const struct counter_count *const counts = counter->counts;
> + size_t num_counts = counter->num_counts;
> +
> + while (num_counts--)
> + simple_counter_counter_count_free(counts + num_counts);
> + kfree(counts);
> +}
> +
> +static void simple_counter_counts_unregister(
> + const struct simple_counter_device *const counter)
> +{
> + const struct counter_device *const counter_dev = counter->counter_dev;
> +
> + simple_counter_counter_counts_unregister(counter_dev);
> + simple_counter_counter_signals_unregister(counter_dev);
> +}
> +
> +/**
> + * simple_counter_register - register Simple Counter to the system
> + * @counter: pointer to Simple Counter to register
> + *
> + * This function registers a Simple Counter to the system. A sysfs "counter"
> + * directory will be created and populated with sysfs attributes correlating
> + * with the Simple Counter Signals, Synapses, and Counts respectively.
> + */
> +int simple_counter_register(struct simple_counter_device *const counter)
> +{
> + struct counter_device *counter_dev;
> + int err;
> +
> + if (!counter)
> + return -EINVAL;
> +
> + /* Allocate internal Counter container */
> + counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL);
> + if (!counter)
> + return -ENOMEM;
> + counter->counter_dev = counter_dev;
> +
> + /* Configure internal Counter */
> + counter_dev->name = counter->name;
> + counter_dev->parent = counter->parent;
> + counter_dev->signal_read = (counter->signal_read) ?
> + simple_counter_signal_read : NULL;
Ah. This will make my below comment on taking this const
tricky (at least for the generic counter).
What do you loose if you take the null check into the simple_counter
wrappers rather than here?
> + counter_dev->signal_write = (counter->signal_write) ?
> + simple_counter_signal_write : NULL;
> + counter_dev->count_read = (counter->count_read) ?
> + simple_counter_count_read : NULL;
> + counter_dev->count_write = (counter->count_write) ?
> + simple_counter_count_write : NULL;
> + counter_dev->function_get = (counter->function_get) ?
> + simple_counter_function_get : NULL;
> + counter_dev->function_set = (counter->function_set) ?
> + simple_counter_function_set : NULL;
> + counter_dev->action_get = (counter->action_get) ?
> + simple_counter_action_get : NULL;
> + counter_dev->action_set = (counter->action_set) ?
> + simple_counter_action_set : NULL;
> + counter_dev->priv = counter;
> +
> + /* Register Simple Counter Counts */
> + err = simple_counter_counts_register(counter);
> + if (err)
> + goto err_free_counter_dev;
> +
> + /* Register Simple Counter device extension attributes */
> + err = simple_counter_device_ext_register(counter);
> + if (err)
> + goto err_unregister_counts;
> +
> + /* Register internal Counter to the system */
> + err = counter_register(counter_dev);
> + if (err)
> + goto err_free_ext;
> +
> + return 0;
> +
> +err_free_ext:
> + kfree(counter_dev->ext);
> +err_unregister_counts:
> + simple_counter_counts_unregister(counter);
> +err_free_counter_dev:
> + kfree(counter_dev);
> + return err;
> +}
> +EXPORT_SYMBOL(simple_counter_register);
> +
> +/**
> + * simple_counter_unregister - unregister Simple Counter from the system
> + * @counter: pointer to Simple Counter to unregister
> + *
> + * The Simple Counter is unregistered from the system; all allocated memory is
> + * freed.
> + */
> +void simple_counter_unregister(struct simple_counter_device *const counter)
> +{
> + struct counter_device *counter_dev;
> +
> + if (!counter)
> + return;
> +
> + counter_dev = counter->counter_dev;
> +
> + counter_unregister(counter_dev);
> +
> + kfree(counter_dev->ext);
> + simple_counter_counts_unregister(counter);
> + kfree(counter_dev);
> +}
> +EXPORT_SYMBOL(simple_counter_unregister);
> +
> +static void devm_simple_counter_unreg(struct device *dev, void *res)
> +{
> + simple_counter_unregister(*(struct simple_counter_device **)res);
> +}
> +
> +/**
> + * devm_simple_counter_register - Resource-managed simple_counter_register
> + * @dev: device to allocate simple_counter_device for
> + * @counter: pointer to Simple Counter to register
> + *
> + * Managed simple_counter_register. The Simple Counter registered with this
> + * function is automatically unregistered on driver detach. This function calls
> + * simple_counter_register internally. Refer to that function for more
> + * information.
> + *
> + * If an Simple Counter registered with this function needs to be unregistered
> + * separately, devm_simple_counter_unregister must be used.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int devm_simple_counter_register(struct device *dev,
> + struct simple_counter_device *const counter)
> +{
> + struct simple_counter_device **ptr;
> + int ret;
> +
> + ptr = devres_alloc(devm_simple_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr)
> + return -ENOMEM;
> +
> + ret = simple_counter_register(counter);
> + if (!ret) {
> + *ptr = counter;
> + devres_add(dev, ptr);
> + } else
> + devres_free(ptr);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(devm_simple_counter_register);
> +
> +static int devm_simple_counter_match(struct device *dev, void *res, void *data)
> +{
> + struct simple_counter_device **r = res;
> +
> + if (!r || !*r) {
> + WARN_ON(!r || !*r);
> + return 0;
> + }
> +
> + return *r == data;
> +}
> +
> +/**
> + * devm_simple_counter_unregister - Resource-managed simple_counter_unregister
> + * @dev: device this simple_counter_device belongs to
> + * @counter: the Simple Counter associated with the device
> + *
> + * Unregister Simple Counter registered with devm_simple_counter_register.
> + */
> +void devm_simple_counter_unregister(struct device *dev,
> + struct simple_counter_device *const counter)
> +{
> + int rc;
> +
> + rc = devres_release(dev, devm_simple_counter_unreg,
> + devm_simple_counter_match, counter);
> + WARN_ON(rc);
> +}
> +EXPORT_SYMBOL(devm_simple_counter_unregister);
> +
> +MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
> +MODULE_DESCRIPTION("Simple Counter interface");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
> index 070ed8fd53fb..0967ea2a9bef 100644
> --- a/include/linux/iio/counter.h
> +++ b/include/linux/iio/counter.h
> @@ -236,4 +236,203 @@ extern int devm_counter_register(struct device *dev,
> extern void devm_counter_unregister(struct device *dev,
> struct counter_device *const counter);
>
> +struct simple_counter_device;
> +struct simple_counter_signal;
> +
> +/**
> + * struct simple_counter_signal_ext - Simple Counter Signal extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct simple_counter_signal_ext {
> + const char *name;
> + ssize_t (*read)(struct simple_counter_device *counter,
> + struct simple_counter_signal *signal,
> + void *priv, char *buf);
> + ssize_t (*write)(struct simple_counter_device *counter,
> + struct simple_counter_signal *signal,
> + void *priv, const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct simple_counter_signal - Simple Counter Signal node
> + * @id: [DRIVER] unique ID used to identify signal
> + * @name: [DRIVER] device-specific signal name
> + * @ext: [DRIVER] optional array of Simple Counter Signal extensions
> + * @num_ext: [DRIVER] number of Simple Counter Signal extensions specified in
> + * @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct simple_counter_signal {
> + int id;
> + const char *name;
> +
> + const struct simple_counter_signal_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +enum simple_counter_signal_level {
> + SIMPLE_COUNTER_SIGNAL_LOW = 0,
> + SIMPLE_COUNTER_SIGNAL_HIGH
> +};
> +
> +struct simple_counter_count;
> +
> +enum simple_counter_function {
> + SIMPLE_COUNTER_FUNCTION_INCREASE = 0,
> + SIMPLE_COUNTER_FUNCTION_DECREASE
> +};
> +
> +enum simple_counter_action {
> + SIMPLE_COUNTER_ACTION_NONE = 0,
> + SIMPLE_COUNTER_ACTION_RISING_EDGE,
> + SIMPLE_COUNTER_ACTION_FALLING_EDGE,
> + SIMPLE_COUNTER_ACTION_BOTH_EDGES
> +};
> +
> +/**
> + * struct simple_counter_count_ext - Simple Counter Count extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct simple_counter_count_ext {
> + const char *name;
> + ssize_t (*read)(struct simple_counter_device *counter,
> + struct simple_counter_count *count, void *priv,
> + char *buf);
> + ssize_t (*write)(struct simple_counter_device *counter,
> + struct simple_counter_count *count, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct simple_counter_count - Simple Counter Count node
> + * @id: [DRIVER] unique ID used to identify Count
> + * @name: [DRIVER] device-specific Count name
> + * @function: [DRIVER] current function mode
> + * @action: [DRIVER] current action mode
> + * @signal: [DRIVER] associated signal
> + * @ext: [DRIVER] optional array of Simple Counter Count extensions
> + * @num_ext: [DRIVER] number of Simple Counter Count extensions specified in
> + * @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct simple_counter_count {
> + int id;
> + const char *name;
> + enum simple_counter_function function;
> + enum simple_counter_action action;
> +
> + struct simple_counter_signal signal;
> +
> + const struct simple_counter_count_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +/**
> + * struct simple_counter_device_ext - Simple Counter device extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct simple_counter_device_ext {
> + const char *name;
> + ssize_t (*read)(struct simple_counter_device *counter,
> + void *priv, char *buf);
> + ssize_t (*write)(struct simple_counter_device *counter,
> + void *priv, const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct simple_counter_device - Simple Counter data structure
> + * @name: [DRIVER] name of the device
> + * @parent: [DRIVER] optional parent device providing the counters
> + * @counter_dev: [INTERN] internal Counter container
Same comment as before. Just mark the on the drive shouldn't play with.
> + * @signal_read: [DRIVER] read callback for Signal attribute; may be
> + * NULL. Returns 0 on success and negative error code on
> + * error. The respective Signal's returned level should be
> + * passed back via the level parameter.
> + * @signal_write: [DRIVER] write callback for Signal attribute; may be
> + * NULL
> + * @count_read: [DRIVER] read callback for Count attribute; may be NULL.
> + * Returns 0 on success and negative error code on error.
> + * The respective Count's returned value should be passed
> + * back via the val parameter.
> + * @count_write: [DRIVER] write callback for Count attribute; may be NULL
> + * @function_get: [DRIVER] function to get the current count function
> + * mode. Returns 0 on success and negative error code on
> + * error. The respective Count's returned function mode
> + * should be passed back via the function parameter.
> + * @function_set: [DRIVER] function to set the count function mode
> + * @action_get: [DRIVER] function to get the current action mode.
> + * Returns 0 on success and negative error code on error.
> + * The respective Signal's returned action mode should be
> + * passed back via the action parameter.
> + * @action_set: [DRIVER] function to set the action mode
> + * @counts: [DRIVER] array of Simple Counter Counts
> + * @num_counts: [DRIVER] number of Simple Counter Counts specified in
> + * @counts
> + * @ext: [DRIVER] optional array of Simple Counter device
> + * extensions
> + * @num_ext: [DRIVER] number of Simple Counter device extensions
> + * specified in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct simple_counter_device {
> + const char *name;
> + struct device *parent;
> + struct counter_device *counter_dev;
> +
Not sure why I didn't notice before, but presumably all the below
are effectively const function pointers?
Lift them out to an ops structure as that'll let the order randomization
stuff play with them safely (security measure). Also lets
us take a large block of this constant.
Adds a small level of indirection but worth it for the gains.
> + int (*signal_read)(struct simple_counter_device *counter,
> + struct simple_counter_signal *signal,
> + enum simple_counter_signal_level *level);
> + int (*signal_write)(struct simple_counter_device *counter,
> + struct simple_counter_signal *signal,
> + enum simple_counter_signal_level level);
> + int (*count_read)(struct simple_counter_device *counter,
> + struct simple_counter_count *count, long *val);
> + int (*count_write)(struct simple_counter_device *counter,
> + struct simple_counter_count *count, long val);
> + int (*function_get)(struct simple_counter_device *counter,
> + struct simple_counter_count *count,
> + enum simple_counter_function *function);
> + int (*function_set)(struct simple_counter_device *counter,
> + struct simple_counter_count *count,
> + enum simple_counter_function function);
> + int (*action_get)(struct simple_counter_device *counter,
> + struct simple_counter_count *count,
> + enum simple_counter_action *action);
> + int (*action_set)(struct simple_counter_device *counter,
> + struct simple_counter_count *count,
> + enum simple_counter_action action);
> +
> + struct simple_counter_count *counts;
> + size_t num_counts;
> +
> + const struct simple_counter_device_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +extern int simple_counter_register(struct simple_counter_device *const counter);
> +extern void simple_counter_unregister(
> + struct simple_counter_device *const counter);
> +extern int devm_simple_counter_register(struct device *dev,
> + struct simple_counter_device *const counter);
> +extern void devm_simple_counter_unregister(struct device *dev,
> + struct simple_counter_device *const counter);
> +
> #endif /* _COUNTER_H_ */
On Thu, 14 Dec 2017 15:51:43 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch adds standard documentation for the userspace sysfs
> attributes of the Simple Counter interface.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
> ---
> .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++++++++++++++++++++++
> 1 file changed, 61 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs b/Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
> new file mode 100644
> index 000000000000..e1f32c64c667
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
> @@ -0,0 +1,61 @@
> +What: /sys/bus/counter/devices/counterX/countY
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Count data of Count Y. This is a signed integer value that
> + represents the accumulated count.
> +
> +What: /sys/bus/counter/devices/counterX/countY_function
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Count function mode of Count Y. Count function evaluation is
> + triggered by conditions specified by the countY_signalZ_action
> + attributes. Two count function modes are available: increase and
> + decrease.
> +
> + Increase:
> + Accumulated count is incremented.
> +
> + Decrease:
> + Accumulated count is decremented.
Ah, so the specifics are in these files. Fair enough I suppose though in that
case. Perhaps add a note where relevant in the generic file to say this.
> +
> +What: /sys/bus/counter/devices/counterX/countY_max
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Count Y count data maximum value.
What does this mean? Does it wrap here, or stop here?
Needs defining.
> +
> +What: /sys/bus/counter/devices/counterX/countY_min
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Count Y count data minimum value.
> +
> +What: /sys/bus/counter/devices/counterX/countY_signalZ_action
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Action mode of Count Y for Signal Z. This attribute indicates
> + the condition of Signal Z that triggers the count function
> + evaluation for Count Y. Four action modes are available: none,
> + rising edge, falling edge, and both edges.
I'd put the values in quotes to make it easy to isolate them.
> +
> + None:
> + Signal does not trigger the count function.
> +
> + Rising Edge:
> + Low state transitions to High state.
> +
> + Falling Edge:
> + High state transitions to Low state.
> +
> + Both Edges:
> + Any state transition.
> +
> +What: /sys/bus/counter/devices/counterX/signalY
> +KernelVersion: 4.16
> +Contact: [email protected]
> +Description:
> + Signal data of Signal Y. This is the respective input level
> + represented by two available states: low and high.
On Thu, 14 Dec 2017 15:51:55 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch adds high-level documentation about the Simple Counter
> interface.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
On small suggestion inline.
Jonathan
> ---
> Documentation/driver-api/iio/index.rst | 1 +
> Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++++++++++++++++++
> 2 files changed, 394 insertions(+)
> create mode 100644 Documentation/driver-api/iio/simple-counter.rst
>
> diff --git a/Documentation/driver-api/iio/index.rst b/Documentation/driver-api/iio/index.rst
> index e6c5b75c2e03..60e17f16cfb8 100644
> --- a/Documentation/driver-api/iio/index.rst
> +++ b/Documentation/driver-api/iio/index.rst
> @@ -16,3 +16,4 @@ Contents:
> triggers
> triggered-buffers
> generic-counter
> + simple-counter
> diff --git a/Documentation/driver-api/iio/simple-counter.rst b/Documentation/driver-api/iio/simple-counter.rst
> new file mode 100644
> index 000000000000..fa34f689e99f
> --- /dev/null
> +++ b/Documentation/driver-api/iio/simple-counter.rst
> @@ -0,0 +1,393 @@
> +========================
> +Simple Counter Interface
> +========================
> +
> +Introduction
> +============
> +
> +The most basic counter device may be expressed as a single Count
> +associated with a single Signal via a single Synapse. This is a popular
> +type of Counter implemented by hardware devices, and also what commonly
> +comes to mind when one thinks of a "counter device." This driver API
> +provides a basic Counter interface and standard of interaction and
> +exposure for these simple counter devices. The Simple Counter interface
> +enables drivers to support and expose simple counter devices in a more
> +apt and well-defined way, without the hassles and imprecisions of
> +utilizing a more generic interface.
> +
> +Theory
> +======
> +
> +The Simple Counter interface may be considered a subclass of the
> +Generic Counter interface; the same paradigm core components apply:
> +Counts, Signals, and Synapses. However, the Simple Counter interface
> +goes a bit further and defines aspects of those core components to
> +properly represent Simple Counter devices.
> +
> +The three core components of a Simple Counter:
> +
> + COUNT
> + -----
> + A Count represents the count data for a set of Signals. The
> + count data for a Simple Counter is a signed integer representing
> + the accumulated count of Simple Signal action conditions.
> +
> + A Count has a count function mode which represents the update
> + behavior for the count data. The following two count function
> + modes are possible for a Simple Count:
> +
> + * Increase:
> + Accumulated count is incremented.
> + * Decrease:
> + Accumulated count is decremented.
> +
> + A Simple Count has one associated Simple Signal.
> +
> + SIGNAL
> + ------
> + A Signal represents a counter input data; this is the data that
> + is typically analyzed by the counter to determine the count
> + data. A Simple Signal represents a counter input line with two
> + possible states:
> +
> + * Low
> + * High
> +
> + A Simple Signal is associated to a Simple Count.
Might be worth noting that there may be no visibility of this to software.
> +
> + SYNAPSE
> + -------
> + A Synapse represents the association of a Signal with a
> + respective Count. Signal data affects respective Count data, and
> + the Synapse represents this relationship.
> +
> + The Synapse action mode specifies the Signal data condition
> + which triggers the respective Count's count function evaluation
> + to update the count data. There are four possible action modes
> + for a Simple Counter:
> +
> + * None:
> + Signal does not trigger the count function.
> + * Rising Edge:
> + Low state transitions to High state.
> + * Falling Edge:
> + High state transitions to Low state.
> + * Both Edges:
> + Any state transition.
> +
> +Paradigm
> +========
> +
> +Simple Counter devices consist of a single Count associated with a
> +single Signal via a single Synapse. Take for example a counter device
> +which simply accumulates a count of rising edges on a source input line.
> +
> + Count Synapse Signal
> + ----- ------- ------
> ++---------------------+
> +| Data: Count | Rising Edge ________
> +| Function: Increase | <------------- / Source \
> +| | ____________
> ++---------------------+
> +
> +In this example, the Signal is a source input line with a pulsing
> +voltage, while the Count is a persistent count value which is repeatedly
> +incremented. The Signal is associated with the respective Count via a
> +Synapse. The increase function is triggered by the Signal data condition
> +specified by the Synapse -- in this case a rising edge condition on the
> +voltage input line.
> +
> +In summary, the simple counter device existence and behavior is aptly
> +represented by respective Count, Signal, and Synapse components: e.g. a
> +rising edge condition triggers an increase function on an accumulating
> +count datum.
> +
> +Userspace Interface
> +===================
> +
> +Several sysfs attributes are generated by the Simple Counter interface,
> +and reside under the /sys/bus/counter/devices/counterX directory, where
> +counterX refers to the respective counter device. Please see
> +Documentation/ABI/testing/sys-bus-counter-simple-sysfs for detailed
> +information on each Simple Counter interface sysfs attribute.
> +
> +In addition, several sysfs attributes are generated by the underlying
> +Generic Counter interface, and also reside under the
> +/sys/bus/counter/devices/counterX directory, where counterX refers to
> +the respective counter device. Please see
> +Documentation/ABI/testing/sys-bus-counter-generic-sysfs for detailed
> +information on each Generic Counter interface sysfs attribute.
> +
> +Through these sysfs attributes, programs and scripts may interact with
> +the Simple Counter paradigm Counts, Signals, and Synapses of respective
> +counter devices.
> +
> +Driver API
> +==========
> +
> +Driver authors may utilize the Simple Counter interface in their code
> +by including the include/linux/iio/counter.h header file.
I would have a separate header file for the simple counter that
itself includes counter.h. Thus devices not using simple counter don't
get to see its functions etc. You might not even build it for example.
> This header
> +file provides several core data structures and function prototypes for
> +defining a simple counter.
> +
> +
> +struct simple_counter_signal_ext
> +--------------------------------
> +
> +This structure defines a Simple Counter Signal extension attribute.
> +These attributes expose auxiliary configuration and functionality
> +specific to the respective Simple Counter Signal.
> +
> + name: attribute name
> + read: read callback for this attribute; may be NULL
> + write: write callback for this attribute; may be NULL
> + priv: data private to the driver
> +
> +struct simple_counter_signal
> +----------------------------
> +
> +This structure defines a Simple Counter Signal component. Typically,
> +this will correlate with an input channel on a physical counter device.
> +This structure is the simplest of the Simple Counter paradigm core
> +components to define with only two structure members which require
> +explicit configuration:
> +
> + id: unique ID used to identify signal
> + name: device-specific signal name
> + ext: optional array of Simple Counter Signal
> + extensions
> + num_ext: number of Simple Counter Signal extensions
> + specified in @ext
> + priv: optional private data supplied by driver
> +
> +enum simple_counter_signal_level
> +--------------------------------
> +
> +This enum defines enumeration constants to represent the possible Simple
> +Signal data level states.
> +
> + SIMPLE_COUNTER_SIGNAL_LOW: Low
> + SIMPLE_COUNTER_SIGNAL_HIGH: High
> +
> +enum simple_counter_function
> +----------------------------
> +
> +This enum defines enumeration constants to represent the possible Simple
> +Counter count function modes.
> +
> + SIMPLE_COUNTER_FUNCTION_INCREASE: Increase
> + SIMPLE_COUNTER_FUNCTION_DECREASE: Decrease
> +
> +enum simple_counter_action
> +--------------------------
> +
> +This enum defines enumeration constants to represent the possible Simple
> +Counter action modes.
> +
> + SIMPLE_COUNTER_ACTION_NONE: None
> + SIMPLE_COUNTER_ACTION_RISING_EDGE: Rising Edge
> + SIMPLE_COUNTER_ACTION_FALLING_EDGE: Falling Edge
> + SIMPLE_COUNTER_ACTION_BOTH_EDGES: Both Edges
> +
> +struct simple_counter_count_ext
> +-------------------------------
> +
> +This structure defines a Simple Counter Count extension attribute. These
> +attributes expose auxiliary configuration and functionality specific to
> +the respective Simple Counter Count.
> +
> + name: attribute name
> + read: read callback for this attribute; may be NULL
> + write: write callback for this attribute; may be NULL
> + priv: data private to the driver
> +
> +struct simple_counter_count
> +---------------------------
> +
> +This structure defines a Simple Counter Count 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, and the Simple Signal associated with this
> +Simple Count.
> +
> + id: unique ID used to identify Count
> + name: device-specific Count name
> + function: current function mode
> + action: current action mode
> + signal: associated signal
> + ext: optional array of Simple Counter Count
> + extensions
> + num_ext: number of Simple Counter Count extensions
> + specified in @ext
> + priv: optional private data supplied by driver
> +
> +struct simple_counter_device_ext
> +--------------------------------
> +
> +This structure defines a Simple Counter extension attribute. These
> +attributes expose auxiliary configuration and functionality specific to
> +the respective Simple Counter.
> +
> + name: attribute name
> + read: read callback for this attribute; may be NULL
> + write: write callback for this attribute; may be NULL
> + priv: data private to the driver
> +
> +struct simple_counter_device
> +----------------------------
> +
> +This is the main data structure for a Simple Counter device. Access to
> +all respective Counts, Signals, and Synapses is possible from this
> +structure. This structure requires the configuration of a name and
> +Simple Counter Counts:
> +
> + name: name of the device
> + parent: optional parent device providing the counters
> + signal_read: read callback for Signal attribute; may be NULL.
> + Returns 0 on success and negative error code on
> + error. The respective Signal's returned level
> + should be passed back via the level parameter.
> + signal_write: write callback for Signal attribute; may be NULL
> + count_read: read callback for Count attribute; may be NULL.
> + Returns 0 on success and negative error code on
> + error. The respective Count's returned value
> + should be passed back via the val parameter.
> + count_write: write callback for Count attribute; may be NULL
> + function_get: function to get the current count function mode.
> + Returns 0 on success and negative error code on
> + error. The respective Count's returned function
> + mode should be passed back via the function
> + parameter.
> + function_set: function to set the count function mode
> + action_get: function to get the current action mode. Returns
> + 0 on success and negative error code on error.
> + The respective Signal's returned action mode
> + should be passed back via the action parameter.
> + action_set: function to set the action mode
> + counts: array of Simple Counter Counts
> + num_counts: number of Simple Counter Counts specified in
> + @counts
> + ext: optional array of Simple Counter device
> + extensions
> + num_ext: number of Simple Counter device extensions
> + specified in @ext
> + priv: optional private data supplied by driver
> +
> +Registration functions
> +----------------------
> +
> +A simple counter device is registered to the system by passing the
> +respective initialized simple_counter_device structure to the
> +simple_counter_register function; similarly, the
> +simple_counter_unregister function unregisters the respective
> +Simple Counter. The devm_simple_counter_register and
> +devm_simple_counter_unregister functions serve as device memory-managed
> +versions of the simple_counter_register and simple_counter_unregister
> +functions respectively.
> +
> +Implementation
> +==============
> +
> +To use the Simple Counter interface, create an array of
> +simple_counter_count structures to represent the desired Counts and
> +Signals of the counter device. The simple_counter_count structure has a
> +Simple Signal member that represents the associated Signal, so only the
> +Simple Counts need to be explicited allocated -- the associated Simple
> +Signal members are simply populated with the respective desired
> +definitions. The defined simple_counter_count array may then be added
> +to a simple_counter_device structure for registration to the system.
> +
> + Simple Count Count Signal
> + ------------ ----- ------
> ++---------------------+ +--------------------+
> +| Data: Count | ---> | Data: Count |
> +| Function: Increase | | Function: Increase |
> +| | +--------------------+
> +| |
> ++---------------------+ ________
> +| Signal: Source | ----------------------------> / Source \
> +| | ____________
> ++---------------------+
> +
> +Driver callbacks should be provided to the simple_counter_device
> +structure in order to communicate with the device: to read and write
> +various Signals and Counts, and to set and get the "action mode" and
> +"function mode" for various Synapses and Counts respectively.
> +
> +A defined simple_counter_device structure may be registered to the
> +system by passing it to the simple_counter_register function, and
> +unregistered by passing it to the simple_counter_unregister function.
> +Similarly, the devm_simple_counter_register and
> +devm_simple_counter_unregister functions may be used if device
> +memory-managed registration is desired.
> +
> +Extension sysfs attributes can be created for auxiliary functionality
> +and data by passing in defined simple_counter_device_ext,
> +simple_counter_count_ext, and simple_counter_signal_ext structures. In
> +these cases, the simple_counter_device_ext structure is used for global
> +configuration of the respective Counter device, while the
> +simple_counter_count_ext and simple_counter_signal_ext structures allow
> +for auxiliary exposure and configuration of a specific Count or Signal
> +respectively.
> +
> +Architecture
> +============
> +
> +The Simple Counter interface is a subclass of the Generic Counter
> +interface, and the Simple Counter interface functions serve as mappings
> +onto the Generic Counter interface functions to simplify and aptly
> +define support for simple counter devices.
> +
> +In this vein, the Generic Counter interface functions are ultimately
> +called when the Simple Counter interface functions are utilized. For
> +more information on the architecture of the Generic Counter interface,
> +please refer to the Documentation/driver-api/iio/generic-counter.rst
> +file.
> +
> +Simple Counter devices are registered to the system via the
> +simple_counter_register function, and later removed via the
> +simple_counter_unregister function. The simple_counter_register function
> +will allocate a counter_device structure, populate it with the required
> +Generic Counter structures and data required to represent the Simple
> +Counter components, and register it to the system via a counter_register
> +call.
> +
> + _______________________ +-------------------------+
> + / simple_counter_device \ --> | simple_counter_register |
> +___________________________ +-------------------------+
> + |
> + +------------------------------+
> + |
> + V
> + ________________ +------------------+
> + / counter_device \ --> | counter_register |
> +____________________ +------------------+
> +
> +The signal_read/signal_write driver callbacks, the
> +count_read/count_write and function_get/function_set driver callbacks,
> +and the action_get/action_set driver callbacks are mapped to the
> +allocated internal counter_device structure via appropriate hook
> +functions.
> +
> + Simple Counter Remap Function Generic Counter
> + -------------- -------------- ---------------
> +
> + signal_read simple_counter_signal_read signal_read
> + signal_write simple_counter_signal_write signal_write
> + count_read simple_counter_count_read count_read
> + count_write simple_counter_count_write count_write
> + function_get simple_counter_function_get function_get
> + function_set simple_counter_function_set function_set
> + action_get simple_counter_action_get action_get
> + action_set simple_counter_action_set action_set
> +
> +This is how Generic Counter interface sysfs attributes are inherited and
> +extended by the Simple Counter interface. If a driver callback is left
> +undefined, then the respective read/write permission is left disabled
> +for the relevant attributes.
> +
> +Similarly, simple_counter_device_ext, simple_counter_count_ext, and
> +simple_counter_signal_ext structures are mapped to respective
> +counter_device_ext, counter_count_ext, and counter_signal_ext structures
> +via appropriate hook functions, and then added to the allocated internal
> +counter_device structure for registration.
On Thu, 14 Dec 2017 15:52:20 -0500
William Breathitt Gray <[email protected]> wrote:
> This patch introduces the Quadrature Counter interface. The Quadrature
> Counter interface serves as an API to provide support for quadrature
> encoder counter devices. The Quadrature Counter interface is built on
> top of the Generic Counter interface.
>
> A quadrature encoder counter device is a counter device that has a
> quadrature pair of signals associated with each count value. Signals may
> have a value of "low" or "high."
>
> Signals may be represented by two possible states:
>
> QUAD_COUNTER_SIGNAL_LOW: "low"
> QUAD_COUNTER_SIGNAL_HIGH: "high"
>
> With quadrature encoders, there types of encoding are typically used:
> X1, X2, and X4; some quadrature encoders also offer a non-quadrature
> mode (typically pulse-direction encoding).
>
> The Quadrature Counter interface provides four count function modes:
>
> QUAD_COUNTER_FUNCTION_PULSE_DIRECTION: "pulse-direction"
> QUAD_COUNTER_FUNCTION_QUADRATURE_X1: "quadrature x1"
> QUAD_COUNTER_FUNCTION_QUADRATURE_X2: "quadrature x2"
> QUAD_COUNTER_FUNCTION_QUADRATURE_X4: "quadrature x4"
>
> Since the Quadrature Counter interface utilizes the Generic Counter
> interface underneath, all the expected functionality of the Generic
> Counter interface such as sysfs attributes is exposed to userspace for
> end user consumption. The Quadrature Counter interface serves as a
> convenience API for supporting a common class of counter devices without
> the need to manually configure the more cumbersome Generic Counter
> interface for use.
>
> In addition to the typical sysfs attributes of the Generic Counter
> interface, the Quadrature Counter interface provides "direction"
> attributes for each count value. These read-only attributes provide the
> current direction of their respective quadrature encoding stream.
>
> To use the Quadrature Counter interface, first create an array of
> quad_counter_count structures to represent the desired counts and
> signals of the counter device; the signal_a member of a
> quad_counter_count structure should define the Channel A signal of the
> respective quadrature pair, and similarly the signal_b member should
> define Channel B. Next, allocate a quad_counter_device structure and
> populate it with the desired driver callbacks and the quad_counter_count
> array created earlier. Finally, register the counter by calling the
> quad_counter_register function. The quad_counter_unregister function may
> be used to unregistered a previously registered counter.
>
> Memory-managed versions of quad_counter_register and
> quad_counter_unregister functions are provided by the
> devm_quad_counter_register and devm_quad_counter_unregister functions
> respectively.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
Pretty much same few comments as for the simple counter so I haven't repeated
here.
Jonathan
> ---
> drivers/iio/counter/Kconfig | 4 +-
> drivers/iio/counter/Makefile | 1 +
> drivers/iio/counter/quad-counter.c | 774 +++++++++++++++++++++++++++++++++++++
> include/linux/iio/counter.h | 191 +++++++++
> 4 files changed, 969 insertions(+), 1 deletion(-)
> create mode 100644 drivers/iio/counter/quad-counter.c
>
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index 9d7dae137f9c..33fde25e5018 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -10,7 +10,9 @@ menuconfig COUNTER
> rudimentary support for counters and serves as building blocks to
> create more complex counter interfaces. The Simple Counter API
> provides support for simple hardware counter devices that have a
> - one-to-one mapping between their Signals and Counts.
> + one-to-one mapping between their Signals and Counts. The Quadrature
> + Counter API provides support for quadrature counter devices that have
> + Signals arranged as quadrature pairs associated to Counts.
>
> if COUNTER
>
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index febd2884b474..55f59e566d72 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -6,6 +6,7 @@
>
> obj-$(CONFIG_COUNTER) += counter.o
> counter-$(CONFIG_COUNTER) += generic-counter.o
> +counter-$(CONFIG_COUNTER) += quad-counter.o
> counter-$(CONFIG_COUNTER) += simple-counter.o
>
> obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
> diff --git a/drivers/iio/counter/quad-counter.c b/drivers/iio/counter/quad-counter.c
> new file mode 100644
> index 000000000000..74a738e4b515
> --- /dev/null
> +++ b/drivers/iio/counter/quad-counter.c
> @@ -0,0 +1,774 @@
> +/*
> + * Quadrature 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.
> + */
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +
> +#include <linux/iio/counter.h>
> +
> +static const char *const quad_counter_signal_level_names[] = {
> + [QUAD_COUNTER_SIGNAL_LOW] = "low",
> + [QUAD_COUNTER_SIGNAL_HIGH] = "high"
> +};
> +
> +static ssize_t quad_counter_signal_read(struct counter_device *counter_dev,
> + struct counter_signal *counter_sig, char *buf)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_signal *const signal = counter_sig->priv;
> + int err;
> + enum quad_counter_signal_level level;
> +
> + err = counter->signal_read(counter, signal, &level);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n",
> + quad_counter_signal_level_names[level]);
> +}
> +
> +static ssize_t quad_counter_count_read(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, char *buf)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + int err;
> + long val;
> +
> + err = counter->count_read(counter, count, &val);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
> +}
> +
> +static ssize_t quad_counter_count_write(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, const char *buf, size_t len)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + int err;
> + long val;
> +
> + err = kstrtol(buf, 0, &val);
> + if (err)
> + return err;
> +
> + err = counter->count_write(counter, count, val);
> + if (err)
> + return err;
> +
> + return len;
> +}
> +
> +static int quad_counter_function_get(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, size_t *counter_func)
> +{
> + int err;
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + enum quad_counter_function function;
> +
> + err = counter->function_get(counter, count, &function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + *counter_func = function;
> +
> + return 0;
> +}
> +
> +static int quad_counter_function_set(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, size_t function)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + int err;
> +
> + err = counter->function_set(counter, count, function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + return 0;
> +}
> +
> +enum quad_counter_action {
> + QUAD_COUNTER_ACTION_NONE = 0,
> + QUAD_COUNTER_ACTION_RISING_EDGE,
> + QUAD_COUNTER_ACTION_FALLING_EDGE,
> + QUAD_COUNTER_ACTION_BOTH_EDGES
> +};
> +
> +static int quad_counter_action_get(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
> + size_t *counter_act)
> +{
> + int err;
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + enum quad_counter_function function;
> + enum quad_counter_direction dir;
> +
> + err = counter->function_get(counter, count, &function);
> + if (err)
> + return err;
> +
> + /* Default action mode */
> + *counter_act = QUAD_COUNTER_ACTION_NONE;
> +
> + /* Determine action mode based on current count function mode */
> + switch (function) {
> + case QUAD_COUNTER_FUNCTION_PULSE_DIRECTION:
> + if (count->signal_a.id == counter_syn->signal->id)
> + *counter_act = QUAD_COUNTER_ACTION_RISING_EDGE;
> + break;
> + case QUAD_COUNTER_FUNCTION_QUADRATURE_X1:
> + if (count->signal_a.id == counter_syn->signal->id) {
> + err = counter->direction_get(counter, count, &dir);
> + if (err)
> + return err;
> +
> + if (dir == QUAD_COUNTER_DIRECTION_FORWARD)
> + *counter_act = QUAD_COUNTER_ACTION_RISING_EDGE;
> + else
> + *counter_act = QUAD_COUNTER_ACTION_FALLING_EDGE;
> + }
> + break;
> + case QUAD_COUNTER_FUNCTION_QUADRATURE_X2:
> + if (count->signal_a.id == counter_syn->signal->id)
> + *counter_act = QUAD_COUNTER_ACTION_BOTH_EDGES;
> + break;
> + case QUAD_COUNTER_FUNCTION_QUADRATURE_X4:
> + *counter_act = QUAD_COUNTER_ACTION_BOTH_EDGES;
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t quad_counter_signal_ext_read(struct counter_device *dev,
> + struct counter_signal *signal, void *priv, char *buf)
> +{
> + const struct quad_counter_signal_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_signal *const quad_signal = signal->priv;
> +
> + return ext->read(counter, quad_signal, ext->priv, buf);
> +}
> +
> +static ssize_t quad_counter_signal_ext_write(struct counter_device *dev,
> + struct counter_signal *signal, void *priv, const char *buf, size_t len)
> +{
> + const struct quad_counter_signal_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_signal *const quad_signal = signal->priv;
> +
> + return ext->write(counter, quad_signal, ext->priv, buf, len);
> +}
> +
> +static int quad_counter_counter_signal_ext_register(
> + const struct quad_counter_signal *const quad_signal,
> + struct counter_signal *const signal)
> +{
> + const struct quad_counter_signal_ext *const quad_ext = quad_signal->ext;
> + const size_t num_ext = quad_signal->num_ext;
> + struct counter_signal_ext *ext;
> + size_t i;
> +
> + /* Exit early if no extensions */
> + if (!quad_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for counter_signal_ext array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register quad_counter_signal_ext via counter_signal_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = quad_ext[i].name;
> + ext[i].read = (quad_ext[i].read) ?
> + quad_counter_signal_ext_read : NULL;
> + ext[i].write = (quad_ext[i].write) ?
> + quad_counter_signal_ext_write : NULL;
> + ext[i].priv = quad_ext + i;
> + }
> +
> + /* Register Counter Signal extensions */
> + signal->ext = ext;
> + signal->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static int quad_counter_counter_signals_register(
> + const struct quad_counter_device *const counter)
> +{
> + struct counter_signal *signals;
> + const size_t num_counts = counter->num_counts;
> + const size_t num_signals = 2 * num_counts;
> + size_t i;
> + struct counter_signal *signal;
> + struct quad_counter_signal *quad_signal;
> + struct quad_counter_count *const counts = counter->counts;
> + int err;
> + struct counter_device *const counter_dev = counter->counter_dev;
> +
> + /* Allocate space for signals array */
> + signals = kcalloc(num_signals, sizeof(*signals), GFP_KERNEL);
> + if (!signals)
> + return -ENOMEM;
> +
> + /* Configure Signals */
> + for (i = 0; i < num_signals; i++) {
> + signal = signals + i;
> + if (i % 2)
> + quad_signal = &counts[i / 2].signal_b;
> + else
> + quad_signal = &counts[i / 2].signal_a;
> +
> + signal->id = quad_signal->id;
> + signal->name = quad_signal->name;
> + signal->priv = quad_signal;
> +
> + /* Register Counter Signal extensions */
> + err = quad_counter_counter_signal_ext_register(quad_signal,
> + signal);
> + if (err)
> + goto err_free_signals;
> + }
> +
> + /* Register Signals to Counter device container */
> + counter_dev->signals = signals;
> + counter_dev->num_signals = num_signals;
> +
> + return 0;
> +
> +err_free_signals:
> + while (i--)
> + kfree(signals[i].ext);
> + kfree(signals);
> + return err;
> +}
> +
> +static const char *const quad_counter_function_names[] = {
> + [QUAD_COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
> + [QUAD_COUNTER_FUNCTION_QUADRATURE_X1] = "quadrature x1",
> + [QUAD_COUNTER_FUNCTION_QUADRATURE_X2] = "quadrature x2",
> + [QUAD_COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
> +};
> +
> +static const char *const quad_counter_action_names[] = {
> + [QUAD_COUNTER_ACTION_NONE] = "none",
> + [QUAD_COUNTER_ACTION_RISING_EDGE] = "rising edge",
> + [QUAD_COUNTER_ACTION_FALLING_EDGE] = "falling edge",
> + [QUAD_COUNTER_ACTION_BOTH_EDGES] = "both edges"
> +};
> +
> +static int quad_counter_counter_synapses_register(
> + struct counter_signal *const signals, struct counter_count *const count)
> +{
> + struct counter_synapse *synapses;
> + const size_t num_synapses = 2;
> + size_t i;
> +
> + /* Allocate space for Counter Synapses */
> + synapses = kcalloc(num_synapses, sizeof(*synapses), GFP_KERNEL);
> + if (!synapses)
> + return -ENOMEM;
> +
> + /* Configure Synapses */
> + for (i = 0; i < num_synapses; i++) {
> + synapses[i].signal = signals + i;
> + synapses[i].actions = quad_counter_action_names;
> + synapses[i].num_actions = ARRAY_SIZE(quad_counter_action_names);
> + }
> +
> + /* Register Counter Synapses */
> + count->synapses = synapses;
> + count->num_synapses = num_synapses;
> +
> + return 0;
> +}
> +
> +static const char *const quad_counter_direction_names[] = {
> + [QUAD_COUNTER_DIRECTION_FORWARD] = "forward",
> + [QUAD_COUNTER_DIRECTION_BACKWARD] = "backward"
> +};
> +
> +static ssize_t quad_counter_direction_read(struct counter_device *dev,
> + struct counter_count *count, void *priv, char *buf)
> +{
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_count *const quad_count = count->priv;
> + int err;
> + enum quad_counter_direction direction;
> +
> + err = counter->direction_get(counter, quad_count, &direction);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n",
> + quad_counter_direction_names[direction]);
> +}
> +
> +static ssize_t quad_counter_count_ext_read(struct counter_device *dev,
> + struct counter_count *count, void *priv, char *buf)
> +{
> + const struct quad_counter_count_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_count *const quad_count = count->priv;
> +
> + return ext->read(counter, quad_count, ext->priv, buf);
> +}
> +
> +static ssize_t quad_counter_count_ext_write(struct counter_device *dev,
> + struct counter_count *count, void *priv, const char *buf, size_t len)
> +{
> + const struct quad_counter_count_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_count *const quad_count = count->priv;
> +
> + return ext->write(counter, quad_count, ext->priv, buf, len);
> +}
> +
> +static int quad_counter_counter_count_ext_register(
> + const struct quad_counter_device *const counter,
> + const struct quad_counter_count *const quad_count,
> + struct counter_count *const count)
> +{
> + size_t num_ext = 0;
> + const struct quad_counter_count_ext *const quad_ext = quad_count->ext;
> + const size_t quad_num_ext = quad_count->num_ext;
> + struct counter_count_ext *ext;
> + size_t ext_i = 0;
> + size_t i;
> +
> + /* Count number of extensions */
> + if (counter->direction_get)
> + num_ext++;
> + if (quad_ext)
> + num_ext += quad_num_ext;
> +
> + /* Return early if no extensions */
> + if (!num_ext)
> + return 0;
> +
> + /* Allocate space for Counter Count extensions array */
> + ext = kcalloc(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register direction extension */
> + if (counter->direction_get) {
> + ext[ext_i].name = "direction";
> + ext[ext_i].read = quad_counter_direction_read;
> +
> + ext_i++;
> + }
> +
> + /* Register driver Quadrature Counter Count extensions */
> + for (i = 0; i < quad_num_ext; i++) {
> + ext[ext_i + i].name = quad_ext[i].name;
> + ext[ext_i + i].read = (quad_ext[i].read) ?
> + quad_counter_count_ext_read : NULL;
> + ext[ext_i + i].write = (quad_ext[i].write) ?
> + quad_counter_count_ext_write : NULL;
> + ext[ext_i + i].priv = quad_ext + i;
> + }
> + ext_i += quad_num_ext;
> +
> + /* Register Counter Count extensions */
> + count->ext = ext;
> + count->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static void quad_counter_counter_synapses_unregister(
> + const struct counter_count *const count)
> +{
> + kfree(count->synapses);
> +}
> +
> +static int quad_counter_counter_count_init(struct counter_count *const count,
> + struct quad_counter_count *const quad_count,
> + struct counter_signal *const signals,
> + const struct quad_counter_device *const counter)
> +{
> + int err;
> +
> + count->id = quad_count->id;
> + count->name = quad_count->name;
> + count->functions = quad_counter_function_names;
> + count->num_functions = ARRAY_SIZE(quad_counter_function_names);
> + count->priv = quad_count;
> +
> + /* Register Counter Synapses */
> + err = quad_counter_counter_synapses_register(signals, count);
> + if (err)
> + return -ENOMEM;
> +
> + /* Register Quadrature Counter Count extensions */
> + err = quad_counter_counter_count_ext_register(counter, quad_count,
> + count);
> + if (err)
> + goto err_unregister_synapses;
> +
> + return 0;
> +
> +err_unregister_synapses:
> + quad_counter_counter_synapses_unregister(count);
> + return err;
> +}
> +
> +static void quad_counter_counter_count_ext_unregister(
> + const struct counter_count *const count)
> +{
> + kfree(count->ext);
> +}
> +
> +static void quad_counter_counter_count_free(
> + const struct counter_count *const count)
> +{
> + quad_counter_counter_count_ext_unregister(count);
> + quad_counter_counter_synapses_unregister(count);
> +}
> +
> +static int quad_counter_counter_counts_register(
> + const struct quad_counter_device *const counter)
> +{
> + struct counter_device *const counter_dev = counter->counter_dev;
> + struct counter_count *counts;
> + const size_t num_counts = counter->num_counts;
> + size_t i;
> + struct quad_counter_count *const quad_counts = counter->counts;
> + struct counter_signal *const signals = counter_dev->signals;
> + int err;
> +
> + /* Allocate space for counts array */
> + counts = kcalloc(num_counts, sizeof(*counts), GFP_KERNEL);
> + if (!counts)
> + return -ENOMEM;
> +
> + /* Initialize Counts */
> + for (i = 0; i < num_counts; i++) {
> + err = quad_counter_counter_count_init(counts + i,
> + quad_counts + i, signals + 2 * i, counter);
> + if (err)
> + goto err_free_counts;
> + }
> +
> + /* Register Counts to Counter device container */
> + counter_dev->counts = counts;
> + counter_dev->num_counts = num_counts;
> +
> + return 0;
> +
> +err_free_counts:
> + while (i--)
> + quad_counter_counter_count_free(counts + i);
> + kfree(counts);
> + return err;
> +}
> +
> +static void quad_counter_counter_signals_unregister(
> + const struct counter_device *const counter_dev)
> +{
> + const struct counter_signal *const signals = counter_dev->signals;
> + size_t num_signals = counter_dev->num_signals;
> +
> + while (num_signals--)
> + kfree(signals[num_signals].ext);
> + kfree(signals);
> +}
> +
> +static int quad_counter_counts_register(
> + struct quad_counter_device *const counter)
> +{
> + const struct quad_counter_count *const quad_counts = counter->counts;
> + const size_t num_counts = counter->num_counts;
> + int err;
> +
> + /* At least one Count must be defined */
> + if (!quad_counts || !num_counts) {
> + pr_err("quad-counter: Quadrature Counter Counts undefined\n");
> + return -EINVAL;
> + }
> +
> + /* Allocate Counter Signals */
> + err = quad_counter_counter_signals_register(counter);
> + if (err)
> + return err;
> +
> + /* Allocate Counter Counts */
> + err = quad_counter_counter_counts_register(counter);
> + if (err)
> + goto err_unregister_signals;
> +
> + return 0;
> +
> +err_unregister_signals:
> + quad_counter_counter_signals_unregister(counter->counter_dev);
> + return err;
> +}
> +
> +static ssize_t quad_counter_device_ext_read(struct counter_device *dev,
> + void *priv, char *buf)
> +{
> + const struct quad_counter_device_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> +
> + return ext->read(counter, ext->priv, buf);
> +}
> +
> +static ssize_t quad_counter_device_ext_write(struct counter_device *dev,
> + void *priv, const char *buf, size_t len)
> +{
> + const struct quad_counter_device_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> +
> + return ext->write(counter, ext->priv, buf, len);
> +}
> +
> +static int quad_counter_device_ext_register(
> + struct quad_counter_device *const counter)
> +{
> + const struct quad_counter_device_ext *const quad_ext = counter->ext;
> + const size_t num_ext = counter->num_ext;
> + struct counter_device_ext *ext;
> + size_t i;
> + struct counter_device *const counter_dev = counter->counter_dev;
> +
> + /* Return early if no extensions */
> + if (!quad_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for counter_device_ext array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register quad_counter_device_ext via counter_device_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = quad_ext[i].name;
> + ext[i].read = (quad_ext[i].read) ?
> + quad_counter_device_ext_read : NULL;
> + ext[i].write = (quad_ext[i].write) ?
> + quad_counter_device_ext_write : NULL;
> + ext[i].priv = quad_ext + i;
> + }
> +
> + /* Register Counter device extensions */
> + counter_dev->ext = ext;
> + counter_dev->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static void quad_counter_counter_counts_unregister(
> + const struct counter_device *const counter_dev)
> +{
> + const struct counter_count *const counts = counter_dev->counts;
> + size_t num_counts = counter_dev->num_counts;
> +
> + while (num_counts--)
> + quad_counter_counter_count_free(counts + num_counts);
> + kfree(counts);
> +}
> +
> +static void quad_counter_counts_unregister(
> + const struct quad_counter_device *const counter)
> +{
> + const struct counter_device *const counter_dev = counter->counter_dev;
> +
> + quad_counter_counter_counts_unregister(counter_dev);
> + quad_counter_counter_signals_unregister(counter_dev);
> +}
> +
> +/**
> + * quad_counter_register - register Quadrature Counter to the system
> + * @counter: pointer to Quadrature Counter to register
> + *
> + * This function registers a Quadrature Counter to the system. A sysfs "counter"
> + * directory will be created and populated with sysfs attributes correlating
> + * with the Quadrature Counter Signals, Synapses, and Counts respectively.
> + */
> +int quad_counter_register(struct quad_counter_device *const counter)
> +{
> + struct counter_device *counter_dev;
> + int err;
> +
> + if (!counter)
> + return -EINVAL;
> +
> + /* Allocate internal Counter container */
> + counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL);
> + if (!counter)
> + return -ENOMEM;
> + counter->counter_dev = counter_dev;
> +
> + /* Configure internal Counter */
> + counter_dev->name = counter->name;
> + counter_dev->parent = counter->parent;
> + counter_dev->signal_read = (counter->signal_read) ?
> + quad_counter_signal_read : NULL;
> + counter_dev->count_read = (counter->count_read) ?
> + quad_counter_count_read : NULL;
> + counter_dev->count_write = (counter->count_write) ?
> + quad_counter_count_write : NULL;
> + counter_dev->function_get = (counter->function_get) ?
> + quad_counter_function_get : NULL;
> + counter_dev->function_set = (counter->function_set) ?
> + quad_counter_function_set : NULL;
> + counter_dev->action_get = (counter->function_get &&
> + counter->direction_get) ? quad_counter_action_get : NULL;
> + counter_dev->priv = counter;
> +
> + /* Register Quadrature Counter Counts */
> + err = quad_counter_counts_register(counter);
> + if (err)
> + goto err_free_counter_dev;
> +
> + /* Register Quadrature Counter device extension attributes */
> + err = quad_counter_device_ext_register(counter);
> + if (err)
> + goto err_unregister_counts;
> +
> + /* Register internal Counter to the system */
> + err = counter_register(counter_dev);
> + if (err)
> + goto err_free_ext;
> +
> + return 0;
> +
> +err_free_ext:
> + kfree(counter_dev->ext);
> +err_unregister_counts:
> + quad_counter_counts_unregister(counter);
> +err_free_counter_dev:
> + kfree(counter_dev);
> + return err;
> +}
> +EXPORT_SYMBOL(quad_counter_register);
> +
> +/**
> + * quad_counter_unregister - unregister Quadrature Counter from the system
> + * @counter: pointer to Quadrature Counter to unregister
> + *
> + * The Quadrature Counter is unregistered from the system; all allocated memory
> + * is freed.
> + */
> +void quad_counter_unregister(struct quad_counter_device *const counter)
> +{
> + struct counter_device *counter_dev;
> +
> + if (!counter)
> + return;
> +
> + counter_dev = counter->counter_dev;
> +
> + counter_unregister(counter_dev);
> +
> + kfree(counter_dev->ext);
> + quad_counter_counts_unregister(counter);
> + kfree(counter_dev);
> +}
> +EXPORT_SYMBOL(quad_counter_unregister);
> +
> +static void devm_quad_counter_unreg(struct device *dev, void *res)
> +{
> + quad_counter_unregister(*(struct quad_counter_device **)res);
> +}
> +
> +/**
> + * devm_quad_counter_register - Resource-managed quad_counter_register
> + * @dev: device to allocate quad_counter_device for
> + * @counter: pointer to Quadrature Counter to register
> + *
> + * Managed quad_counter_register. The Quadrature Counter registered with this
> + * function is automatically unregistered on driver detach. This function calls
> + * quad_counter_register internally. Refer to that function for more
> + * information.
> + *
> + * If an Quadrature Counter registered with this function needs to be
> + * unregistered separately, devm_quad_counter_unregister must be used.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int devm_quad_counter_register(struct device *dev,
> + struct quad_counter_device *const counter)
> +{
> + struct quad_counter_device **ptr;
> + int ret;
> +
> + ptr = devres_alloc(devm_quad_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr)
> + return -ENOMEM;
> +
> + ret = quad_counter_register(counter);
> + if (!ret) {
> + *ptr = counter;
> + devres_add(dev, ptr);
> + } else
> + devres_free(ptr);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(devm_quad_counter_register);
> +
> +static int devm_quad_counter_match(struct device *dev, void *res, void *data)
> +{
> + struct quad_counter_device **r = res;
> +
> + if (!r || !*r) {
> + WARN_ON(!r || !*r);
> + return 0;
> + }
> +
> + return *r == data;
> +}
> +
> +/**
> + * devm_quad_counter_unregister - Resource-managed quad_counter_unregister
> + * @dev: device this quad_counter_device belongs to
> + * @counter: the Quadrature Counter associated with the device
> + *
> + * Unregister Quadrature Counter registered with devm_quad_counter_register.
> + */
> +void devm_quad_counter_unregister(struct device *dev,
> + struct quad_counter_device *const counter)
> +{
> + int rc;
> +
> + rc = devres_release(dev, devm_quad_counter_unreg,
> + devm_quad_counter_match, counter);
> + WARN_ON(rc);
> +}
> +EXPORT_SYMBOL(devm_quad_counter_unregister);
> +
> +MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
> +MODULE_DESCRIPTION("Quadrature Counter interface");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
> index 0967ea2a9bef..a6f0f9130377 100644
> --- a/include/linux/iio/counter.h
> +++ b/include/linux/iio/counter.h
> @@ -435,4 +435,195 @@ extern int devm_simple_counter_register(struct device *dev,
> extern void devm_simple_counter_unregister(struct device *dev,
> struct simple_counter_device *const counter);
>
> +struct quad_counter_device;
> +struct quad_counter_signal;
> +
> +/**
> + * struct quad_counter_signal_ext - Quadrature Counter Signal extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct quad_counter_signal_ext {
> + const char *name;
> + ssize_t (*read)(struct quad_counter_device *counter,
> + struct quad_counter_signal *signal, void *priv,
> + char *buf);
> + ssize_t (*write)(struct quad_counter_device *counter,
> + struct quad_counter_signal *signal, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_signal - Quadrature Counter Signal node
> + * @id: [DRIVER] unique ID used to identify signal
> + * @name: [DRIVER] device-specific signal name
> + * @ext: [DRIVER] optional array of Quadrature Counter Signal extensions
> + * @num_ext: [DRIVER] number of Quadrature Counter Signal extensions
> + * specified in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct quad_counter_signal {
> + int id;
> + const char *name;
> +
> + const struct quad_counter_signal_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +enum quad_counter_signal_level {
> + QUAD_COUNTER_SIGNAL_LOW = 0,
> + QUAD_COUNTER_SIGNAL_HIGH
> +};
> +
> +struct quad_counter_count;
> +
> +enum quad_counter_function {
> + QUAD_COUNTER_FUNCTION_PULSE_DIRECTION = 0,
> + QUAD_COUNTER_FUNCTION_QUADRATURE_X1,
> + QUAD_COUNTER_FUNCTION_QUADRATURE_X2,
> + QUAD_COUNTER_FUNCTION_QUADRATURE_X4
> +};
> +
> +enum quad_counter_direction {
> + QUAD_COUNTER_DIRECTION_FORWARD = 0,
> + QUAD_COUNTER_DIRECTION_BACKWARD
> +};
> +
> +/**
> + * struct quad_counter_count_ext - Quadrature Counter Count extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct quad_counter_count_ext {
> + const char *name;
> + ssize_t (*read)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, void *priv,
> + char *buf);
> + ssize_t (*write)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_count - Quadrature Counter Count node
> + * @id: [DRIVER] unique ID used to identify Count
> + * @name: [DRIVER] device-specific Count name
> + * @function: [DRIVER] current function mode
> + * @direction: [DRIVER] current direction state
> + * @signal_a: [DRIVER] associated quadrature A signal
> + * @signal_b: [DRIVER] associated quadrature B signal
> + * @ext: [DRIVER] optional array of Quadrature Counter Count extensions
> + * @num_ext: [DRIVER] number of Quadrature Counter Count extensions specified
> + * in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct quad_counter_count {
> + int id;
> + const char *name;
> + enum quad_counter_function function;
> + enum quad_counter_direction direction;
> +
> + struct quad_counter_signal signal_a;
> + struct quad_counter_signal signal_b;
> +
> + const struct quad_counter_count_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_device_ext - Quadrature Counter device extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct quad_counter_device_ext {
> + const char *name;
> + ssize_t (*read)(struct quad_counter_device *counter, void *priv,
> + char *buf);
> + ssize_t (*write)(struct quad_counter_device *counter,
> + void *priv, const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_device - Quadrature Counter data structure
> + * @name: [DRIVER] name of the device
> + * @parent: [DRIVER] optional parent device providing the counters
> + * @counter_dev: [INTERN] internal Counter container
> + * @signal_read: [DRIVER] read callback for Signal attribute; may be
> + * NULL. Returns 0 on success and negative error code on
> + * error. The respective Signal's returned level should be
> + * passed back via the level parameter.
> + * @count_read: [DRIVER] read callback for Count attribute; may be NULL.
> + * Returns 0 on success and negative error code on error.
> + * The respective Count's returned value should be passed
> + * back via the val parameter.
> + * @count_write: [DRIVER] write callback for Count attribute; may be NULL
> + * @function_get: [DRIVER] function to get the current count function
> + * mode. Returns 0 on success and negative error code on
> + * error. The respective Count's returned function mode
> + * should be passed back via the function parameter.
> + * @function_set: [DRIVER] function to set the count function mode
> + * @direction_get: [DRIVER] function to get the current direction. Returns
> + * 0 on success and negative error code on error. The
> + * respective Count's returned direction should be passed
> + * back via the direction parameter.
> + * @counts: [DRIVER] array of Quadrature Counter Counts
> + * @num_counts: [DRIVER] number of Quadrature Counter Counts specified
> + * in @counts
> + * @ext: [DRIVER] optional array of Quadrature Counter device
> + * extensions
> + * @num_ext: [DRIVER] number of Quadrature Counter device extensions
> + * specified in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct quad_counter_device {
> + const char *name;
> + struct device *parent;
> + struct counter_device *counter_dev;
> +
> + int (*signal_read)(struct quad_counter_device *counter,
> + struct quad_counter_signal *signal,
> + enum quad_counter_signal_level *level);
> + int (*count_read)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, long *val);
> + int (*count_write)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, long val);
> + int (*function_get)(struct quad_counter_device *counter,
> + struct quad_counter_count *count,
> + enum quad_counter_function *function);
> + int (*function_set)(struct quad_counter_device *counter,
> + struct quad_counter_count *count,
> + enum quad_counter_function function);
> + int (*direction_get)(struct quad_counter_device *counter,
> + struct quad_counter_count *count,
> + enum quad_counter_direction *direction);
> +
> + struct quad_counter_count *counts;
> + size_t num_counts;
> +
> + const struct quad_counter_device_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +extern int quad_counter_register(struct quad_counter_device *const counter);
> +extern void quad_counter_unregister(struct quad_counter_device *const counter);
> +extern int devm_quad_counter_register(struct device *dev,
> + struct quad_counter_device *const counter);
> +extern void devm_quad_counter_unregister(struct device *dev,
> + struct quad_counter_device *const counter);
> +
> #endif /* _COUNTER_H_ */
On Mon, 1 Jan 2018 11:16:30 +0000
Jonathan Cameron <[email protected]> wrote:
Sorry to top post but I just want to add some general comments across
the whole series.
1) Basics look good to me. It all fits together well.
2) I'm concerned about the two 'simplified interfaces' for a couple of reasons:
a) They add a lot of code and I'm not convinced the simplifications justify that.
b) They aren't as generically applicable as we might want. For example, it's
common for SoC Encoder blocks to operative in both the simple counter mode and
the quadrature counter mode. To support that we have (I think) to go back to
basics and do it ourselves from the generic counter interface. The TI eQEP
IP does these modes for example.
So these simplifications serve two purposes (I think)
1) To enforce interface. This is nice (and I did some similar stuff in IIO
for the same reason) but there is so much flexibility in the rest of the
interface (from a code point of view) that I'm unsure this is significant.
2) To save on boiler plate. I'm not sure we save that much and that it couldn't
mostly be achieved by providing some useful utility functions and
standard enums / string arrays for the common cases.
We have to justify a couple of thousand lines of core code. To do that we
need to be saving a reasonably multiple more than that in driver code.
The full setup for a generic_counter is not so complex that we need this
stuff. Your examples make it all pretty clear what is going on and a
couple of clean well commented drivers to act as a baseline for new
implementations would get us much of the rest of the way.
So going well, but this aspect needs some more consideration.
I also think we need at least rough outlines of a few more drivers
in here to convince people that there aren't any problems that this
is too inflexible to cover. Hopefully an ST one will be forthcoming.
If not we can do the exercise off datasheets.
Jonathan
> On Thu, 14 Dec 2017 15:50:29 -0500
> William Breathitt Gray <[email protected]> wrote:
>
> > Introduction
> > ============
> >
> > Apologies for going silent these past couple months just to return with
> > a patchset over 3000 lines larger than the last -- I should have been
> > releasing intermediate versions along the way so shame on me!
>
> :) Sometimes it's better to wait until you are moderately happy with it
> yourself!
>
> > The
> > Counter system has effectively been rewritten anew, so I believe very
> > little of the code in the previous versions of this patchset remain.
> > However, the Generic Counter paradigm has pretty much remained the same
> > so the theory should be familar. Regardless, I realize I'm dropping off
> > this patchset near the winter holidays so I don't expect a review until
> > well into January -- I'm just releasing this now before I myself head
> > off on an end of year sabbatical.
>
> It's at least a few hours into January so here goes before life gets
> properly busy again.
>
> >
> > The most significant difference between this version and the previous,
> > as well as part of the reason for the implementation code changes, is
> > the complete separation of the Generic Counter system from IIO. I
> > decided it was improper to build the Generic Counter system on top of
> > IIO core: it was leading to ugly code, convulted hacks, and forced
> > restrictions on the Generic Counter interface in order to appease the
> > architecture of the IIO system. Most importantly, the IIO core code that
> > was leveraged by the Generic Counter was so minor (essentially just the
> > sysfs attribute support) that it did not justify the extensive
> > hoop-jumping performed to make the code work.
> >
> > So this patchset introduces the Generic Counter interface without the
> > dependence on IIO code. This now gives the Generic Counter system the
> > freedom to aptly represent counter devices without implementation
> > compatibility concerns regarding other high-level subsystems.
> >
> > This also makes sense ontologically I believe because whereas the IIO
> > system appears more focused on representing the industrial I/O of a
> > device and their configuration directly, the Generic Counter system is
> > more concerned with the abstract representation of that counter device
> > and the relationships and configurations within which define its
> > operation at a high-level; a counter driver could in theory separately
> > support both the high-level Generic Counter representation of the device
> > as a whole (what are we counting conceptually, how much are we counting,
> > etc.), as well as the low-level IIO representation of the individual
> > inputs and outputs on that device (are the signals differential, do
> > certain signals have current requirements, etc.).
>
> I think there are concepts that over time may blur the lines more
> but agree with the basic point. I'm just planning to nick all your
> good ideas if they will improve IIO in turn.
>
> >
> > Overview
> > ========
> >
> > This patchset may be divided into three main groups:
> >
> > * Generic Counter
> > * Simple Counter
> > * Quadrature Counter
> >
> > Each group begins with a patch introducing the implementation of the
> > interface system, followed afterwards by documentation patches. I
> > recommend reading through the documentation patches first to familiarize
> > your with the interface itself before jumping into the source code for
> > the implementation.
> >
> > The Simple Counter and Quadrature Counter groups also have example
> > driver code in the dummy-counter and 104-quad-8 patches respectively.
> > The Simple Counter and Quadrature Counter systems themselves being
> > subclasses of the Generic Counter may serve as example driver code for
> > the Generic Counter interface -- though I may end up adding an explicit
> > Generic Counter example in a later patch to the dummy-counter for easier
> > reference.
> >
> > Since the Generic Counter system no longer depends on IIO, I moved all
> > Counter related source code to the drivers/iio/counter/ directory to
> > keep everything contained together. In addition, with the IIO Kconfig
> > dependency removed, the COUNTER menu appear now appears at the same
> > level as the IIO menu:
> >
> > -> Device drivers
> > -> Counter Support (COUNTER [=m])
> >
> > I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
> > order to match the Kconfig heirarchy or to keep it where it is to match
> > the legacy IIO counter location established when we first added the
> > 104-QUAD-8 driver.
>
> I would move it out entirely - otherwise things are just confusing.
> You 'could' sit it in IIO (as in put it under the top level menu option)
> if you would prefer but I don't thing that really makes sense.
>
> >
> > Paradigm updates
> > ================
> >
> > The Generic Counter paradigm has essentially remained the same from the
> > previous patch, but I have made some minor updates. In particular, I've
> > finally settled on a naming convention for the core components of a
> > Counter:
> >
> > COUNT
> > -----
> > A Count represents the count data for a set of Signals. A Count
> > has a count function mode (e.g. "increase" or "quadrature x4")
> > which represents the update behavior for the count data. A Count
> > also has a set of one or more associated Signals.
> >
> > This component was called "Value" in the previous patches. I believe
> > "Count" is a more direct name for this data, and it also matches how
> > datasheets and people commonly refer to this information in
> > documentation.
>
> Agreed - better name.
>
> >
> > SIGNAL
> > ------
> > A Signal represents a counter input data; this is the data that
> > is typically analyzed by the counter to determine the count
> > data. A Signal may be associated to one or more Counts.
> >
> > The naming for this component has not changed since the previous
> > patches. I believe "Signal" is a fitting enough name for the input
> > data, as well as matching the common nomenclature for existing counter
> > devices.
> >
> > SYNAPSE
> > -------
> > A Synapse represents the association of a Signal with a
> > respective Count. Signal data affects respective Count data, and
> > the Synapse represents this relationship. The Synapse action
> > mode (e.g. "rising edge" or "double pulse") specifies the Signal
> > data condition which triggers the respective Count's count
> > function evaluation to update the count data. It is possible for
> > the Synapse action mode to be "none" if a Signal is associated
> > with a Count but does not trigger the count function (e.g. the
> > direction signal line for a Pulse-Direction encoding counter).
> >
> > This component was called "Trigger" in the previous patches. I do not
> > believe "Trigger" was a good name for two main reasons: it could easily
> > be confused for the existing IIO trigger concept, and most importantly
> > it does not convey the connection association aspect of the
> > Count-Signal relationship.
>
> An alternative here would be to use MAP as a number of similar
> 'connection' type arrangements in the kernel do. It doesn't really
> imply the 'how' element though so perhaps a new term is indeed better.
>
>
> >
> > I settled on the "Synapse" name both due to etymology -- from Greek
> > _sunapsis_ meaning "conjunction" -- as well as a biological analogy:
> > just as neurons connect and fire communication over synapses, so does a
> > Counter Signal connect and fire communication to a Counter Count over a
> > Counter Synapse.
> >
> > Following the same naming convention and analogy, what was previously
> > called trigger_mode is now known as action_mode, named in reference to
> > action potential -- the condition in a neuron which triggers a fire
> > communication over a synapse, just as a Counter Signal condition
> > specified in the action_mode of a Counter Synapse triggers the count
> > function evaluation for a Counter Count.
> >
> > Counter classes descriptions
> > ============================
> >
> > The Generic Counter interface is the most general interface for
> > supporting counter devices; if it qualifies as a Counter, then it can be
> > represented by the Generic Counter interface. Unfortunately, the
> > flexibility of the interface does result in a more cumbersome
> > integration for driver authors: much of the components must be manually
> > configured by the author, which can be a tedious task for large and
> > complex counter devices.
> >
> > To this end, two subclasses of the Generic Counter interface as
> > introduced in this patchset: the Simple Counter interface, and the
> > Quadrature Counter interface. Both of these interfaces inherit the
> > Generic Counter paradigm, and may be seen as extensions to the interface
> > which restrict the components to a respective specific class of counter
> > devices in order to provide a more apt interface for such devices.
> >
> > Simple Counter
> > --------------
> > Simple Counters are devices that count edge pulses on an input
> > line (e.g. tally counters).
> >
> > Since the relationship between Signals and Counts is known to be
> > one-to-one, a simple_counter_count structure already contains
> > the associated Signal member for the respective Count. A driver
> > author no longer needs to worry about allocating a separate
> > Signal and Synapse, nor about configuring the association
> > between the respective Count and Signal; the Simple Counter
> > interface abstracts away such details.
> >
> > Furthermore, since the device type is known, component
> > properties may be further defined and restricted: Count data is
> > a signed integer, Signal data "low" and "high" state is set via
> > enumeration constants, and so are count function and action mode
> > restricted to well-defined "increase"/"decrease" and
> > "none"/"rising edge"/"falling edge"/"both edges" enumeration
> > constants respectively.
>
> I do wonder a little on whether this is too restrictive to actually
> represent many devices.
> >
> > Quadrature Counter
> > ------------------
> > Quadrature Counters are devices that track position based on
> > quadrature pair signals (e.g. rotary encoder counters).
> >
> > Since the relationship between Signals and Counts is known to be
> > a quadrature pair of Signals to each Count, a quad_counter_count
> > structure already contains the associated Signal members for the
> > respective Count. A driver author no longer needs to worry about
> > allocating separate Signals and Synapses for each quadrature
> > pair, nor about configuring the association between the
> > respective Count and Signals; the Quadrature Counter interface
> > abstracts away such details.
> >
> > Furthermore, since the device type is known, component
> > properties may be further defined and restricted: Count data is
> > a signed integer, Signal data "low" and "high" state is set via
> > enumeration constants, and so is count function mode restricted
> > to well-defined enumeration constants to represent modes such as
> > "pulse-direction" and "quadrature x4" for example.
>
> Pulse direction is definitely not a quadrature counter... Maybe this needs
> a rename to dual-signal-counter or similar?
>
> Another classic case here would be increment / decrement counters where
> a signal is used for each operation (counting items between two light gates
> - used a lot in tracking products in the production industry).
>
> >
> > Note how driver authors no longer need to interact with Synapses
> > directly when utilizing the Simple Counter and Quadrature Counter
> > interfaces. This should make it easier too for authors to add support
> > since they don't need to fully understand the underlying Counter
> > paradigm in order to take advantage of the interfaces -- just define the
> > Counts and Signals, and they're ready to go.
> >
> > Even more so, the Quadrature Counter interface takes it a step further
> > and doesn't require action_modes to be explicitly set -- rather they are
> > implicitly determined internally by the system based on the direction
> > and function mode. Abstractions like these should make the Counter
> > interface system as a whole robust enough to handle the diverse classes
> > of counter devices out in the real world.
> >
> > Compilation warnings
> > ====================
> >
> > There are three main compilation warnings which pop for this patchset.
> > I've inspected these warnings and none are errors, however they do
> > require some explanation.
> >
> > * 104-quad-8: warning: enumeration value
> > ‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
> > switch
> >
> > The first warning is rather simple to explain: the
> > QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
> > statement's else condition, so an explicit case condition is not
> > necessary. I can add a default case line to pacify the compiler, but
> > since it would be empty the effort seems frivolous.
>
> Do it anyway and put a comment of /* Should not get here */
>
> Suppressing false warnings is useful from a code maintenance point of view.
>
> > In some sense as
> > well, a default case may make the switch logic less clear by implying
> > the possibility of additional cases which are not possible in the
> > context of that code path.
> >
> > * simple-counter: warning: assignment discards ‘const’ qualifier
> > from pointer target type
> > * quad-counter: warning: assignment discards ‘const’ qualifier
> > from pointer target type
> >
> > The second warning comes from the mapping of
> > simple_counter_device_ext/quad_counter_device_ext,
> > simple_counter_count_ext/quad_counter_count_ext, and
> > simple_counter_signal_ext/quad_counter_signal_ext to the internal
> > Counter counter_device_ext, counter_count_ext, and counter_signal_ext
> > structures respectively.
> >
> > The priv member of the counter_device_ext, counter_count_ext, or
> > counter_signal_ext is leveraged to pass the respective
> > simple_counter_device_ext/quad_counter_device_ext,
> > simple_counter_count_ext/quad_counter_count_ext, or
> > simple_counter_signal_ext/quad_counter_signal_ext structure to their
> > respective read/write callback. The priv member is generic on purpose to
> > allow any desired data to be passed; the supplied read/write callbacks
> > should know the datatype of the passed-in priv argument so they cast it
> > appropriately to access their expected data.
> >
> > As such, the 'const' qualifier of the structures are thus discarded but
> > subsequently cast back when the respective registered callback functions
> > are called. Since this is the intended use case of the priv member -- to
> > generically pass driver data for later recast -- I don't believe this
> > warning needs to be rectified.
>
> All warnings need to be rectified. Sorry but this noise will do two things:
> 1) Get you a patch every few weeks from someone fixing it.
> 2) Potentially make real warnings harder to see.
>
> Sometimes we have to play games to work around them, but such is life.
>
> >
> > * generic-counter: warning: passing argument 5 of
> > ‘counter_attribute_create’ discards ‘const’ qualifier
> > from pointer target type
> > * generic-counter: warning: passing argument 6 of
> > ‘counter_attribute_create’ discards ‘const’ qualifier
> > from pointer target type
> >
> > The third warnings comes from a similar situation to the second warning:
> > a 'const' argument is passed generically via 'void *' for later recast.
> > In this cast, I decided to create a generic function called
> > counter_attribute_create in order to simplify the sysfs attribute
> > registration code in the generic-counter.c file.
> >
> > The counter_attribute_create function takes in read and write callbacks,
> > as well as two optional generic data arguments to be stored as 'void *'
> > (the component and component_data parameters). Using this setup allows
> > the counter_attribute_create function to be the sole function necessary
> > to create a desired Generic Counter sysfs attribute: read and write
> > callbacks are passed along with relevant Counter component and data
> > generically, which can be cast back later inside those read and write
> > functions to match the expected datatype.
> >
> > Using a generic counter_attribute_create function reduces duplicate
> > code, but it does result in many superfluous compilation warnings. I can
> > define new attribute_create functions specific to each type of sysfs
> > attribute in order to pacify the warnings, but that seems to be such a
> > waste to increase the amount of code with duplications that are
> > unnecessary. What would you recommend; should I attempt to pacify these
> > warnings or leave them be?
>
> You must fix them I'm afraid.
>
> >
> > Known TODO items
> > ================
> >
> > Although I've added the interface documentation files with rst file
> > extensions, I still need to familiarize myself with Sphinx markup
> > constructs to take advantage of the language. For example, I've copied
> > verbatim several structure definitions into the documentation directly,
> > but I believe this would be better left dynamically generated by using
> > the relevant markup syntax. I'll try to clean up the documentation then
> > once I've brushed up on Sphinx.
> >
> > As noted in a previous patchset version, the signal_write callback
> > should be removed from the interface; there are few if any cases where
> > it makese sense to have a signal_write callback since Signals are
> > always considered inputs in the context of the Counter paradigm.
> >
> > I've retained the signal_write callback in this version since I'm unsure
> > how to implement the dummy-counter Signal source. Benjamin Gaignard
> > suggested implementing dummy-counter as a gpio-counter which could use
> > gpio to provide a software quadratic counter. Is this the path I should
> > take?
>
> It would certainly work well and be simple enough for easy understanding.
> Also, it might be a useful driver in it's own right.
>
> >
> > Furthermore, the dummy-counter driver defines its own single
> > platform_device which restricts it to loading only a single instance.
> > I can fix this to allow multiple instances in the next patchset version
> > -- as suggested, I'll check out industrialio-sw-device.c for reference.
> >
> > Right now the dummy-counter driver only has example code for the Simple
> > Counter interface. It may be prudent to add example code for the Generic
> > Counter and Quadrature Counter interfaces too. I think dummy-counter
> > should serve as the reference driver implementation for all the Counter
> > interfaces, so that driver authors have an example of how to integrate
> > the particular interface they desire.
>
> Such a driver is useful, but it doesn't add much if you have another,
> only slightly more complex real driver that also does the job.
> Perhaps do them all as gpio based drivers for example?
> >
> > Finally, I only added very basic support for the Quadrature Counter
> > interface in the 104-QUAD-8 driver. It's possible to support all
> > existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
> > corresponding quad_counter_device_ext, quad_counter_count_ext, and
> > quad_counter_signal_ext structures, such that only the
> > /sys/bus/counter/devices/counterX/ directory needs to be accessed to
> > interact with the 104-QUAD-8 device. I'll try to add support for those
> > remaining sysfs attributes in the next patchset version.
> >
> > If I missed anything from the last patchset version review just remind
> > me again and I'll add it to my TODO list. ;)
>
> You are seriously optimistic if you think we can remember!
>
> Jonathan
>
> >
> > William Breathitt Gray (11):
> > iio: Introduce the Generic Counter interface
> > counter: Documentation: Add Generic Counter sysfs documentation
> > docs: Add Generic Counter interface documentation
> > counter: Introduce the Simple Counter interface
> > counter: Documentation: Add Simple Counter sysfs documentation
> > docs: Add Simple Counter interface documentation
> > counter: Add dummy counter driver
> > counter: Introduce the Quadrature Counter interface
> > counter: Documentation: Add Quadrature Counter sysfs documentation
> > docs: Add Quadrature Counter interface documentation
> > counter: 104-quad-8: Add Quadrature Counter interface support
> >
> > .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
> > .../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
> > .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
> > Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
> > Documentation/driver-api/iio/index.rst | 3 +
> > Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
> > Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
> > MAINTAINERS | 9 +
> > drivers/iio/Kconfig | 3 +-
> > drivers/iio/counter/104-quad-8.c | 257 +++++-
> > drivers/iio/counter/Kconfig | 35 +-
> > drivers/iio/counter/Makefile | 6 +
> > drivers/iio/counter/dummy-counter.c | 308 +++++++
> > drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
> > drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
> > drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
> > include/linux/iio/counter.h | 629 +++++++++++++
> > 17 files changed, 5216 insertions(+), 15 deletions(-)
> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
> > create mode 100644 Documentation/driver-api/iio/generic-counter.rst
> > create mode 100644 Documentation/driver-api/iio/quad-counter.rst
> > create mode 100644 Documentation/driver-api/iio/simple-counter.rst
> > create mode 100644 drivers/iio/counter/dummy-counter.c
> > create mode 100644 drivers/iio/counter/generic-counter.c
> > create mode 100644 drivers/iio/counter/quad-counter.c
> > create mode 100644 drivers/iio/counter/simple-counter.c
> > create mode 100644 include/linux/iio/counter.h
> >
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
2018-01-01 14:04 GMT+01:00 Jonathan Cameron <[email protected]>:
> On Mon, 1 Jan 2018 11:16:30 +0000
> Jonathan Cameron <[email protected]> wrote:
>
> Sorry to top post but I just want to add some general comments across
> the whole series.
>
> 1) Basics look good to me. It all fits together well.
> 2) I'm concerned about the two 'simplified interfaces' for a couple of reasons:
> a) They add a lot of code and I'm not convinced the simplifications justify that.
> b) They aren't as generically applicable as we might want. For example, it's
> common for SoC Encoder blocks to operative in both the simple counter mode and
> the quadrature counter mode. To support that we have (I think) to go back to
> basics and do it ourselves from the generic counter interface. The TI eQEP
> IP does these modes for example.
>
> So these simplifications serve two purposes (I think)
> 1) To enforce interface. This is nice (and I did some similar stuff in IIO
> for the same reason) but there is so much flexibility in the rest of the
> interface (from a code point of view) that I'm unsure this is significant.
> 2) To save on boiler plate. I'm not sure we save that much and that it couldn't
> mostly be achieved by providing some useful utility functions and
> standard enums / string arrays for the common cases.
>
> We have to justify a couple of thousand lines of core code. To do that we
> need to be saving a reasonably multiple more than that in driver code.
>
> The full setup for a generic_counter is not so complex that we need this
> stuff. Your examples make it all pretty clear what is going on and a
> couple of clean well commented drivers to act as a baseline for new
> implementations would get us much of the rest of the way.
>
> So going well, but this aspect needs some more consideration.
>
> I also think we need at least rough outlines of a few more drivers
> in here to convince people that there aren't any problems that this
> is too inflexible to cover. Hopefully an ST one will be forthcoming.
> If not we can do the exercise off datasheets.
>
Sorry for the long delay before answering to thread.
I have succesfully implement and test a quadrature encoder driver
on stm32 timer part. Some clean up are need but the basic functions
like setting the two supported modes (quadX2 or quadX4) supported by
my hardware, counting, preset and direction are functional.
I have used the "simplified interface" so my driver is quite simple with
only few functions to implement (~300 lines of code).
When this series will be upstream we can convert stm32 drivers to use it.
Thanks a lot for this work.
Benjamin
> Jonathan
>
>> On Thu, 14 Dec 2017 15:50:29 -0500
>> William Breathitt Gray <[email protected]> wrote:
>>
>> > Introduction
>> > ============
>> >
>> > Apologies for going silent these past couple months just to return with
>> > a patchset over 3000 lines larger than the last -- I should have been
>> > releasing intermediate versions along the way so shame on me!
>>
>> :) Sometimes it's better to wait until you are moderately happy with it
>> yourself!
>>
>> > The
>> > Counter system has effectively been rewritten anew, so I believe very
>> > little of the code in the previous versions of this patchset remain.
>> > However, the Generic Counter paradigm has pretty much remained the same
>> > so the theory should be familar. Regardless, I realize I'm dropping off
>> > this patchset near the winter holidays so I don't expect a review until
>> > well into January -- I'm just releasing this now before I myself head
>> > off on an end of year sabbatical.
>>
>> It's at least a few hours into January so here goes before life gets
>> properly busy again.
>>
>> >
>> > The most significant difference between this version and the previous,
>> > as well as part of the reason for the implementation code changes, is
>> > the complete separation of the Generic Counter system from IIO. I
>> > decided it was improper to build the Generic Counter system on top of
>> > IIO core: it was leading to ugly code, convulted hacks, and forced
>> > restrictions on the Generic Counter interface in order to appease the
>> > architecture of the IIO system. Most importantly, the IIO core code that
>> > was leveraged by the Generic Counter was so minor (essentially just the
>> > sysfs attribute support) that it did not justify the extensive
>> > hoop-jumping performed to make the code work.
>> >
>> > So this patchset introduces the Generic Counter interface without the
>> > dependence on IIO code. This now gives the Generic Counter system the
>> > freedom to aptly represent counter devices without implementation
>> > compatibility concerns regarding other high-level subsystems.
>> >
>> > This also makes sense ontologically I believe because whereas the IIO
>> > system appears more focused on representing the industrial I/O of a
>> > device and their configuration directly, the Generic Counter system is
>> > more concerned with the abstract representation of that counter device
>> > and the relationships and configurations within which define its
>> > operation at a high-level; a counter driver could in theory separately
>> > support both the high-level Generic Counter representation of the device
>> > as a whole (what are we counting conceptually, how much are we counting,
>> > etc.), as well as the low-level IIO representation of the individual
>> > inputs and outputs on that device (are the signals differential, do
>> > certain signals have current requirements, etc.).
>>
>> I think there are concepts that over time may blur the lines more
>> but agree with the basic point. I'm just planning to nick all your
>> good ideas if they will improve IIO in turn.
>>
>> >
>> > Overview
>> > ========
>> >
>> > This patchset may be divided into three main groups:
>> >
>> > * Generic Counter
>> > * Simple Counter
>> > * Quadrature Counter
>> >
>> > Each group begins with a patch introducing the implementation of the
>> > interface system, followed afterwards by documentation patches. I
>> > recommend reading through the documentation patches first to familiarize
>> > your with the interface itself before jumping into the source code for
>> > the implementation.
>> >
>> > The Simple Counter and Quadrature Counter groups also have example
>> > driver code in the dummy-counter and 104-quad-8 patches respectively.
>> > The Simple Counter and Quadrature Counter systems themselves being
>> > subclasses of the Generic Counter may serve as example driver code for
>> > the Generic Counter interface -- though I may end up adding an explicit
>> > Generic Counter example in a later patch to the dummy-counter for easier
>> > reference.
>> >
>> > Since the Generic Counter system no longer depends on IIO, I moved all
>> > Counter related source code to the drivers/iio/counter/ directory to
>> > keep everything contained together. In addition, with the IIO Kconfig
>> > dependency removed, the COUNTER menu appear now appears at the same
>> > level as the IIO menu:
>> >
>> > -> Device drivers
>> > -> Counter Support (COUNTER [=m])
>> >
>> > I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
>> > order to match the Kconfig heirarchy or to keep it where it is to match
>> > the legacy IIO counter location established when we first added the
>> > 104-QUAD-8 driver.
>>
>> I would move it out entirely - otherwise things are just confusing.
>> You 'could' sit it in IIO (as in put it under the top level menu option)
>> if you would prefer but I don't thing that really makes sense.
>>
>> >
>> > Paradigm updates
>> > ================
>> >
>> > The Generic Counter paradigm has essentially remained the same from the
>> > previous patch, but I have made some minor updates. In particular, I've
>> > finally settled on a naming convention for the core components of a
>> > Counter:
>> >
>> > COUNT
>> > -----
>> > A Count represents the count data for a set of Signals. A Count
>> > has a count function mode (e.g. "increase" or "quadrature x4")
>> > which represents the update behavior for the count data. A Count
>> > also has a set of one or more associated Signals.
>> >
>> > This component was called "Value" in the previous patches. I believe
>> > "Count" is a more direct name for this data, and it also matches how
>> > datasheets and people commonly refer to this information in
>> > documentation.
>>
>> Agreed - better name.
>>
>> >
>> > SIGNAL
>> > ------
>> > A Signal represents a counter input data; this is the data that
>> > is typically analyzed by the counter to determine the count
>> > data. A Signal may be associated to one or more Counts.
>> >
>> > The naming for this component has not changed since the previous
>> > patches. I believe "Signal" is a fitting enough name for the input
>> > data, as well as matching the common nomenclature for existing counter
>> > devices.
>> >
>> > SYNAPSE
>> > -------
>> > A Synapse represents the association of a Signal with a
>> > respective Count. Signal data affects respective Count data, and
>> > the Synapse represents this relationship. The Synapse action
>> > mode (e.g. "rising edge" or "double pulse") specifies the Signal
>> > data condition which triggers the respective Count's count
>> > function evaluation to update the count data. It is possible for
>> > the Synapse action mode to be "none" if a Signal is associated
>> > with a Count but does not trigger the count function (e.g. the
>> > direction signal line for a Pulse-Direction encoding counter).
>> >
>> > This component was called "Trigger" in the previous patches. I do not
>> > believe "Trigger" was a good name for two main reasons: it could easily
>> > be confused for the existing IIO trigger concept, and most importantly
>> > it does not convey the connection association aspect of the
>> > Count-Signal relationship.
>>
>> An alternative here would be to use MAP as a number of similar
>> 'connection' type arrangements in the kernel do. It doesn't really
>> imply the 'how' element though so perhaps a new term is indeed better.
>>
>>
>> >
>> > I settled on the "Synapse" name both due to etymology -- from Greek
>> > _sunapsis_ meaning "conjunction" -- as well as a biological analogy:
>> > just as neurons connect and fire communication over synapses, so does a
>> > Counter Signal connect and fire communication to a Counter Count over a
>> > Counter Synapse.
>> >
>> > Following the same naming convention and analogy, what was previously
>> > called trigger_mode is now known as action_mode, named in reference to
>> > action potential -- the condition in a neuron which triggers a fire
>> > communication over a synapse, just as a Counter Signal condition
>> > specified in the action_mode of a Counter Synapse triggers the count
>> > function evaluation for a Counter Count.
>> >
>> > Counter classes descriptions
>> > ============================
>> >
>> > The Generic Counter interface is the most general interface for
>> > supporting counter devices; if it qualifies as a Counter, then it can be
>> > represented by the Generic Counter interface. Unfortunately, the
>> > flexibility of the interface does result in a more cumbersome
>> > integration for driver authors: much of the components must be manually
>> > configured by the author, which can be a tedious task for large and
>> > complex counter devices.
>> >
>> > To this end, two subclasses of the Generic Counter interface as
>> > introduced in this patchset: the Simple Counter interface, and the
>> > Quadrature Counter interface. Both of these interfaces inherit the
>> > Generic Counter paradigm, and may be seen as extensions to the interface
>> > which restrict the components to a respective specific class of counter
>> > devices in order to provide a more apt interface for such devices.
>> >
>> > Simple Counter
>> > --------------
>> > Simple Counters are devices that count edge pulses on an input
>> > line (e.g. tally counters).
>> >
>> > Since the relationship between Signals and Counts is known to be
>> > one-to-one, a simple_counter_count structure already contains
>> > the associated Signal member for the respective Count. A driver
>> > author no longer needs to worry about allocating a separate
>> > Signal and Synapse, nor about configuring the association
>> > between the respective Count and Signal; the Simple Counter
>> > interface abstracts away such details.
>> >
>> > Furthermore, since the device type is known, component
>> > properties may be further defined and restricted: Count data is
>> > a signed integer, Signal data "low" and "high" state is set via
>> > enumeration constants, and so are count function and action mode
>> > restricted to well-defined "increase"/"decrease" and
>> > "none"/"rising edge"/"falling edge"/"both edges" enumeration
>> > constants respectively.
>>
>> I do wonder a little on whether this is too restrictive to actually
>> represent many devices.
>> >
>> > Quadrature Counter
>> > ------------------
>> > Quadrature Counters are devices that track position based on
>> > quadrature pair signals (e.g. rotary encoder counters).
>> >
>> > Since the relationship between Signals and Counts is known to be
>> > a quadrature pair of Signals to each Count, a quad_counter_count
>> > structure already contains the associated Signal members for the
>> > respective Count. A driver author no longer needs to worry about
>> > allocating separate Signals and Synapses for each quadrature
>> > pair, nor about configuring the association between the
>> > respective Count and Signals; the Quadrature Counter interface
>> > abstracts away such details.
>> >
>> > Furthermore, since the device type is known, component
>> > properties may be further defined and restricted: Count data is
>> > a signed integer, Signal data "low" and "high" state is set via
>> > enumeration constants, and so is count function mode restricted
>> > to well-defined enumeration constants to represent modes such as
>> > "pulse-direction" and "quadrature x4" for example.
>>
>> Pulse direction is definitely not a quadrature counter... Maybe this needs
>> a rename to dual-signal-counter or similar?
>>
>> Another classic case here would be increment / decrement counters where
>> a signal is used for each operation (counting items between two light gates
>> - used a lot in tracking products in the production industry).
>>
>> >
>> > Note how driver authors no longer need to interact with Synapses
>> > directly when utilizing the Simple Counter and Quadrature Counter
>> > interfaces. This should make it easier too for authors to add support
>> > since they don't need to fully understand the underlying Counter
>> > paradigm in order to take advantage of the interfaces -- just define the
>> > Counts and Signals, and they're ready to go.
>> >
>> > Even more so, the Quadrature Counter interface takes it a step further
>> > and doesn't require action_modes to be explicitly set -- rather they are
>> > implicitly determined internally by the system based on the direction
>> > and function mode. Abstractions like these should make the Counter
>> > interface system as a whole robust enough to handle the diverse classes
>> > of counter devices out in the real world.
>> >
>> > Compilation warnings
>> > ====================
>> >
>> > There are three main compilation warnings which pop for this patchset.
>> > I've inspected these warnings and none are errors, however they do
>> > require some explanation.
>> >
>> > * 104-quad-8: warning: enumeration value
>> > ‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
>> > switch
>> >
>> > The first warning is rather simple to explain: the
>> > QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
>> > statement's else condition, so an explicit case condition is not
>> > necessary. I can add a default case line to pacify the compiler, but
>> > since it would be empty the effort seems frivolous.
>>
>> Do it anyway and put a comment of /* Should not get here */
>>
>> Suppressing false warnings is useful from a code maintenance point of view.
>>
>> > In some sense as
>> > well, a default case may make the switch logic less clear by implying
>> > the possibility of additional cases which are not possible in the
>> > context of that code path.
>> >
>> > * simple-counter: warning: assignment discards ‘const’ qualifier
>> > from pointer target type
>> > * quad-counter: warning: assignment discards ‘const’ qualifier
>> > from pointer target type
>> >
>> > The second warning comes from the mapping of
>> > simple_counter_device_ext/quad_counter_device_ext,
>> > simple_counter_count_ext/quad_counter_count_ext, and
>> > simple_counter_signal_ext/quad_counter_signal_ext to the internal
>> > Counter counter_device_ext, counter_count_ext, and counter_signal_ext
>> > structures respectively.
>> >
>> > The priv member of the counter_device_ext, counter_count_ext, or
>> > counter_signal_ext is leveraged to pass the respective
>> > simple_counter_device_ext/quad_counter_device_ext,
>> > simple_counter_count_ext/quad_counter_count_ext, or
>> > simple_counter_signal_ext/quad_counter_signal_ext structure to their
>> > respective read/write callback. The priv member is generic on purpose to
>> > allow any desired data to be passed; the supplied read/write callbacks
>> > should know the datatype of the passed-in priv argument so they cast it
>> > appropriately to access their expected data.
>> >
>> > As such, the 'const' qualifier of the structures are thus discarded but
>> > subsequently cast back when the respective registered callback functions
>> > are called. Since this is the intended use case of the priv member -- to
>> > generically pass driver data for later recast -- I don't believe this
>> > warning needs to be rectified.
>>
>> All warnings need to be rectified. Sorry but this noise will do two things:
>> 1) Get you a patch every few weeks from someone fixing it.
>> 2) Potentially make real warnings harder to see.
>>
>> Sometimes we have to play games to work around them, but such is life.
>>
>> >
>> > * generic-counter: warning: passing argument 5 of
>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>> > from pointer target type
>> > * generic-counter: warning: passing argument 6 of
>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>> > from pointer target type
>> >
>> > The third warnings comes from a similar situation to the second warning:
>> > a 'const' argument is passed generically via 'void *' for later recast.
>> > In this cast, I decided to create a generic function called
>> > counter_attribute_create in order to simplify the sysfs attribute
>> > registration code in the generic-counter.c file.
>> >
>> > The counter_attribute_create function takes in read and write callbacks,
>> > as well as two optional generic data arguments to be stored as 'void *'
>> > (the component and component_data parameters). Using this setup allows
>> > the counter_attribute_create function to be the sole function necessary
>> > to create a desired Generic Counter sysfs attribute: read and write
>> > callbacks are passed along with relevant Counter component and data
>> > generically, which can be cast back later inside those read and write
>> > functions to match the expected datatype.
>> >
>> > Using a generic counter_attribute_create function reduces duplicate
>> > code, but it does result in many superfluous compilation warnings. I can
>> > define new attribute_create functions specific to each type of sysfs
>> > attribute in order to pacify the warnings, but that seems to be such a
>> > waste to increase the amount of code with duplications that are
>> > unnecessary. What would you recommend; should I attempt to pacify these
>> > warnings or leave them be?
>>
>> You must fix them I'm afraid.
>>
>> >
>> > Known TODO items
>> > ================
>> >
>> > Although I've added the interface documentation files with rst file
>> > extensions, I still need to familiarize myself with Sphinx markup
>> > constructs to take advantage of the language. For example, I've copied
>> > verbatim several structure definitions into the documentation directly,
>> > but I believe this would be better left dynamically generated by using
>> > the relevant markup syntax. I'll try to clean up the documentation then
>> > once I've brushed up on Sphinx.
>> >
>> > As noted in a previous patchset version, the signal_write callback
>> > should be removed from the interface; there are few if any cases where
>> > it makese sense to have a signal_write callback since Signals are
>> > always considered inputs in the context of the Counter paradigm.
>> >
>> > I've retained the signal_write callback in this version since I'm unsure
>> > how to implement the dummy-counter Signal source. Benjamin Gaignard
>> > suggested implementing dummy-counter as a gpio-counter which could use
>> > gpio to provide a software quadratic counter. Is this the path I should
>> > take?
>>
>> It would certainly work well and be simple enough for easy understanding.
>> Also, it might be a useful driver in it's own right.
>>
>> >
>> > Furthermore, the dummy-counter driver defines its own single
>> > platform_device which restricts it to loading only a single instance.
>> > I can fix this to allow multiple instances in the next patchset version
>> > -- as suggested, I'll check out industrialio-sw-device.c for reference.
>> >
>> > Right now the dummy-counter driver only has example code for the Simple
>> > Counter interface. It may be prudent to add example code for the Generic
>> > Counter and Quadrature Counter interfaces too. I think dummy-counter
>> > should serve as the reference driver implementation for all the Counter
>> > interfaces, so that driver authors have an example of how to integrate
>> > the particular interface they desire.
>>
>> Such a driver is useful, but it doesn't add much if you have another,
>> only slightly more complex real driver that also does the job.
>> Perhaps do them all as gpio based drivers for example?
>> >
>> > Finally, I only added very basic support for the Quadrature Counter
>> > interface in the 104-QUAD-8 driver. It's possible to support all
>> > existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
>> > corresponding quad_counter_device_ext, quad_counter_count_ext, and
>> > quad_counter_signal_ext structures, such that only the
>> > /sys/bus/counter/devices/counterX/ directory needs to be accessed to
>> > interact with the 104-QUAD-8 device. I'll try to add support for those
>> > remaining sysfs attributes in the next patchset version.
>> >
>> > If I missed anything from the last patchset version review just remind
>> > me again and I'll add it to my TODO list. ;)
>>
>> You are seriously optimistic if you think we can remember!
>>
>> Jonathan
>>
>> >
>> > William Breathitt Gray (11):
>> > iio: Introduce the Generic Counter interface
>> > counter: Documentation: Add Generic Counter sysfs documentation
>> > docs: Add Generic Counter interface documentation
>> > counter: Introduce the Simple Counter interface
>> > counter: Documentation: Add Simple Counter sysfs documentation
>> > docs: Add Simple Counter interface documentation
>> > counter: Add dummy counter driver
>> > counter: Introduce the Quadrature Counter interface
>> > counter: Documentation: Add Quadrature Counter sysfs documentation
>> > docs: Add Quadrature Counter interface documentation
>> > counter: 104-quad-8: Add Quadrature Counter interface support
>> >
>> > .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
>> > .../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
>> > .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
>> > Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
>> > Documentation/driver-api/iio/index.rst | 3 +
>> > Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
>> > Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
>> > MAINTAINERS | 9 +
>> > drivers/iio/Kconfig | 3 +-
>> > drivers/iio/counter/104-quad-8.c | 257 +++++-
>> > drivers/iio/counter/Kconfig | 35 +-
>> > drivers/iio/counter/Makefile | 6 +
>> > drivers/iio/counter/dummy-counter.c | 308 +++++++
>> > drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
>> > drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
>> > drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
>> > include/linux/iio/counter.h | 629 +++++++++++++
>> > 17 files changed, 5216 insertions(+), 15 deletions(-)
>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
>> > create mode 100644 Documentation/driver-api/iio/generic-counter.rst
>> > create mode 100644 Documentation/driver-api/iio/quad-counter.rst
>> > create mode 100644 Documentation/driver-api/iio/simple-counter.rst
>> > create mode 100644 drivers/iio/counter/dummy-counter.c
>> > create mode 100644 drivers/iio/counter/generic-counter.c
>> > create mode 100644 drivers/iio/counter/quad-counter.c
>> > create mode 100644 drivers/iio/counter/simple-counter.c
>> > create mode 100644 include/linux/iio/counter.h
>> >
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
2018-01-15 10:02 GMT+01:00 Benjamin Gaignard <[email protected]>:
> 2018-01-01 14:04 GMT+01:00 Jonathan Cameron <[email protected]>:
>> On Mon, 1 Jan 2018 11:16:30 +0000
>> Jonathan Cameron <[email protected]> wrote:
>>
>> Sorry to top post but I just want to add some general comments across
>> the whole series.
>>
>> 1) Basics look good to me. It all fits together well.
>> 2) I'm concerned about the two 'simplified interfaces' for a couple of reasons:
>> a) They add a lot of code and I'm not convinced the simplifications justify that.
>> b) They aren't as generically applicable as we might want. For example, it's
>> common for SoC Encoder blocks to operative in both the simple counter mode and
>> the quadrature counter mode. To support that we have (I think) to go back to
>> basics and do it ourselves from the generic counter interface. The TI eQEP
>> IP does these modes for example.
>>
>> So these simplifications serve two purposes (I think)
>> 1) To enforce interface. This is nice (and I did some similar stuff in IIO
>> for the same reason) but there is so much flexibility in the rest of the
>> interface (from a code point of view) that I'm unsure this is significant.
>> 2) To save on boiler plate. I'm not sure we save that much and that it couldn't
>> mostly be achieved by providing some useful utility functions and
>> standard enums / string arrays for the common cases.
>>
>> We have to justify a couple of thousand lines of core code. To do that we
>> need to be saving a reasonably multiple more than that in driver code.
>>
>> The full setup for a generic_counter is not so complex that we need this
>> stuff. Your examples make it all pretty clear what is going on and a
>> couple of clean well commented drivers to act as a baseline for new
>> implementations would get us much of the rest of the way.
>>
>> So going well, but this aspect needs some more consideration.
>>
>> I also think we need at least rough outlines of a few more drivers
>> in here to convince people that there aren't any problems that this
>> is too inflexible to cover. Hopefully an ST one will be forthcoming.
>> If not we can do the exercise off datasheets.
>>
>
> Sorry for the long delay before answering to thread.
> I have succesfully implement and test a quadrature encoder driver
> on stm32 timer part. Some clean up are need but the basic functions
> like setting the two supported modes (quadX2 or quadX4) supported by
> my hardware, counting, preset and direction are functional.
>
> I have used the "simplified interface" so my driver is quite simple with
> only few functions to implement (~300 lines of code).
> When this series will be upstream we can convert stm32 drivers to use it.
>
> Thanks a lot for this work.
> Benjamin
Any news about those patches ?
Regards,
Benjamin
>
>> Jonathan
>>
>>> On Thu, 14 Dec 2017 15:50:29 -0500
>>> William Breathitt Gray <[email protected]> wrote:
>>>
>>> > Introduction
>>> > ============
>>> >
>>> > Apologies for going silent these past couple months just to return with
>>> > a patchset over 3000 lines larger than the last -- I should have been
>>> > releasing intermediate versions along the way so shame on me!
>>>
>>> :) Sometimes it's better to wait until you are moderately happy with it
>>> yourself!
>>>
>>> > The
>>> > Counter system has effectively been rewritten anew, so I believe very
>>> > little of the code in the previous versions of this patchset remain.
>>> > However, the Generic Counter paradigm has pretty much remained the same
>>> > so the theory should be familar. Regardless, I realize I'm dropping off
>>> > this patchset near the winter holidays so I don't expect a review until
>>> > well into January -- I'm just releasing this now before I myself head
>>> > off on an end of year sabbatical.
>>>
>>> It's at least a few hours into January so here goes before life gets
>>> properly busy again.
>>>
>>> >
>>> > The most significant difference between this version and the previous,
>>> > as well as part of the reason for the implementation code changes, is
>>> > the complete separation of the Generic Counter system from IIO. I
>>> > decided it was improper to build the Generic Counter system on top of
>>> > IIO core: it was leading to ugly code, convulted hacks, and forced
>>> > restrictions on the Generic Counter interface in order to appease the
>>> > architecture of the IIO system. Most importantly, the IIO core code that
>>> > was leveraged by the Generic Counter was so minor (essentially just the
>>> > sysfs attribute support) that it did not justify the extensive
>>> > hoop-jumping performed to make the code work.
>>> >
>>> > So this patchset introduces the Generic Counter interface without the
>>> > dependence on IIO code. This now gives the Generic Counter system the
>>> > freedom to aptly represent counter devices without implementation
>>> > compatibility concerns regarding other high-level subsystems.
>>> >
>>> > This also makes sense ontologically I believe because whereas the IIO
>>> > system appears more focused on representing the industrial I/O of a
>>> > device and their configuration directly, the Generic Counter system is
>>> > more concerned with the abstract representation of that counter device
>>> > and the relationships and configurations within which define its
>>> > operation at a high-level; a counter driver could in theory separately
>>> > support both the high-level Generic Counter representation of the device
>>> > as a whole (what are we counting conceptually, how much are we counting,
>>> > etc.), as well as the low-level IIO representation of the individual
>>> > inputs and outputs on that device (are the signals differential, do
>>> > certain signals have current requirements, etc.).
>>>
>>> I think there are concepts that over time may blur the lines more
>>> but agree with the basic point. I'm just planning to nick all your
>>> good ideas if they will improve IIO in turn.
>>>
>>> >
>>> > Overview
>>> > ========
>>> >
>>> > This patchset may be divided into three main groups:
>>> >
>>> > * Generic Counter
>>> > * Simple Counter
>>> > * Quadrature Counter
>>> >
>>> > Each group begins with a patch introducing the implementation of the
>>> > interface system, followed afterwards by documentation patches. I
>>> > recommend reading through the documentation patches first to familiarize
>>> > your with the interface itself before jumping into the source code for
>>> > the implementation.
>>> >
>>> > The Simple Counter and Quadrature Counter groups also have example
>>> > driver code in the dummy-counter and 104-quad-8 patches respectively.
>>> > The Simple Counter and Quadrature Counter systems themselves being
>>> > subclasses of the Generic Counter may serve as example driver code for
>>> > the Generic Counter interface -- though I may end up adding an explicit
>>> > Generic Counter example in a later patch to the dummy-counter for easier
>>> > reference.
>>> >
>>> > Since the Generic Counter system no longer depends on IIO, I moved all
>>> > Counter related source code to the drivers/iio/counter/ directory to
>>> > keep everything contained together. In addition, with the IIO Kconfig
>>> > dependency removed, the COUNTER menu appear now appears at the same
>>> > level as the IIO menu:
>>> >
>>> > -> Device drivers
>>> > -> Counter Support (COUNTER [=m])
>>> >
>>> > I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
>>> > order to match the Kconfig heirarchy or to keep it where it is to match
>>> > the legacy IIO counter location established when we first added the
>>> > 104-QUAD-8 driver.
>>>
>>> I would move it out entirely - otherwise things are just confusing.
>>> You 'could' sit it in IIO (as in put it under the top level menu option)
>>> if you would prefer but I don't thing that really makes sense.
>>>
>>> >
>>> > Paradigm updates
>>> > ================
>>> >
>>> > The Generic Counter paradigm has essentially remained the same from the
>>> > previous patch, but I have made some minor updates. In particular, I've
>>> > finally settled on a naming convention for the core components of a
>>> > Counter:
>>> >
>>> > COUNT
>>> > -----
>>> > A Count represents the count data for a set of Signals. A Count
>>> > has a count function mode (e.g. "increase" or "quadrature x4")
>>> > which represents the update behavior for the count data. A Count
>>> > also has a set of one or more associated Signals.
>>> >
>>> > This component was called "Value" in the previous patches. I believe
>>> > "Count" is a more direct name for this data, and it also matches how
>>> > datasheets and people commonly refer to this information in
>>> > documentation.
>>>
>>> Agreed - better name.
>>>
>>> >
>>> > SIGNAL
>>> > ------
>>> > A Signal represents a counter input data; this is the data that
>>> > is typically analyzed by the counter to determine the count
>>> > data. A Signal may be associated to one or more Counts.
>>> >
>>> > The naming for this component has not changed since the previous
>>> > patches. I believe "Signal" is a fitting enough name for the input
>>> > data, as well as matching the common nomenclature for existing counter
>>> > devices.
>>> >
>>> > SYNAPSE
>>> > -------
>>> > A Synapse represents the association of a Signal with a
>>> > respective Count. Signal data affects respective Count data, and
>>> > the Synapse represents this relationship. The Synapse action
>>> > mode (e.g. "rising edge" or "double pulse") specifies the Signal
>>> > data condition which triggers the respective Count's count
>>> > function evaluation to update the count data. It is possible for
>>> > the Synapse action mode to be "none" if a Signal is associated
>>> > with a Count but does not trigger the count function (e.g. the
>>> > direction signal line for a Pulse-Direction encoding counter).
>>> >
>>> > This component was called "Trigger" in the previous patches. I do not
>>> > believe "Trigger" was a good name for two main reasons: it could easily
>>> > be confused for the existing IIO trigger concept, and most importantly
>>> > it does not convey the connection association aspect of the
>>> > Count-Signal relationship.
>>>
>>> An alternative here would be to use MAP as a number of similar
>>> 'connection' type arrangements in the kernel do. It doesn't really
>>> imply the 'how' element though so perhaps a new term is indeed better.
>>>
>>>
>>> >
>>> > I settled on the "Synapse" name both due to etymology -- from Greek
>>> > _sunapsis_ meaning "conjunction" -- as well as a biological analogy:
>>> > just as neurons connect and fire communication over synapses, so does a
>>> > Counter Signal connect and fire communication to a Counter Count over a
>>> > Counter Synapse.
>>> >
>>> > Following the same naming convention and analogy, what was previously
>>> > called trigger_mode is now known as action_mode, named in reference to
>>> > action potential -- the condition in a neuron which triggers a fire
>>> > communication over a synapse, just as a Counter Signal condition
>>> > specified in the action_mode of a Counter Synapse triggers the count
>>> > function evaluation for a Counter Count.
>>> >
>>> > Counter classes descriptions
>>> > ============================
>>> >
>>> > The Generic Counter interface is the most general interface for
>>> > supporting counter devices; if it qualifies as a Counter, then it can be
>>> > represented by the Generic Counter interface. Unfortunately, the
>>> > flexibility of the interface does result in a more cumbersome
>>> > integration for driver authors: much of the components must be manually
>>> > configured by the author, which can be a tedious task for large and
>>> > complex counter devices.
>>> >
>>> > To this end, two subclasses of the Generic Counter interface as
>>> > introduced in this patchset: the Simple Counter interface, and the
>>> > Quadrature Counter interface. Both of these interfaces inherit the
>>> > Generic Counter paradigm, and may be seen as extensions to the interface
>>> > which restrict the components to a respective specific class of counter
>>> > devices in order to provide a more apt interface for such devices.
>>> >
>>> > Simple Counter
>>> > --------------
>>> > Simple Counters are devices that count edge pulses on an input
>>> > line (e.g. tally counters).
>>> >
>>> > Since the relationship between Signals and Counts is known to be
>>> > one-to-one, a simple_counter_count structure already contains
>>> > the associated Signal member for the respective Count. A driver
>>> > author no longer needs to worry about allocating a separate
>>> > Signal and Synapse, nor about configuring the association
>>> > between the respective Count and Signal; the Simple Counter
>>> > interface abstracts away such details.
>>> >
>>> > Furthermore, since the device type is known, component
>>> > properties may be further defined and restricted: Count data is
>>> > a signed integer, Signal data "low" and "high" state is set via
>>> > enumeration constants, and so are count function and action mode
>>> > restricted to well-defined "increase"/"decrease" and
>>> > "none"/"rising edge"/"falling edge"/"both edges" enumeration
>>> > constants respectively.
>>>
>>> I do wonder a little on whether this is too restrictive to actually
>>> represent many devices.
>>> >
>>> > Quadrature Counter
>>> > ------------------
>>> > Quadrature Counters are devices that track position based on
>>> > quadrature pair signals (e.g. rotary encoder counters).
>>> >
>>> > Since the relationship between Signals and Counts is known to be
>>> > a quadrature pair of Signals to each Count, a quad_counter_count
>>> > structure already contains the associated Signal members for the
>>> > respective Count. A driver author no longer needs to worry about
>>> > allocating separate Signals and Synapses for each quadrature
>>> > pair, nor about configuring the association between the
>>> > respective Count and Signals; the Quadrature Counter interface
>>> > abstracts away such details.
>>> >
>>> > Furthermore, since the device type is known, component
>>> > properties may be further defined and restricted: Count data is
>>> > a signed integer, Signal data "low" and "high" state is set via
>>> > enumeration constants, and so is count function mode restricted
>>> > to well-defined enumeration constants to represent modes such as
>>> > "pulse-direction" and "quadrature x4" for example.
>>>
>>> Pulse direction is definitely not a quadrature counter... Maybe this needs
>>> a rename to dual-signal-counter or similar?
>>>
>>> Another classic case here would be increment / decrement counters where
>>> a signal is used for each operation (counting items between two light gates
>>> - used a lot in tracking products in the production industry).
>>>
>>> >
>>> > Note how driver authors no longer need to interact with Synapses
>>> > directly when utilizing the Simple Counter and Quadrature Counter
>>> > interfaces. This should make it easier too for authors to add support
>>> > since they don't need to fully understand the underlying Counter
>>> > paradigm in order to take advantage of the interfaces -- just define the
>>> > Counts and Signals, and they're ready to go.
>>> >
>>> > Even more so, the Quadrature Counter interface takes it a step further
>>> > and doesn't require action_modes to be explicitly set -- rather they are
>>> > implicitly determined internally by the system based on the direction
>>> > and function mode. Abstractions like these should make the Counter
>>> > interface system as a whole robust enough to handle the diverse classes
>>> > of counter devices out in the real world.
>>> >
>>> > Compilation warnings
>>> > ====================
>>> >
>>> > There are three main compilation warnings which pop for this patchset.
>>> > I've inspected these warnings and none are errors, however they do
>>> > require some explanation.
>>> >
>>> > * 104-quad-8: warning: enumeration value
>>> > ‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
>>> > switch
>>> >
>>> > The first warning is rather simple to explain: the
>>> > QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
>>> > statement's else condition, so an explicit case condition is not
>>> > necessary. I can add a default case line to pacify the compiler, but
>>> > since it would be empty the effort seems frivolous.
>>>
>>> Do it anyway and put a comment of /* Should not get here */
>>>
>>> Suppressing false warnings is useful from a code maintenance point of view.
>>>
>>> > In some sense as
>>> > well, a default case may make the switch logic less clear by implying
>>> > the possibility of additional cases which are not possible in the
>>> > context of that code path.
>>> >
>>> > * simple-counter: warning: assignment discards ‘const’ qualifier
>>> > from pointer target type
>>> > * quad-counter: warning: assignment discards ‘const’ qualifier
>>> > from pointer target type
>>> >
>>> > The second warning comes from the mapping of
>>> > simple_counter_device_ext/quad_counter_device_ext,
>>> > simple_counter_count_ext/quad_counter_count_ext, and
>>> > simple_counter_signal_ext/quad_counter_signal_ext to the internal
>>> > Counter counter_device_ext, counter_count_ext, and counter_signal_ext
>>> > structures respectively.
>>> >
>>> > The priv member of the counter_device_ext, counter_count_ext, or
>>> > counter_signal_ext is leveraged to pass the respective
>>> > simple_counter_device_ext/quad_counter_device_ext,
>>> > simple_counter_count_ext/quad_counter_count_ext, or
>>> > simple_counter_signal_ext/quad_counter_signal_ext structure to their
>>> > respective read/write callback. The priv member is generic on purpose to
>>> > allow any desired data to be passed; the supplied read/write callbacks
>>> > should know the datatype of the passed-in priv argument so they cast it
>>> > appropriately to access their expected data.
>>> >
>>> > As such, the 'const' qualifier of the structures are thus discarded but
>>> > subsequently cast back when the respective registered callback functions
>>> > are called. Since this is the intended use case of the priv member -- to
>>> > generically pass driver data for later recast -- I don't believe this
>>> > warning needs to be rectified.
>>>
>>> All warnings need to be rectified. Sorry but this noise will do two things:
>>> 1) Get you a patch every few weeks from someone fixing it.
>>> 2) Potentially make real warnings harder to see.
>>>
>>> Sometimes we have to play games to work around them, but such is life.
>>>
>>> >
>>> > * generic-counter: warning: passing argument 5 of
>>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>>> > from pointer target type
>>> > * generic-counter: warning: passing argument 6 of
>>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>>> > from pointer target type
>>> >
>>> > The third warnings comes from a similar situation to the second warning:
>>> > a 'const' argument is passed generically via 'void *' for later recast.
>>> > In this cast, I decided to create a generic function called
>>> > counter_attribute_create in order to simplify the sysfs attribute
>>> > registration code in the generic-counter.c file.
>>> >
>>> > The counter_attribute_create function takes in read and write callbacks,
>>> > as well as two optional generic data arguments to be stored as 'void *'
>>> > (the component and component_data parameters). Using this setup allows
>>> > the counter_attribute_create function to be the sole function necessary
>>> > to create a desired Generic Counter sysfs attribute: read and write
>>> > callbacks are passed along with relevant Counter component and data
>>> > generically, which can be cast back later inside those read and write
>>> > functions to match the expected datatype.
>>> >
>>> > Using a generic counter_attribute_create function reduces duplicate
>>> > code, but it does result in many superfluous compilation warnings. I can
>>> > define new attribute_create functions specific to each type of sysfs
>>> > attribute in order to pacify the warnings, but that seems to be such a
>>> > waste to increase the amount of code with duplications that are
>>> > unnecessary. What would you recommend; should I attempt to pacify these
>>> > warnings or leave them be?
>>>
>>> You must fix them I'm afraid.
>>>
>>> >
>>> > Known TODO items
>>> > ================
>>> >
>>> > Although I've added the interface documentation files with rst file
>>> > extensions, I still need to familiarize myself with Sphinx markup
>>> > constructs to take advantage of the language. For example, I've copied
>>> > verbatim several structure definitions into the documentation directly,
>>> > but I believe this would be better left dynamically generated by using
>>> > the relevant markup syntax. I'll try to clean up the documentation then
>>> > once I've brushed up on Sphinx.
>>> >
>>> > As noted in a previous patchset version, the signal_write callback
>>> > should be removed from the interface; there are few if any cases where
>>> > it makese sense to have a signal_write callback since Signals are
>>> > always considered inputs in the context of the Counter paradigm.
>>> >
>>> > I've retained the signal_write callback in this version since I'm unsure
>>> > how to implement the dummy-counter Signal source. Benjamin Gaignard
>>> > suggested implementing dummy-counter as a gpio-counter which could use
>>> > gpio to provide a software quadratic counter. Is this the path I should
>>> > take?
>>>
>>> It would certainly work well and be simple enough for easy understanding.
>>> Also, it might be a useful driver in it's own right.
>>>
>>> >
>>> > Furthermore, the dummy-counter driver defines its own single
>>> > platform_device which restricts it to loading only a single instance.
>>> > I can fix this to allow multiple instances in the next patchset version
>>> > -- as suggested, I'll check out industrialio-sw-device.c for reference.
>>> >
>>> > Right now the dummy-counter driver only has example code for the Simple
>>> > Counter interface. It may be prudent to add example code for the Generic
>>> > Counter and Quadrature Counter interfaces too. I think dummy-counter
>>> > should serve as the reference driver implementation for all the Counter
>>> > interfaces, so that driver authors have an example of how to integrate
>>> > the particular interface they desire.
>>>
>>> Such a driver is useful, but it doesn't add much if you have another,
>>> only slightly more complex real driver that also does the job.
>>> Perhaps do them all as gpio based drivers for example?
>>> >
>>> > Finally, I only added very basic support for the Quadrature Counter
>>> > interface in the 104-QUAD-8 driver. It's possible to support all
>>> > existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
>>> > corresponding quad_counter_device_ext, quad_counter_count_ext, and
>>> > quad_counter_signal_ext structures, such that only the
>>> > /sys/bus/counter/devices/counterX/ directory needs to be accessed to
>>> > interact with the 104-QUAD-8 device. I'll try to add support for those
>>> > remaining sysfs attributes in the next patchset version.
>>> >
>>> > If I missed anything from the last patchset version review just remind
>>> > me again and I'll add it to my TODO list. ;)
>>>
>>> You are seriously optimistic if you think we can remember!
>>>
>>> Jonathan
>>>
>>> >
>>> > William Breathitt Gray (11):
>>> > iio: Introduce the Generic Counter interface
>>> > counter: Documentation: Add Generic Counter sysfs documentation
>>> > docs: Add Generic Counter interface documentation
>>> > counter: Introduce the Simple Counter interface
>>> > counter: Documentation: Add Simple Counter sysfs documentation
>>> > docs: Add Simple Counter interface documentation
>>> > counter: Add dummy counter driver
>>> > counter: Introduce the Quadrature Counter interface
>>> > counter: Documentation: Add Quadrature Counter sysfs documentation
>>> > docs: Add Quadrature Counter interface documentation
>>> > counter: 104-quad-8: Add Quadrature Counter interface support
>>> >
>>> > .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
>>> > .../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
>>> > .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
>>> > Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
>>> > Documentation/driver-api/iio/index.rst | 3 +
>>> > Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
>>> > Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
>>> > MAINTAINERS | 9 +
>>> > drivers/iio/Kconfig | 3 +-
>>> > drivers/iio/counter/104-quad-8.c | 257 +++++-
>>> > drivers/iio/counter/Kconfig | 35 +-
>>> > drivers/iio/counter/Makefile | 6 +
>>> > drivers/iio/counter/dummy-counter.c | 308 +++++++
>>> > drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
>>> > drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
>>> > drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
>>> > include/linux/iio/counter.h | 629 +++++++++++++
>>> > 17 files changed, 5216 insertions(+), 15 deletions(-)
>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
>>> > create mode 100644 Documentation/driver-api/iio/generic-counter.rst
>>> > create mode 100644 Documentation/driver-api/iio/quad-counter.rst
>>> > create mode 100644 Documentation/driver-api/iio/simple-counter.rst
>>> > create mode 100644 drivers/iio/counter/dummy-counter.c
>>> > create mode 100644 drivers/iio/counter/generic-counter.c
>>> > create mode 100644 drivers/iio/counter/quad-counter.c
>>> > create mode 100644 drivers/iio/counter/simple-counter.c
>>> > create mode 100644 include/linux/iio/counter.h
>>> >
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to [email protected]
>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
On Fri, Feb 23, 2018 at 01:58:36PM +0100, Benjamin Gaignard wrote:
>2018-01-15 10:02 GMT+01:00 Benjamin Gaignard <[email protected]>:
>> 2018-01-01 14:04 GMT+01:00 Jonathan Cameron <[email protected]>:
>>> On Mon, 1 Jan 2018 11:16:30 +0000
>>> Jonathan Cameron <[email protected]> wrote:
>>>
>>> Sorry to top post but I just want to add some general comments across
>>> the whole series.
>>>
>>> 1) Basics look good to me. It all fits together well.
>>> 2) I'm concerned about the two 'simplified interfaces' for a couple of reasons:
>>> a) They add a lot of code and I'm not convinced the simplifications justify that.
>>> b) They aren't as generically applicable as we might want. For example, it's
>>> common for SoC Encoder blocks to operative in both the simple counter mode and
>>> the quadrature counter mode. To support that we have (I think) to go back to
>>> basics and do it ourselves from the generic counter interface. The TI eQEP
>>> IP does these modes for example.
>>>
>>> So these simplifications serve two purposes (I think)
>>> 1) To enforce interface. This is nice (and I did some similar stuff in IIO
>>> for the same reason) but there is so much flexibility in the rest of the
>>> interface (from a code point of view) that I'm unsure this is significant.
>>> 2) To save on boiler plate. I'm not sure we save that much and that it couldn't
>>> mostly be achieved by providing some useful utility functions and
>>> standard enums / string arrays for the common cases.
>>>
>>> We have to justify a couple of thousand lines of core code. To do that we
>>> need to be saving a reasonably multiple more than that in driver code.
>>>
>>> The full setup for a generic_counter is not so complex that we need this
>>> stuff. Your examples make it all pretty clear what is going on and a
>>> couple of clean well commented drivers to act as a baseline for new
>>> implementations would get us much of the rest of the way.
>>>
>>> So going well, but this aspect needs some more consideration.
>>>
>>> I also think we need at least rough outlines of a few more drivers
>>> in here to convince people that there aren't any problems that this
>>> is too inflexible to cover. Hopefully an ST one will be forthcoming.
>>> If not we can do the exercise off datasheets.
>>>
>>
>> Sorry for the long delay before answering to thread.
>> I have succesfully implement and test a quadrature encoder driver
>> on stm32 timer part. Some clean up are need but the basic functions
>> like setting the two supported modes (quadX2 or quadX4) supported by
>> my hardware, counting, preset and direction are functional.
>>
>> I have used the "simplified interface" so my driver is quite simple with
>> only few functions to implement (~300 lines of code).
>> When this series will be upstream we can convert stm32 drivers to use it.
>>
>> Thanks a lot for this work.
>> Benjamin
>
>Any news about those patches ?
>
>Regards,
>Benjamin
Hi Benjamin,
Sorry for going dark all this time, I'm still incorporating the changes
suggested by Jonathan in his review. The biggest change will likely be a
reimplementation of the "simple" and "quadrature" API as macros
leveraging the "generic" API in order to reduce a lot of redundant code
under the hood.
I think I might be comfortable releasing the next revision of the
patchset on March 3 or 4, so keep an eye out for it then. :)
William Breathitt Gray
>
>>
>>> Jonathan
>>>
>>>> On Thu, 14 Dec 2017 15:50:29 -0500
>>>> William Breathitt Gray <[email protected]> wrote:
>>>>
>>>> > Introduction
>>>> > ============
>>>> >
>>>> > Apologies for going silent these past couple months just to return with
>>>> > a patchset over 3000 lines larger than the last -- I should have been
>>>> > releasing intermediate versions along the way so shame on me!
>>>>
>>>> :) Sometimes it's better to wait until you are moderately happy with it
>>>> yourself!
>>>>
>>>> > The
>>>> > Counter system has effectively been rewritten anew, so I believe very
>>>> > little of the code in the previous versions of this patchset remain.
>>>> > However, the Generic Counter paradigm has pretty much remained the same
>>>> > so the theory should be familar. Regardless, I realize I'm dropping off
>>>> > this patchset near the winter holidays so I don't expect a review until
>>>> > well into January -- I'm just releasing this now before I myself head
>>>> > off on an end of year sabbatical.
>>>>
>>>> It's at least a few hours into January so here goes before life gets
>>>> properly busy again.
>>>>
>>>> >
>>>> > The most significant difference between this version and the previous,
>>>> > as well as part of the reason for the implementation code changes, is
>>>> > the complete separation of the Generic Counter system from IIO. I
>>>> > decided it was improper to build the Generic Counter system on top of
>>>> > IIO core: it was leading to ugly code, convulted hacks, and forced
>>>> > restrictions on the Generic Counter interface in order to appease the
>>>> > architecture of the IIO system. Most importantly, the IIO core code that
>>>> > was leveraged by the Generic Counter was so minor (essentially just the
>>>> > sysfs attribute support) that it did not justify the extensive
>>>> > hoop-jumping performed to make the code work.
>>>> >
>>>> > So this patchset introduces the Generic Counter interface without the
>>>> > dependence on IIO code. This now gives the Generic Counter system the
>>>> > freedom to aptly represent counter devices without implementation
>>>> > compatibility concerns regarding other high-level subsystems.
>>>> >
>>>> > This also makes sense ontologically I believe because whereas the IIO
>>>> > system appears more focused on representing the industrial I/O of a
>>>> > device and their configuration directly, the Generic Counter system is
>>>> > more concerned with the abstract representation of that counter device
>>>> > and the relationships and configurations within which define its
>>>> > operation at a high-level; a counter driver could in theory separately
>>>> > support both the high-level Generic Counter representation of the device
>>>> > as a whole (what are we counting conceptually, how much are we counting,
>>>> > etc.), as well as the low-level IIO representation of the individual
>>>> > inputs and outputs on that device (are the signals differential, do
>>>> > certain signals have current requirements, etc.).
>>>>
>>>> I think there are concepts that over time may blur the lines more
>>>> but agree with the basic point. I'm just planning to nick all your
>>>> good ideas if they will improve IIO in turn.
>>>>
>>>> >
>>>> > Overview
>>>> > ========
>>>> >
>>>> > This patchset may be divided into three main groups:
>>>> >
>>>> > * Generic Counter
>>>> > * Simple Counter
>>>> > * Quadrature Counter
>>>> >
>>>> > Each group begins with a patch introducing the implementation of the
>>>> > interface system, followed afterwards by documentation patches. I
>>>> > recommend reading through the documentation patches first to familiarize
>>>> > your with the interface itself before jumping into the source code for
>>>> > the implementation.
>>>> >
>>>> > The Simple Counter and Quadrature Counter groups also have example
>>>> > driver code in the dummy-counter and 104-quad-8 patches respectively.
>>>> > The Simple Counter and Quadrature Counter systems themselves being
>>>> > subclasses of the Generic Counter may serve as example driver code for
>>>> > the Generic Counter interface -- though I may end up adding an explicit
>>>> > Generic Counter example in a later patch to the dummy-counter for easier
>>>> > reference.
>>>> >
>>>> > Since the Generic Counter system no longer depends on IIO, I moved all
>>>> > Counter related source code to the drivers/iio/counter/ directory to
>>>> > keep everything contained together. In addition, with the IIO Kconfig
>>>> > dependency removed, the COUNTER menu appear now appears at the same
>>>> > level as the IIO menu:
>>>> >
>>>> > -> Device drivers
>>>> > -> Counter Support (COUNTER [=m])
>>>> >
>>>> > I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
>>>> > order to match the Kconfig heirarchy or to keep it where it is to match
>>>> > the legacy IIO counter location established when we first added the
>>>> > 104-QUAD-8 driver.
>>>>
>>>> I would move it out entirely - otherwise things are just confusing.
>>>> You 'could' sit it in IIO (as in put it under the top level menu option)
>>>> if you would prefer but I don't thing that really makes sense.
>>>>
>>>> >
>>>> > Paradigm updates
>>>> > ================
>>>> >
>>>> > The Generic Counter paradigm has essentially remained the same from the
>>>> > previous patch, but I have made some minor updates. In particular, I've
>>>> > finally settled on a naming convention for the core components of a
>>>> > Counter:
>>>> >
>>>> > COUNT
>>>> > -----
>>>> > A Count represents the count data for a set of Signals. A Count
>>>> > has a count function mode (e.g. "increase" or "quadrature x4")
>>>> > which represents the update behavior for the count data. A Count
>>>> > also has a set of one or more associated Signals.
>>>> >
>>>> > This component was called "Value" in the previous patches. I believe
>>>> > "Count" is a more direct name for this data, and it also matches how
>>>> > datasheets and people commonly refer to this information in
>>>> > documentation.
>>>>
>>>> Agreed - better name.
>>>>
>>>> >
>>>> > SIGNAL
>>>> > ------
>>>> > A Signal represents a counter input data; this is the data that
>>>> > is typically analyzed by the counter to determine the count
>>>> > data. A Signal may be associated to one or more Counts.
>>>> >
>>>> > The naming for this component has not changed since the previous
>>>> > patches. I believe "Signal" is a fitting enough name for the input
>>>> > data, as well as matching the common nomenclature for existing counter
>>>> > devices.
>>>> >
>>>> > SYNAPSE
>>>> > -------
>>>> > A Synapse represents the association of a Signal with a
>>>> > respective Count. Signal data affects respective Count data, and
>>>> > the Synapse represents this relationship. The Synapse action
>>>> > mode (e.g. "rising edge" or "double pulse") specifies the Signal
>>>> > data condition which triggers the respective Count's count
>>>> > function evaluation to update the count data. It is possible for
>>>> > the Synapse action mode to be "none" if a Signal is associated
>>>> > with a Count but does not trigger the count function (e.g. the
>>>> > direction signal line for a Pulse-Direction encoding counter).
>>>> >
>>>> > This component was called "Trigger" in the previous patches. I do not
>>>> > believe "Trigger" was a good name for two main reasons: it could easily
>>>> > be confused for the existing IIO trigger concept, and most importantly
>>>> > it does not convey the connection association aspect of the
>>>> > Count-Signal relationship.
>>>>
>>>> An alternative here would be to use MAP as a number of similar
>>>> 'connection' type arrangements in the kernel do. It doesn't really
>>>> imply the 'how' element though so perhaps a new term is indeed better.
>>>>
>>>>
>>>> >
>>>> > I settled on the "Synapse" name both due to etymology -- from Greek
>>>> > _sunapsis_ meaning "conjunction" -- as well as a biological analogy:
>>>> > just as neurons connect and fire communication over synapses, so does a
>>>> > Counter Signal connect and fire communication to a Counter Count over a
>>>> > Counter Synapse.
>>>> >
>>>> > Following the same naming convention and analogy, what was previously
>>>> > called trigger_mode is now known as action_mode, named in reference to
>>>> > action potential -- the condition in a neuron which triggers a fire
>>>> > communication over a synapse, just as a Counter Signal condition
>>>> > specified in the action_mode of a Counter Synapse triggers the count
>>>> > function evaluation for a Counter Count.
>>>> >
>>>> > Counter classes descriptions
>>>> > ============================
>>>> >
>>>> > The Generic Counter interface is the most general interface for
>>>> > supporting counter devices; if it qualifies as a Counter, then it can be
>>>> > represented by the Generic Counter interface. Unfortunately, the
>>>> > flexibility of the interface does result in a more cumbersome
>>>> > integration for driver authors: much of the components must be manually
>>>> > configured by the author, which can be a tedious task for large and
>>>> > complex counter devices.
>>>> >
>>>> > To this end, two subclasses of the Generic Counter interface as
>>>> > introduced in this patchset: the Simple Counter interface, and the
>>>> > Quadrature Counter interface. Both of these interfaces inherit the
>>>> > Generic Counter paradigm, and may be seen as extensions to the interface
>>>> > which restrict the components to a respective specific class of counter
>>>> > devices in order to provide a more apt interface for such devices.
>>>> >
>>>> > Simple Counter
>>>> > --------------
>>>> > Simple Counters are devices that count edge pulses on an input
>>>> > line (e.g. tally counters).
>>>> >
>>>> > Since the relationship between Signals and Counts is known to be
>>>> > one-to-one, a simple_counter_count structure already contains
>>>> > the associated Signal member for the respective Count. A driver
>>>> > author no longer needs to worry about allocating a separate
>>>> > Signal and Synapse, nor about configuring the association
>>>> > between the respective Count and Signal; the Simple Counter
>>>> > interface abstracts away such details.
>>>> >
>>>> > Furthermore, since the device type is known, component
>>>> > properties may be further defined and restricted: Count data is
>>>> > a signed integer, Signal data "low" and "high" state is set via
>>>> > enumeration constants, and so are count function and action mode
>>>> > restricted to well-defined "increase"/"decrease" and
>>>> > "none"/"rising edge"/"falling edge"/"both edges" enumeration
>>>> > constants respectively.
>>>>
>>>> I do wonder a little on whether this is too restrictive to actually
>>>> represent many devices.
>>>> >
>>>> > Quadrature Counter
>>>> > ------------------
>>>> > Quadrature Counters are devices that track position based on
>>>> > quadrature pair signals (e.g. rotary encoder counters).
>>>> >
>>>> > Since the relationship between Signals and Counts is known to be
>>>> > a quadrature pair of Signals to each Count, a quad_counter_count
>>>> > structure already contains the associated Signal members for the
>>>> > respective Count. A driver author no longer needs to worry about
>>>> > allocating separate Signals and Synapses for each quadrature
>>>> > pair, nor about configuring the association between the
>>>> > respective Count and Signals; the Quadrature Counter interface
>>>> > abstracts away such details.
>>>> >
>>>> > Furthermore, since the device type is known, component
>>>> > properties may be further defined and restricted: Count data is
>>>> > a signed integer, Signal data "low" and "high" state is set via
>>>> > enumeration constants, and so is count function mode restricted
>>>> > to well-defined enumeration constants to represent modes such as
>>>> > "pulse-direction" and "quadrature x4" for example.
>>>>
>>>> Pulse direction is definitely not a quadrature counter... Maybe this needs
>>>> a rename to dual-signal-counter or similar?
>>>>
>>>> Another classic case here would be increment / decrement counters where
>>>> a signal is used for each operation (counting items between two light gates
>>>> - used a lot in tracking products in the production industry).
>>>>
>>>> >
>>>> > Note how driver authors no longer need to interact with Synapses
>>>> > directly when utilizing the Simple Counter and Quadrature Counter
>>>> > interfaces. This should make it easier too for authors to add support
>>>> > since they don't need to fully understand the underlying Counter
>>>> > paradigm in order to take advantage of the interfaces -- just define the
>>>> > Counts and Signals, and they're ready to go.
>>>> >
>>>> > Even more so, the Quadrature Counter interface takes it a step further
>>>> > and doesn't require action_modes to be explicitly set -- rather they are
>>>> > implicitly determined internally by the system based on the direction
>>>> > and function mode. Abstractions like these should make the Counter
>>>> > interface system as a whole robust enough to handle the diverse classes
>>>> > of counter devices out in the real world.
>>>> >
>>>> > Compilation warnings
>>>> > ====================
>>>> >
>>>> > There are three main compilation warnings which pop for this patchset.
>>>> > I've inspected these warnings and none are errors, however they do
>>>> > require some explanation.
>>>> >
>>>> > * 104-quad-8: warning: enumeration value
>>>> > ‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
>>>> > switch
>>>> >
>>>> > The first warning is rather simple to explain: the
>>>> > QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
>>>> > statement's else condition, so an explicit case condition is not
>>>> > necessary. I can add a default case line to pacify the compiler, but
>>>> > since it would be empty the effort seems frivolous.
>>>>
>>>> Do it anyway and put a comment of /* Should not get here */
>>>>
>>>> Suppressing false warnings is useful from a code maintenance point of view.
>>>>
>>>> > In some sense as
>>>> > well, a default case may make the switch logic less clear by implying
>>>> > the possibility of additional cases which are not possible in the
>>>> > context of that code path.
>>>> >
>>>> > * simple-counter: warning: assignment discards ‘const’ qualifier
>>>> > from pointer target type
>>>> > * quad-counter: warning: assignment discards ‘const’ qualifier
>>>> > from pointer target type
>>>> >
>>>> > The second warning comes from the mapping of
>>>> > simple_counter_device_ext/quad_counter_device_ext,
>>>> > simple_counter_count_ext/quad_counter_count_ext, and
>>>> > simple_counter_signal_ext/quad_counter_signal_ext to the internal
>>>> > Counter counter_device_ext, counter_count_ext, and counter_signal_ext
>>>> > structures respectively.
>>>> >
>>>> > The priv member of the counter_device_ext, counter_count_ext, or
>>>> > counter_signal_ext is leveraged to pass the respective
>>>> > simple_counter_device_ext/quad_counter_device_ext,
>>>> > simple_counter_count_ext/quad_counter_count_ext, or
>>>> > simple_counter_signal_ext/quad_counter_signal_ext structure to their
>>>> > respective read/write callback. The priv member is generic on purpose to
>>>> > allow any desired data to be passed; the supplied read/write callbacks
>>>> > should know the datatype of the passed-in priv argument so they cast it
>>>> > appropriately to access their expected data.
>>>> >
>>>> > As such, the 'const' qualifier of the structures are thus discarded but
>>>> > subsequently cast back when the respective registered callback functions
>>>> > are called. Since this is the intended use case of the priv member -- to
>>>> > generically pass driver data for later recast -- I don't believe this
>>>> > warning needs to be rectified.
>>>>
>>>> All warnings need to be rectified. Sorry but this noise will do two things:
>>>> 1) Get you a patch every few weeks from someone fixing it.
>>>> 2) Potentially make real warnings harder to see.
>>>>
>>>> Sometimes we have to play games to work around them, but such is life.
>>>>
>>>> >
>>>> > * generic-counter: warning: passing argument 5 of
>>>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>>>> > from pointer target type
>>>> > * generic-counter: warning: passing argument 6 of
>>>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>>>> > from pointer target type
>>>> >
>>>> > The third warnings comes from a similar situation to the second warning:
>>>> > a 'const' argument is passed generically via 'void *' for later recast.
>>>> > In this cast, I decided to create a generic function called
>>>> > counter_attribute_create in order to simplify the sysfs attribute
>>>> > registration code in the generic-counter.c file.
>>>> >
>>>> > The counter_attribute_create function takes in read and write callbacks,
>>>> > as well as two optional generic data arguments to be stored as 'void *'
>>>> > (the component and component_data parameters). Using this setup allows
>>>> > the counter_attribute_create function to be the sole function necessary
>>>> > to create a desired Generic Counter sysfs attribute: read and write
>>>> > callbacks are passed along with relevant Counter component and data
>>>> > generically, which can be cast back later inside those read and write
>>>> > functions to match the expected datatype.
>>>> >
>>>> > Using a generic counter_attribute_create function reduces duplicate
>>>> > code, but it does result in many superfluous compilation warnings. I can
>>>> > define new attribute_create functions specific to each type of sysfs
>>>> > attribute in order to pacify the warnings, but that seems to be such a
>>>> > waste to increase the amount of code with duplications that are
>>>> > unnecessary. What would you recommend; should I attempt to pacify these
>>>> > warnings or leave them be?
>>>>
>>>> You must fix them I'm afraid.
>>>>
>>>> >
>>>> > Known TODO items
>>>> > ================
>>>> >
>>>> > Although I've added the interface documentation files with rst file
>>>> > extensions, I still need to familiarize myself with Sphinx markup
>>>> > constructs to take advantage of the language. For example, I've copied
>>>> > verbatim several structure definitions into the documentation directly,
>>>> > but I believe this would be better left dynamically generated by using
>>>> > the relevant markup syntax. I'll try to clean up the documentation then
>>>> > once I've brushed up on Sphinx.
>>>> >
>>>> > As noted in a previous patchset version, the signal_write callback
>>>> > should be removed from the interface; there are few if any cases where
>>>> > it makese sense to have a signal_write callback since Signals are
>>>> > always considered inputs in the context of the Counter paradigm.
>>>> >
>>>> > I've retained the signal_write callback in this version since I'm unsure
>>>> > how to implement the dummy-counter Signal source. Benjamin Gaignard
>>>> > suggested implementing dummy-counter as a gpio-counter which could use
>>>> > gpio to provide a software quadratic counter. Is this the path I should
>>>> > take?
>>>>
>>>> It would certainly work well and be simple enough for easy understanding.
>>>> Also, it might be a useful driver in it's own right.
>>>>
>>>> >
>>>> > Furthermore, the dummy-counter driver defines its own single
>>>> > platform_device which restricts it to loading only a single instance.
>>>> > I can fix this to allow multiple instances in the next patchset version
>>>> > -- as suggested, I'll check out industrialio-sw-device.c for reference.
>>>> >
>>>> > Right now the dummy-counter driver only has example code for the Simple
>>>> > Counter interface. It may be prudent to add example code for the Generic
>>>> > Counter and Quadrature Counter interfaces too. I think dummy-counter
>>>> > should serve as the reference driver implementation for all the Counter
>>>> > interfaces, so that driver authors have an example of how to integrate
>>>> > the particular interface they desire.
>>>>
>>>> Such a driver is useful, but it doesn't add much if you have another,
>>>> only slightly more complex real driver that also does the job.
>>>> Perhaps do them all as gpio based drivers for example?
>>>> >
>>>> > Finally, I only added very basic support for the Quadrature Counter
>>>> > interface in the 104-QUAD-8 driver. It's possible to support all
>>>> > existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
>>>> > corresponding quad_counter_device_ext, quad_counter_count_ext, and
>>>> > quad_counter_signal_ext structures, such that only the
>>>> > /sys/bus/counter/devices/counterX/ directory needs to be accessed to
>>>> > interact with the 104-QUAD-8 device. I'll try to add support for those
>>>> > remaining sysfs attributes in the next patchset version.
>>>> >
>>>> > If I missed anything from the last patchset version review just remind
>>>> > me again and I'll add it to my TODO list. ;)
>>>>
>>>> You are seriously optimistic if you think we can remember!
>>>>
>>>> Jonathan
>>>>
>>>> >
>>>> > William Breathitt Gray (11):
>>>> > iio: Introduce the Generic Counter interface
>>>> > counter: Documentation: Add Generic Counter sysfs documentation
>>>> > docs: Add Generic Counter interface documentation
>>>> > counter: Introduce the Simple Counter interface
>>>> > counter: Documentation: Add Simple Counter sysfs documentation
>>>> > docs: Add Simple Counter interface documentation
>>>> > counter: Add dummy counter driver
>>>> > counter: Introduce the Quadrature Counter interface
>>>> > counter: Documentation: Add Quadrature Counter sysfs documentation
>>>> > docs: Add Quadrature Counter interface documentation
>>>> > counter: 104-quad-8: Add Quadrature Counter interface support
>>>> >
>>>> > .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
>>>> > .../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
>>>> > .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
>>>> > Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
>>>> > Documentation/driver-api/iio/index.rst | 3 +
>>>> > Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
>>>> > Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
>>>> > MAINTAINERS | 9 +
>>>> > drivers/iio/Kconfig | 3 +-
>>>> > drivers/iio/counter/104-quad-8.c | 257 +++++-
>>>> > drivers/iio/counter/Kconfig | 35 +-
>>>> > drivers/iio/counter/Makefile | 6 +
>>>> > drivers/iio/counter/dummy-counter.c | 308 +++++++
>>>> > drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
>>>> > drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
>>>> > drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
>>>> > include/linux/iio/counter.h | 629 +++++++++++++
>>>> > 17 files changed, 5216 insertions(+), 15 deletions(-)
>>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
>>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
>>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
>>>> > create mode 100644 Documentation/driver-api/iio/generic-counter.rst
>>>> > create mode 100644 Documentation/driver-api/iio/quad-counter.rst
>>>> > create mode 100644 Documentation/driver-api/iio/simple-counter.rst
>>>> > create mode 100644 drivers/iio/counter/dummy-counter.c
>>>> > create mode 100644 drivers/iio/counter/generic-counter.c
>>>> > create mode 100644 drivers/iio/counter/quad-counter.c
>>>> > create mode 100644 drivers/iio/counter/simple-counter.c
>>>> > create mode 100644 include/linux/iio/counter.h
>>>> >
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>>> the body of a message to [email protected]
>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>>
2018-02-23 14:14 GMT+01:00 William Breathitt Gray <[email protected]>:
> On Fri, Feb 23, 2018 at 01:58:36PM +0100, Benjamin Gaignard wrote:
>>2018-01-15 10:02 GMT+01:00 Benjamin Gaignard <[email protected]>:
>>> 2018-01-01 14:04 GMT+01:00 Jonathan Cameron <[email protected]>:
>>>> On Mon, 1 Jan 2018 11:16:30 +0000
>>>> Jonathan Cameron <[email protected]> wrote:
>>>>
>>>> Sorry to top post but I just want to add some general comments across
>>>> the whole series.
>>>>
>>>> 1) Basics look good to me. It all fits together well.
>>>> 2) I'm concerned about the two 'simplified interfaces' for a couple of reasons:
>>>> a) They add a lot of code and I'm not convinced the simplifications justify that.
>>>> b) They aren't as generically applicable as we might want. For example, it's
>>>> common for SoC Encoder blocks to operative in both the simple counter mode and
>>>> the quadrature counter mode. To support that we have (I think) to go back to
>>>> basics and do it ourselves from the generic counter interface. The TI eQEP
>>>> IP does these modes for example.
>>>>
>>>> So these simplifications serve two purposes (I think)
>>>> 1) To enforce interface. This is nice (and I did some similar stuff in IIO
>>>> for the same reason) but there is so much flexibility in the rest of the
>>>> interface (from a code point of view) that I'm unsure this is significant.
>>>> 2) To save on boiler plate. I'm not sure we save that much and that it couldn't
>>>> mostly be achieved by providing some useful utility functions and
>>>> standard enums / string arrays for the common cases.
>>>>
>>>> We have to justify a couple of thousand lines of core code. To do that we
>>>> need to be saving a reasonably multiple more than that in driver code.
>>>>
>>>> The full setup for a generic_counter is not so complex that we need this
>>>> stuff. Your examples make it all pretty clear what is going on and a
>>>> couple of clean well commented drivers to act as a baseline for new
>>>> implementations would get us much of the rest of the way.
>>>>
>>>> So going well, but this aspect needs some more consideration.
>>>>
>>>> I also think we need at least rough outlines of a few more drivers
>>>> in here to convince people that there aren't any problems that this
>>>> is too inflexible to cover. Hopefully an ST one will be forthcoming.
>>>> If not we can do the exercise off datasheets.
>>>>
>>>
>>> Sorry for the long delay before answering to thread.
>>> I have succesfully implement and test a quadrature encoder driver
>>> on stm32 timer part. Some clean up are need but the basic functions
>>> like setting the two supported modes (quadX2 or quadX4) supported by
>>> my hardware, counting, preset and direction are functional.
>>>
>>> I have used the "simplified interface" so my driver is quite simple with
>>> only few functions to implement (~300 lines of code).
>>> When this series will be upstream we can convert stm32 drivers to use it.
>>>
>>> Thanks a lot for this work.
>>> Benjamin
>>
>>Any news about those patches ?
>>
>>Regards,
>>Benjamin
>
> Hi Benjamin,
>
> Sorry for going dark all this time, I'm still incorporating the changes
> suggested by Jonathan in his review. The biggest change will likely be a
> reimplementation of the "simple" and "quadrature" API as macros
> leveraging the "generic" API in order to reduce a lot of redundant code
> under the hood.
>
> I think I might be comfortable releasing the next revision of the
> patchset on March 3 or 4, so keep an eye out for it then. :)
I will, thanks for the update.
>
> William Breathitt Gray
>
>>
>>>
>>>> Jonathan
>>>>
>>>>> On Thu, 14 Dec 2017 15:50:29 -0500
>>>>> William Breathitt Gray <[email protected]> wrote:
>>>>>
>>>>> > Introduction
>>>>> > ============
>>>>> >
>>>>> > Apologies for going silent these past couple months just to return with
>>>>> > a patchset over 3000 lines larger than the last -- I should have been
>>>>> > releasing intermediate versions along the way so shame on me!
>>>>>
>>>>> :) Sometimes it's better to wait until you are moderately happy with it
>>>>> yourself!
>>>>>
>>>>> > The
>>>>> > Counter system has effectively been rewritten anew, so I believe very
>>>>> > little of the code in the previous versions of this patchset remain.
>>>>> > However, the Generic Counter paradigm has pretty much remained the same
>>>>> > so the theory should be familar. Regardless, I realize I'm dropping off
>>>>> > this patchset near the winter holidays so I don't expect a review until
>>>>> > well into January -- I'm just releasing this now before I myself head
>>>>> > off on an end of year sabbatical.
>>>>>
>>>>> It's at least a few hours into January so here goes before life gets
>>>>> properly busy again.
>>>>>
>>>>> >
>>>>> > The most significant difference between this version and the previous,
>>>>> > as well as part of the reason for the implementation code changes, is
>>>>> > the complete separation of the Generic Counter system from IIO. I
>>>>> > decided it was improper to build the Generic Counter system on top of
>>>>> > IIO core: it was leading to ugly code, convulted hacks, and forced
>>>>> > restrictions on the Generic Counter interface in order to appease the
>>>>> > architecture of the IIO system. Most importantly, the IIO core code that
>>>>> > was leveraged by the Generic Counter was so minor (essentially just the
>>>>> > sysfs attribute support) that it did not justify the extensive
>>>>> > hoop-jumping performed to make the code work.
>>>>> >
>>>>> > So this patchset introduces the Generic Counter interface without the
>>>>> > dependence on IIO code. This now gives the Generic Counter system the
>>>>> > freedom to aptly represent counter devices without implementation
>>>>> > compatibility concerns regarding other high-level subsystems.
>>>>> >
>>>>> > This also makes sense ontologically I believe because whereas the IIO
>>>>> > system appears more focused on representing the industrial I/O of a
>>>>> > device and their configuration directly, the Generic Counter system is
>>>>> > more concerned with the abstract representation of that counter device
>>>>> > and the relationships and configurations within which define its
>>>>> > operation at a high-level; a counter driver could in theory separately
>>>>> > support both the high-level Generic Counter representation of the device
>>>>> > as a whole (what are we counting conceptually, how much are we counting,
>>>>> > etc.), as well as the low-level IIO representation of the individual
>>>>> > inputs and outputs on that device (are the signals differential, do
>>>>> > certain signals have current requirements, etc.).
>>>>>
>>>>> I think there are concepts that over time may blur the lines more
>>>>> but agree with the basic point. I'm just planning to nick all your
>>>>> good ideas if they will improve IIO in turn.
>>>>>
>>>>> >
>>>>> > Overview
>>>>> > ========
>>>>> >
>>>>> > This patchset may be divided into three main groups:
>>>>> >
>>>>> > * Generic Counter
>>>>> > * Simple Counter
>>>>> > * Quadrature Counter
>>>>> >
>>>>> > Each group begins with a patch introducing the implementation of the
>>>>> > interface system, followed afterwards by documentation patches. I
>>>>> > recommend reading through the documentation patches first to familiarize
>>>>> > your with the interface itself before jumping into the source code for
>>>>> > the implementation.
>>>>> >
>>>>> > The Simple Counter and Quadrature Counter groups also have example
>>>>> > driver code in the dummy-counter and 104-quad-8 patches respectively.
>>>>> > The Simple Counter and Quadrature Counter systems themselves being
>>>>> > subclasses of the Generic Counter may serve as example driver code for
>>>>> > the Generic Counter interface -- though I may end up adding an explicit
>>>>> > Generic Counter example in a later patch to the dummy-counter for easier
>>>>> > reference.
>>>>> >
>>>>> > Since the Generic Counter system no longer depends on IIO, I moved all
>>>>> > Counter related source code to the drivers/iio/counter/ directory to
>>>>> > keep everything contained together. In addition, with the IIO Kconfig
>>>>> > dependency removed, the COUNTER menu appear now appears at the same
>>>>> > level as the IIO menu:
>>>>> >
>>>>> > -> Device drivers
>>>>> > -> Counter Support (COUNTER [=m])
>>>>> >
>>>>> > I'm not sure if I should move driver/iio/counter/ to driver/counter/ in
>>>>> > order to match the Kconfig heirarchy or to keep it where it is to match
>>>>> > the legacy IIO counter location established when we first added the
>>>>> > 104-QUAD-8 driver.
>>>>>
>>>>> I would move it out entirely - otherwise things are just confusing.
>>>>> You 'could' sit it in IIO (as in put it under the top level menu option)
>>>>> if you would prefer but I don't thing that really makes sense.
>>>>>
>>>>> >
>>>>> > Paradigm updates
>>>>> > ================
>>>>> >
>>>>> > The Generic Counter paradigm has essentially remained the same from the
>>>>> > previous patch, but I have made some minor updates. In particular, I've
>>>>> > finally settled on a naming convention for the core components of a
>>>>> > Counter:
>>>>> >
>>>>> > COUNT
>>>>> > -----
>>>>> > A Count represents the count data for a set of Signals. A Count
>>>>> > has a count function mode (e.g. "increase" or "quadrature x4")
>>>>> > which represents the update behavior for the count data. A Count
>>>>> > also has a set of one or more associated Signals.
>>>>> >
>>>>> > This component was called "Value" in the previous patches. I believe
>>>>> > "Count" is a more direct name for this data, and it also matches how
>>>>> > datasheets and people commonly refer to this information in
>>>>> > documentation.
>>>>>
>>>>> Agreed - better name.
>>>>>
>>>>> >
>>>>> > SIGNAL
>>>>> > ------
>>>>> > A Signal represents a counter input data; this is the data that
>>>>> > is typically analyzed by the counter to determine the count
>>>>> > data. A Signal may be associated to one or more Counts.
>>>>> >
>>>>> > The naming for this component has not changed since the previous
>>>>> > patches. I believe "Signal" is a fitting enough name for the input
>>>>> > data, as well as matching the common nomenclature for existing counter
>>>>> > devices.
>>>>> >
>>>>> > SYNAPSE
>>>>> > -------
>>>>> > A Synapse represents the association of a Signal with a
>>>>> > respective Count. Signal data affects respective Count data, and
>>>>> > the Synapse represents this relationship. The Synapse action
>>>>> > mode (e.g. "rising edge" or "double pulse") specifies the Signal
>>>>> > data condition which triggers the respective Count's count
>>>>> > function evaluation to update the count data. It is possible for
>>>>> > the Synapse action mode to be "none" if a Signal is associated
>>>>> > with a Count but does not trigger the count function (e.g. the
>>>>> > direction signal line for a Pulse-Direction encoding counter).
>>>>> >
>>>>> > This component was called "Trigger" in the previous patches. I do not
>>>>> > believe "Trigger" was a good name for two main reasons: it could easily
>>>>> > be confused for the existing IIO trigger concept, and most importantly
>>>>> > it does not convey the connection association aspect of the
>>>>> > Count-Signal relationship.
>>>>>
>>>>> An alternative here would be to use MAP as a number of similar
>>>>> 'connection' type arrangements in the kernel do. It doesn't really
>>>>> imply the 'how' element though so perhaps a new term is indeed better.
>>>>>
>>>>>
>>>>> >
>>>>> > I settled on the "Synapse" name both due to etymology -- from Greek
>>>>> > _sunapsis_ meaning "conjunction" -- as well as a biological analogy:
>>>>> > just as neurons connect and fire communication over synapses, so does a
>>>>> > Counter Signal connect and fire communication to a Counter Count over a
>>>>> > Counter Synapse.
>>>>> >
>>>>> > Following the same naming convention and analogy, what was previously
>>>>> > called trigger_mode is now known as action_mode, named in reference to
>>>>> > action potential -- the condition in a neuron which triggers a fire
>>>>> > communication over a synapse, just as a Counter Signal condition
>>>>> > specified in the action_mode of a Counter Synapse triggers the count
>>>>> > function evaluation for a Counter Count.
>>>>> >
>>>>> > Counter classes descriptions
>>>>> > ============================
>>>>> >
>>>>> > The Generic Counter interface is the most general interface for
>>>>> > supporting counter devices; if it qualifies as a Counter, then it can be
>>>>> > represented by the Generic Counter interface. Unfortunately, the
>>>>> > flexibility of the interface does result in a more cumbersome
>>>>> > integration for driver authors: much of the components must be manually
>>>>> > configured by the author, which can be a tedious task for large and
>>>>> > complex counter devices.
>>>>> >
>>>>> > To this end, two subclasses of the Generic Counter interface as
>>>>> > introduced in this patchset: the Simple Counter interface, and the
>>>>> > Quadrature Counter interface. Both of these interfaces inherit the
>>>>> > Generic Counter paradigm, and may be seen as extensions to the interface
>>>>> > which restrict the components to a respective specific class of counter
>>>>> > devices in order to provide a more apt interface for such devices.
>>>>> >
>>>>> > Simple Counter
>>>>> > --------------
>>>>> > Simple Counters are devices that count edge pulses on an input
>>>>> > line (e.g. tally counters).
>>>>> >
>>>>> > Since the relationship between Signals and Counts is known to be
>>>>> > one-to-one, a simple_counter_count structure already contains
>>>>> > the associated Signal member for the respective Count. A driver
>>>>> > author no longer needs to worry about allocating a separate
>>>>> > Signal and Synapse, nor about configuring the association
>>>>> > between the respective Count and Signal; the Simple Counter
>>>>> > interface abstracts away such details.
>>>>> >
>>>>> > Furthermore, since the device type is known, component
>>>>> > properties may be further defined and restricted: Count data is
>>>>> > a signed integer, Signal data "low" and "high" state is set via
>>>>> > enumeration constants, and so are count function and action mode
>>>>> > restricted to well-defined "increase"/"decrease" and
>>>>> > "none"/"rising edge"/"falling edge"/"both edges" enumeration
>>>>> > constants respectively.
>>>>>
>>>>> I do wonder a little on whether this is too restrictive to actually
>>>>> represent many devices.
>>>>> >
>>>>> > Quadrature Counter
>>>>> > ------------------
>>>>> > Quadrature Counters are devices that track position based on
>>>>> > quadrature pair signals (e.g. rotary encoder counters).
>>>>> >
>>>>> > Since the relationship between Signals and Counts is known to be
>>>>> > a quadrature pair of Signals to each Count, a quad_counter_count
>>>>> > structure already contains the associated Signal members for the
>>>>> > respective Count. A driver author no longer needs to worry about
>>>>> > allocating separate Signals and Synapses for each quadrature
>>>>> > pair, nor about configuring the association between the
>>>>> > respective Count and Signals; the Quadrature Counter interface
>>>>> > abstracts away such details.
>>>>> >
>>>>> > Furthermore, since the device type is known, component
>>>>> > properties may be further defined and restricted: Count data is
>>>>> > a signed integer, Signal data "low" and "high" state is set via
>>>>> > enumeration constants, and so is count function mode restricted
>>>>> > to well-defined enumeration constants to represent modes such as
>>>>> > "pulse-direction" and "quadrature x4" for example.
>>>>>
>>>>> Pulse direction is definitely not a quadrature counter... Maybe this needs
>>>>> a rename to dual-signal-counter or similar?
>>>>>
>>>>> Another classic case here would be increment / decrement counters where
>>>>> a signal is used for each operation (counting items between two light gates
>>>>> - used a lot in tracking products in the production industry).
>>>>>
>>>>> >
>>>>> > Note how driver authors no longer need to interact with Synapses
>>>>> > directly when utilizing the Simple Counter and Quadrature Counter
>>>>> > interfaces. This should make it easier too for authors to add support
>>>>> > since they don't need to fully understand the underlying Counter
>>>>> > paradigm in order to take advantage of the interfaces -- just define the
>>>>> > Counts and Signals, and they're ready to go.
>>>>> >
>>>>> > Even more so, the Quadrature Counter interface takes it a step further
>>>>> > and doesn't require action_modes to be explicitly set -- rather they are
>>>>> > implicitly determined internally by the system based on the direction
>>>>> > and function mode. Abstractions like these should make the Counter
>>>>> > interface system as a whole robust enough to handle the diverse classes
>>>>> > of counter devices out in the real world.
>>>>> >
>>>>> > Compilation warnings
>>>>> > ====================
>>>>> >
>>>>> > There are three main compilation warnings which pop for this patchset.
>>>>> > I've inspected these warnings and none are errors, however they do
>>>>> > require some explanation.
>>>>> >
>>>>> > * 104-quad-8: warning: enumeration value
>>>>> > ‘QUAD_COUNTER_FUNCTION_PULSE_DIRECTION’ not handled in
>>>>> > switch
>>>>> >
>>>>> > The first warning is rather simple to explain: the
>>>>> > QUAD_COUNTER_FUNCTION_PULSE_DIRECTION state is handled by the parent if
>>>>> > statement's else condition, so an explicit case condition is not
>>>>> > necessary. I can add a default case line to pacify the compiler, but
>>>>> > since it would be empty the effort seems frivolous.
>>>>>
>>>>> Do it anyway and put a comment of /* Should not get here */
>>>>>
>>>>> Suppressing false warnings is useful from a code maintenance point of view.
>>>>>
>>>>> > In some sense as
>>>>> > well, a default case may make the switch logic less clear by implying
>>>>> > the possibility of additional cases which are not possible in the
>>>>> > context of that code path.
>>>>> >
>>>>> > * simple-counter: warning: assignment discards ‘const’ qualifier
>>>>> > from pointer target type
>>>>> > * quad-counter: warning: assignment discards ‘const’ qualifier
>>>>> > from pointer target type
>>>>> >
>>>>> > The second warning comes from the mapping of
>>>>> > simple_counter_device_ext/quad_counter_device_ext,
>>>>> > simple_counter_count_ext/quad_counter_count_ext, and
>>>>> > simple_counter_signal_ext/quad_counter_signal_ext to the internal
>>>>> > Counter counter_device_ext, counter_count_ext, and counter_signal_ext
>>>>> > structures respectively.
>>>>> >
>>>>> > The priv member of the counter_device_ext, counter_count_ext, or
>>>>> > counter_signal_ext is leveraged to pass the respective
>>>>> > simple_counter_device_ext/quad_counter_device_ext,
>>>>> > simple_counter_count_ext/quad_counter_count_ext, or
>>>>> > simple_counter_signal_ext/quad_counter_signal_ext structure to their
>>>>> > respective read/write callback. The priv member is generic on purpose to
>>>>> > allow any desired data to be passed; the supplied read/write callbacks
>>>>> > should know the datatype of the passed-in priv argument so they cast it
>>>>> > appropriately to access their expected data.
>>>>> >
>>>>> > As such, the 'const' qualifier of the structures are thus discarded but
>>>>> > subsequently cast back when the respective registered callback functions
>>>>> > are called. Since this is the intended use case of the priv member -- to
>>>>> > generically pass driver data for later recast -- I don't believe this
>>>>> > warning needs to be rectified.
>>>>>
>>>>> All warnings need to be rectified. Sorry but this noise will do two things:
>>>>> 1) Get you a patch every few weeks from someone fixing it.
>>>>> 2) Potentially make real warnings harder to see.
>>>>>
>>>>> Sometimes we have to play games to work around them, but such is life.
>>>>>
>>>>> >
>>>>> > * generic-counter: warning: passing argument 5 of
>>>>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>>>>> > from pointer target type
>>>>> > * generic-counter: warning: passing argument 6 of
>>>>> > ‘counter_attribute_create’ discards ‘const’ qualifier
>>>>> > from pointer target type
>>>>> >
>>>>> > The third warnings comes from a similar situation to the second warning:
>>>>> > a 'const' argument is passed generically via 'void *' for later recast.
>>>>> > In this cast, I decided to create a generic function called
>>>>> > counter_attribute_create in order to simplify the sysfs attribute
>>>>> > registration code in the generic-counter.c file.
>>>>> >
>>>>> > The counter_attribute_create function takes in read and write callbacks,
>>>>> > as well as two optional generic data arguments to be stored as 'void *'
>>>>> > (the component and component_data parameters). Using this setup allows
>>>>> > the counter_attribute_create function to be the sole function necessary
>>>>> > to create a desired Generic Counter sysfs attribute: read and write
>>>>> > callbacks are passed along with relevant Counter component and data
>>>>> > generically, which can be cast back later inside those read and write
>>>>> > functions to match the expected datatype.
>>>>> >
>>>>> > Using a generic counter_attribute_create function reduces duplicate
>>>>> > code, but it does result in many superfluous compilation warnings. I can
>>>>> > define new attribute_create functions specific to each type of sysfs
>>>>> > attribute in order to pacify the warnings, but that seems to be such a
>>>>> > waste to increase the amount of code with duplications that are
>>>>> > unnecessary. What would you recommend; should I attempt to pacify these
>>>>> > warnings or leave them be?
>>>>>
>>>>> You must fix them I'm afraid.
>>>>>
>>>>> >
>>>>> > Known TODO items
>>>>> > ================
>>>>> >
>>>>> > Although I've added the interface documentation files with rst file
>>>>> > extensions, I still need to familiarize myself with Sphinx markup
>>>>> > constructs to take advantage of the language. For example, I've copied
>>>>> > verbatim several structure definitions into the documentation directly,
>>>>> > but I believe this would be better left dynamically generated by using
>>>>> > the relevant markup syntax. I'll try to clean up the documentation then
>>>>> > once I've brushed up on Sphinx.
>>>>> >
>>>>> > As noted in a previous patchset version, the signal_write callback
>>>>> > should be removed from the interface; there are few if any cases where
>>>>> > it makese sense to have a signal_write callback since Signals are
>>>>> > always considered inputs in the context of the Counter paradigm.
>>>>> >
>>>>> > I've retained the signal_write callback in this version since I'm unsure
>>>>> > how to implement the dummy-counter Signal source. Benjamin Gaignard
>>>>> > suggested implementing dummy-counter as a gpio-counter which could use
>>>>> > gpio to provide a software quadratic counter. Is this the path I should
>>>>> > take?
>>>>>
>>>>> It would certainly work well and be simple enough for easy understanding.
>>>>> Also, it might be a useful driver in it's own right.
>>>>>
>>>>> >
>>>>> > Furthermore, the dummy-counter driver defines its own single
>>>>> > platform_device which restricts it to loading only a single instance.
>>>>> > I can fix this to allow multiple instances in the next patchset version
>>>>> > -- as suggested, I'll check out industrialio-sw-device.c for reference.
>>>>> >
>>>>> > Right now the dummy-counter driver only has example code for the Simple
>>>>> > Counter interface. It may be prudent to add example code for the Generic
>>>>> > Counter and Quadrature Counter interfaces too. I think dummy-counter
>>>>> > should serve as the reference driver implementation for all the Counter
>>>>> > interfaces, so that driver authors have an example of how to integrate
>>>>> > the particular interface they desire.
>>>>>
>>>>> Such a driver is useful, but it doesn't add much if you have another,
>>>>> only slightly more complex real driver that also does the job.
>>>>> Perhaps do them all as gpio based drivers for example?
>>>>> >
>>>>> > Finally, I only added very basic support for the Quadrature Counter
>>>>> > interface in the 104-QUAD-8 driver. It's possible to support all
>>>>> > existing IIO Counter sysfs attributes in the 104-QUAD-8 driver via
>>>>> > corresponding quad_counter_device_ext, quad_counter_count_ext, and
>>>>> > quad_counter_signal_ext structures, such that only the
>>>>> > /sys/bus/counter/devices/counterX/ directory needs to be accessed to
>>>>> > interact with the 104-QUAD-8 device. I'll try to add support for those
>>>>> > remaining sysfs attributes in the next patchset version.
>>>>> >
>>>>> > If I missed anything from the last patchset version review just remind
>>>>> > me again and I'll add it to my TODO list. ;)
>>>>>
>>>>> You are seriously optimistic if you think we can remember!
>>>>>
>>>>> Jonathan
>>>>>
>>>>> >
>>>>> > William Breathitt Gray (11):
>>>>> > iio: Introduce the Generic Counter interface
>>>>> > counter: Documentation: Add Generic Counter sysfs documentation
>>>>> > docs: Add Generic Counter interface documentation
>>>>> > counter: Introduce the Simple Counter interface
>>>>> > counter: Documentation: Add Simple Counter sysfs documentation
>>>>> > docs: Add Simple Counter interface documentation
>>>>> > counter: Add dummy counter driver
>>>>> > counter: Introduce the Quadrature Counter interface
>>>>> > counter: Documentation: Add Quadrature Counter sysfs documentation
>>>>> > docs: Add Quadrature Counter interface documentation
>>>>> > counter: 104-quad-8: Add Quadrature Counter interface support
>>>>> >
>>>>> > .../ABI/testing/sysfs-bus-counter-generic-sysfs | 73 ++
>>>>> > .../ABI/testing/sysfs-bus-counter-quadrature-sysfs | 76 ++
>>>>> > .../ABI/testing/sysfs-bus-counter-simple-sysfs | 61 ++
>>>>> > Documentation/driver-api/iio/generic-counter.rst | 434 +++++++++
>>>>> > Documentation/driver-api/iio/index.rst | 3 +
>>>>> > Documentation/driver-api/iio/quad-counter.rst | 444 +++++++++
>>>>> > Documentation/driver-api/iio/simple-counter.rst | 393 ++++++++
>>>>> > MAINTAINERS | 9 +
>>>>> > drivers/iio/Kconfig | 3 +-
>>>>> > drivers/iio/counter/104-quad-8.c | 257 +++++-
>>>>> > drivers/iio/counter/Kconfig | 35 +-
>>>>> > drivers/iio/counter/Makefile | 6 +
>>>>> > drivers/iio/counter/dummy-counter.c | 308 +++++++
>>>>> > drivers/iio/counter/generic-counter.c | 992 +++++++++++++++++++++
>>>>> > drivers/iio/counter/quad-counter.c | 774 ++++++++++++++++
>>>>> > drivers/iio/counter/simple-counter.c | 734 +++++++++++++++
>>>>> > include/linux/iio/counter.h | 629 +++++++++++++
>>>>> > 17 files changed, 5216 insertions(+), 15 deletions(-)
>>>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-generic-sysfs
>>>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-quadrature-sysfs
>>>>> > create mode 100644 Documentation/ABI/testing/sysfs-bus-counter-simple-sysfs
>>>>> > create mode 100644 Documentation/driver-api/iio/generic-counter.rst
>>>>> > create mode 100644 Documentation/driver-api/iio/quad-counter.rst
>>>>> > create mode 100644 Documentation/driver-api/iio/simple-counter.rst
>>>>> > create mode 100644 drivers/iio/counter/dummy-counter.c
>>>>> > create mode 100644 drivers/iio/counter/generic-counter.c
>>>>> > create mode 100644 drivers/iio/counter/quad-counter.c
>>>>> > create mode 100644 drivers/iio/counter/simple-counter.c
>>>>> > create mode 100644 include/linux/iio/counter.h
>>>>> >
>>>>>
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>>>> the body of a message to [email protected]
>>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>>>