2017-09-25 18:08:12

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v2 0/5] iio: Introduce the generic counter interface

This version 2 submission of this patchset is more of an RFC; I haven't
yet completed all the changes and additional comments/documentation I
would like to make. However, I didn't want to keep pushing back this
submission after so many weeks, so I'm providing this version now in the
hopes that the documentation I have written so far can prove useful to
peruse for the time being.

Very little has changed in terms of the code implementation of the
architecture since version 1 of this patchset. In particular, the
following minor changes were made for v2:

- Elimination of the "__" prefix for symbols; I had used this naming
convention to indicate symbols which had static scope to the counter
system, but decided it was ultimately redundant when the "static"
keyword already conveys such

- Removal of the drive_module configuration for iio_info structures; I
noticed the driver_module member was removed in a recent patch

- Explicit cast to void * before dereferencing uintptr_t variables;
this minor coding convention oversight was resolved after the
heads-up provided in the previous v1 review referencing
iio_counter_trigger_mode_read

- Fix sanity-check bugs derived from copy-paste typos;
iio_counter_value_function_get and iio_counter_value_function_set
had were checking for trigger_mode_get to be set when they should
have checked for value_function_get and value_function_set
respectively

Overall, the major addition to this submission is the addition of some
more in-depth documentation about the IIO Generic Counter Interface.
Architectural and driver API documentation has been added in the
Documentation/driver-api/iio/generic-counter.txt file. I noticed that
the files in this directory are in sphinx format, so I'll convert this
file to such in one of the subsequent versions of this patchset.

The typically sysfs documentation has been added in the
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs file. This
file provides the documentation for the sysfs attributes exposed by
utilizing the IIO Generic Counter Interface.

Unfortunately, I haven't yet added proper commenting to the
industrial-counter.c file, so bear with me until v3 of this patchset.
There's a good number of changes and updates I would still like to make
before the merge so here's a list of what to expect in the coming
revisions:

- More thorough explanation of the specific code implementation
provided in the industrial-counter.c file (this would include the
missing code comments that should resolve the opacity when trying to
follow the source code); the documentation provided in this v2
submission is more of a high-level overview of the architecture and
theory, while a more low-level specific source code roadmap would be
beneficial for navigating the implementation.

- Rationale for certain algorithm and data structure decisions; for
example, why an entire copy of iio_counter is stored rather than
just a pointer (immutability concerns), the locking structure
between Values, Triggers, and Signals interactions (race condition
concerns, why linked lists are used to store Counter components
(though a vector implementation is a viable alternative I'm
investigating), etc. These choices I made are not necessarily
correct, but hopefully my rationale for these decisions will make
suggestions of alternatives far easier for reviewers to make.

- Example implementations to exemplify the driver API; the 104-QUAD-8
generic counter patch in this patchset is not a very good beginner
example for learning; I'm developing a simple dummy counter
driver which should make the API use a lot clearer for driver
authors; I'm hesitant just yet to add support to actual drivers
since they will likely use the more appropriate future Simple
Counter and Quadrature Counter interfaces.

As I mentioned before in previous discussions, the Generic Counter
Interface itself isn't particularly intended for general driver
consumption, but rather to serve as the building blocks for various
classes of counter device interfaces. To this end, I've developed the
generic counter paradigm around the essence and bare requirements
necessary to support the concept of a counter.

As such, the Generic Counter Interface has certain flexibilities and
freedoms ill-suited for general drivers, but beneficial for the
construction of more specific classes of counter interfaces. There are
such classes I intend to submit as their own separate patchsets once the
Generic Counter Interface is merged:

- The Simple Counter Interface for simple counter devices with a
single Signal, single Trigger, and single Value; this what I
anticipate most typical drivers consuming -- an interface that is
simple, rigid, and terse.

- The Quadrature Encoder Counter Interface for quadrature encoder
counter devices; this would be consumed by drivers for devices such
as the 104-QUAD-8 -- an interface with predefined constants for
quadrature pairs and succinct declarations.

I'm going to prioritize source code commenting for v3 of this patchset,
and try to prevent the long delay this version had. I'm aiming for a
submission of v3 in the next couple of weeks then as I integrate the
rest of the documentation.

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

.../testing/sysfs-bus-iio-generic-counter-sysfs | 63 ++
Documentation/driver-api/iio/generic-counter.txt | 555 ++++++++++
MAINTAINERS | 7 +
drivers/iio/Kconfig | 8 +
drivers/iio/Makefile | 1 +
drivers/iio/counter/104-quad-8.c | 306 +++++-
drivers/iio/counter/Kconfig | 1 +
drivers/iio/industrialio-core.c | 14 +-
drivers/iio/industrialio-counter.c | 1151 ++++++++++++++++++++
include/linux/iio/counter.h | 221 ++++
include/linux/iio/iio.h | 2 +
include/uapi/linux/iio/types.h | 1 +
12 files changed, 2312 insertions(+), 18 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
create mode 100644 Documentation/driver-api/iio/generic-counter.txt
create mode 100644 drivers/iio/industrialio-counter.c
create mode 100644 include/linux/iio/counter.h

--
2.14.1


2017-09-25 18:08:59

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v2 2/5] iio: Introduce the generic counter interface

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

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

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

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

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

"Counter values" may be allocated via iio_counter_value structures, and
respective "counter signal" associations made via iio_counter_trigger
structures. Initial associated iio_counter_trigger structures may be
stored as an array and set to the the init_triggers member of the
respective iio_counter_value structure. These iio_counter_value
structures may be set to the init_values member of an allocated
iio_counter structure before the counter is registered if so desired.

A counter device is registered to the system by passing the respective
initialized iio_counter structure to the iio_counter_register function;
similarly, the iio_counter_unregister function unregisters the
respective counter.

Signed-off-by: William Breathitt Gray <[email protected]>
---
MAINTAINERS | 7 +
drivers/iio/Kconfig | 8 +
drivers/iio/Makefile | 1 +
drivers/iio/counter/Kconfig | 1 +
drivers/iio/industrialio-counter.c | 1151 ++++++++++++++++++++++++++++++++++++
include/linux/iio/counter.h | 221 +++++++
6 files changed, 1389 insertions(+)
create mode 100644 drivers/iio/industrialio-counter.c
create mode 100644 include/linux/iio/counter.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 6f7721d1634c..24fc2dcf1995 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6577,6 +6577,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
F: drivers/iio/adc/envelope-detector.c

+IIO GENERIC COUNTER INTERFACE
+M: William Breathitt Gray <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/iio/industrialio-counter.c
+F: include/linux/iio/counter.h
+
IIO MULTIPLEXER
M: Peter Rosin <[email protected]>
L: [email protected]
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b3c8c6ef0dff..78e01f4f5937 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -30,6 +30,14 @@ config IIO_CONFIGFS
(e.g. software triggers). For more info see
Documentation/iio/iio_configfs.txt.

+config IIO_COUNTER
+ bool "Enable IIO counter support"
+ help
+ Provides IIO core support for counters. This API provides
+ a generic interface that serves as the building blocks to
+ create more complex counter interfaces. Rudimentary support
+ for counters is enabled.
+
config IIO_TRIGGER
bool "Enable triggered sampling support"
help
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 93c769cd99bf..6427ff38f964 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_IIO) += industrialio.o
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
+industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o

obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index b37e5fc03149..3d46a790d8db 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -4,6 +4,7 @@
# When adding new entries keep the list in alphabetical order

menu "Counters"
+ depends on IIO_COUNTER

config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
new file mode 100644
index 000000000000..bdf190d010e4
--- /dev/null
+++ b/drivers/iio/industrialio-counter.c
@@ -0,0 +1,1151 @@
+/*
+ * Industrial I/O counter interface functions
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include <linux/iio/counter.h>
+
+static struct iio_counter_signal *iio_counter_signal_find_by_id(
+ const struct iio_counter *const counter, const int id)
+{
+ struct iio_counter_signal *iter;
+
+ list_for_each_entry(iter, &counter->signal_list, list)
+ if (iter->id == id)
+ return iter;
+
+ return NULL;
+}
+
+static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
+ const struct iio_counter_value *const value, const int id)
+{
+ struct iio_counter_trigger *iter;
+
+ list_for_each_entry(iter, &value->trigger_list, list)
+ if (iter->signal->id == id)
+ return iter;
+
+ return NULL;
+}
+
+static struct iio_counter_value *iio_counter_value_find_by_id(
+ const struct iio_counter *const counter, const int id)
+{
+ struct iio_counter_value *iter;
+
+ list_for_each_entry(iter, &counter->value_list, list)
+ if (iter->id == id)
+ return iter;
+
+ return NULL;
+}
+
+static void iio_counter_trigger_unregister_all(
+ struct iio_counter_value *const value)
+{
+ struct iio_counter_trigger *iter, *tmp_iter;
+
+ mutex_lock(&value->trigger_list_lock);
+ list_for_each_entry_safe(iter, tmp_iter, &value->trigger_list, list)
+ list_del(&iter->list);
+ mutex_unlock(&value->trigger_list_lock);
+}
+
+static void iio_counter_signal_unregister_all(struct iio_counter *const counter)
+{
+ struct iio_counter_signal *iter, *tmp_iter;
+
+ mutex_lock(&counter->signal_list_lock);
+ list_for_each_entry_safe(iter, tmp_iter, &counter->signal_list, list)
+ list_del(&iter->list);
+ mutex_unlock(&counter->signal_list_lock);
+}
+
+static void iio_counter_value_unregister_all(struct iio_counter *const counter)
+{
+ struct iio_counter_value *iter, *tmp_iter;
+
+ mutex_lock(&counter->value_list_lock);
+ list_for_each_entry_safe(iter, tmp_iter, &counter->value_list, list) {
+ iio_counter_trigger_unregister_all(iter);
+
+ list_del(&iter->list);
+ }
+ mutex_unlock(&counter->value_list_lock);
+}
+
+static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ const struct iio_counter_signal *signal;
+
+ mutex_lock(&counter->signal_list_lock);
+ signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+ mutex_unlock(&counter->signal_list_lock);
+ if (!signal)
+ return -EINVAL;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
+}
+
+static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ const struct iio_counter_value *value;
+
+ mutex_lock(&counter->value_list_lock);
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ mutex_unlock(&counter->value_list_lock);
+ if (!value)
+ return -EINVAL;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
+}
+
+static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_value *value;
+ const struct iio_counter_trigger *trigger;
+ ssize_t len = 0;
+
+ mutex_lock(&counter->value_list_lock);
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ len = -EINVAL;
+ goto err_find_value;
+ }
+
+ mutex_lock(&value->trigger_list_lock);
+ list_for_each_entry(trigger, &value->trigger_list, list) {
+ len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
+ trigger->signal->id, trigger->signal->name,
+ trigger->trigger_modes[trigger->mode]);
+ if (len >= PAGE_SIZE) {
+ len = -ENOMEM;
+ goto err_no_buffer_space;
+ }
+ }
+err_no_buffer_space:
+ mutex_unlock(&value->trigger_list_lock);
+
+err_find_value:
+ mutex_unlock(&counter->value_list_lock);
+
+ return len;
+}
+
+static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_value *value;
+ ssize_t ret;
+ struct iio_counter_trigger *trigger;
+ const int signal_id = *(int *)((void *)priv);
+ int mode;
+
+ if (!counter->ops->trigger_mode_get)
+ return -EINVAL;
+
+ mutex_lock(&counter->value_list_lock);
+
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ ret = -EINVAL;
+ goto err_value;
+ }
+
+ mutex_lock(&value->trigger_list_lock);
+
+ trigger = iio_counter_trigger_find_by_id(value, signal_id);
+ if (!trigger) {
+ ret = -EINVAL;
+ goto err_trigger;
+ }
+
+ mode = counter->ops->trigger_mode_get(counter, value, trigger);
+
+ if (mode < 0) {
+ ret = mode;
+ goto err_trigger;
+ } else if (mode >= trigger->num_trigger_modes) {
+ ret = -EINVAL;
+ goto err_trigger;
+ }
+
+ trigger->mode = mode;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
+
+ mutex_unlock(&value->trigger_list_lock);
+
+ mutex_unlock(&counter->value_list_lock);
+
+ return ret;
+
+err_trigger:
+ mutex_unlock(&value->trigger_list_lock);
+err_value:
+ mutex_unlock(&counter->value_list_lock);
+ return ret;
+}
+
+static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_value *value;
+ ssize_t err;
+ struct iio_counter_trigger *trigger;
+ const int signal_id = *(int *)((void *)priv);
+ unsigned int mode;
+
+ if (!counter->ops->trigger_mode_set)
+ return -EINVAL;
+
+ mutex_lock(&counter->value_list_lock);
+
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ err = -EINVAL;
+ goto err_value;
+ }
+
+ mutex_lock(&value->trigger_list_lock);
+
+ trigger = iio_counter_trigger_find_by_id(value, signal_id);
+ if (!trigger) {
+ err = -EINVAL;
+ goto err_trigger;
+ }
+
+ for (mode = 0; mode < trigger->num_trigger_modes; mode++)
+ if (sysfs_streq(buf, trigger->trigger_modes[mode]))
+ break;
+
+ if (mode >= trigger->num_trigger_modes) {
+ err = -EINVAL;
+ goto err_trigger;
+ }
+
+ err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
+ if (err)
+ goto err_trigger;
+
+ trigger->mode = mode;
+
+ mutex_unlock(&value->trigger_list_lock);
+
+ mutex_unlock(&counter->value_list_lock);
+
+ return len;
+
+err_trigger:
+ mutex_unlock(&value->trigger_list_lock);
+err_value:
+ mutex_unlock(&counter->value_list_lock);
+ return err;
+}
+
+static ssize_t iio_counter_trigger_mode_available_read(
+ struct iio_dev *indio_dev, uintptr_t priv,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_value *value;
+ ssize_t len = 0;
+ struct iio_counter_trigger *trigger;
+ const int signal_id = *(int *)((void *)priv);
+ unsigned int i;
+
+ mutex_lock(&counter->value_list_lock);
+
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ len = -EINVAL;
+ goto err_no_value;
+ }
+
+ mutex_lock(&value->trigger_list_lock);
+
+ trigger = iio_counter_trigger_find_by_id(value, signal_id);
+ if (!trigger) {
+ len = -EINVAL;
+ goto err_no_trigger;
+ }
+
+ for (i = 0; i < trigger->num_trigger_modes; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+ trigger->trigger_modes[i]);
+
+ mutex_unlock(&value->trigger_list_lock);
+
+ mutex_unlock(&counter->value_list_lock);
+
+ buf[len - 1] = '\n';
+
+ return len;
+
+err_no_trigger:
+ mutex_unlock(&value->trigger_list_lock);
+err_no_value:
+ mutex_unlock(&counter->value_list_lock);
+ return len;
+}
+
+static int iio_counter_value_function_set(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_value *value;
+ int err;
+
+ if (!counter->ops->value_function_set)
+ return -EINVAL;
+
+ mutex_lock(&counter->value_list_lock);
+
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ err = -EINVAL;
+ goto err_value;
+ }
+
+ err = counter->ops->value_function_set(counter, value, mode);
+ if (err)
+ goto err_value;
+
+ value->mode = mode;
+
+err_value:
+ mutex_unlock(&counter->value_list_lock);
+
+ return err;
+}
+
+static int iio_counter_value_function_get(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_value *value;
+ int retval;
+
+ if (!counter->ops->value_function_get)
+ return -EINVAL;
+
+ mutex_lock(&counter->value_list_lock);
+
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ retval = -EINVAL;
+ goto err_value;
+ }
+
+ retval = counter->ops->value_function_get(counter, value);
+ if (retval < 0)
+ goto err_value;
+ else if (retval >= value->num_function_modes) {
+ retval = -EINVAL;
+ goto err_value;
+ }
+
+ value->mode = retval;
+
+err_value:
+ mutex_unlock(&counter->value_list_lock);
+
+ return retval;
+}
+
+static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
+ struct iio_counter_value *const value)
+{
+ const struct iio_chan_spec_ext_info ext_info_default[] = {
+ {
+ .name = "name",
+ .shared = IIO_SEPARATE,
+ .read = iio_counter_value_name_read
+ },
+ IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
+ {
+ .name = "function_available",
+ .shared = IIO_SEPARATE,
+ .read = iio_enum_available_read,
+ .private = (void *)&value->function_enum
+ },
+ {
+ .name = "triggers",
+ .shared = IIO_SEPARATE,
+ .read = iio_counter_value_triggers_read
+ }
+ };
+ const size_t num_default = ARRAY_SIZE(ext_info_default);
+ const struct iio_chan_spec_ext_info ext_info_trigger[] = {
+ {
+ .shared = IIO_SEPARATE,
+ .read = iio_counter_trigger_mode_read,
+ .write = iio_counter_trigger_mode_write
+ },
+ {
+ .shared = IIO_SEPARATE,
+ .read = iio_counter_trigger_mode_available_read
+ }
+ };
+ const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
+ const struct list_head *pos;
+ size_t num_triggers = 0;
+ size_t num_triggers_ext_info;
+ size_t num_ext_info;
+ int err;
+ struct iio_chan_spec_ext_info *ext_info;
+ const struct iio_counter_trigger *trigger_pos;
+ size_t i;
+
+ value->function_enum.items = value->function_modes;
+ value->function_enum.num_items = value->num_function_modes;
+ value->function_enum.set = iio_counter_value_function_set;
+ value->function_enum.get = iio_counter_value_function_get;
+
+ mutex_lock(&value->trigger_list_lock);
+
+ list_for_each(pos, &value->trigger_list)
+ num_triggers++;
+
+ num_triggers_ext_info = num_ext_info_trigger * num_triggers;
+ num_ext_info = num_default + num_triggers_ext_info + 1;
+
+ ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
+ if (!ext_info) {
+ err = -ENOMEM;
+ goto err_ext_info_alloc;
+ }
+ ext_info[num_ext_info - 1].name = NULL;
+
+ memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
+ for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
+ memcpy(ext_info + num_default + i, ext_info_trigger,
+ sizeof(ext_info_trigger));
+
+ i = num_default;
+ list_for_each_entry(trigger_pos, &value->trigger_list, list) {
+ ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
+ chan->channel, trigger_pos->signal->id);
+ if (!ext_info[i].name) {
+ err = -ENOMEM;
+ goto err_name_alloc;
+ }
+ ext_info[i].private = (void *)&trigger_pos->signal->id;
+ i++;
+
+ ext_info[i].name = kasprintf(GFP_KERNEL,
+ "trigger_signal%d-%d_available",
+ chan->channel, trigger_pos->signal->id);
+ if (!ext_info[i].name) {
+ err = -ENOMEM;
+ goto err_name_alloc;
+ }
+ ext_info[i].private = (void *)&trigger_pos->signal->id;
+ i++;
+ }
+
+ chan->ext_info = ext_info;
+
+ mutex_unlock(&value->trigger_list_lock);
+
+ return 0;
+
+err_name_alloc:
+ while (i-- > num_default)
+ kfree(ext_info[i].name);
+ kfree(ext_info);
+err_ext_info_alloc:
+ mutex_unlock(&value->trigger_list_lock);
+ return err;
+}
+
+static void iio_counter_value_ext_info_free(
+ const struct iio_chan_spec *const channel)
+{
+ size_t i;
+ const char *const prefix = "trigger_signal";
+ const size_t prefix_len = strlen(prefix);
+
+ for (i = 0; channel->ext_info[i].name; i++)
+ if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
+ kfree(channel->ext_info[i].name);
+ kfree(channel->ext_info);
+}
+
+static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
+ {
+ .name = "name",
+ .shared = IIO_SEPARATE,
+ .read = iio_counter_signal_name_read
+ },
+ {}
+};
+
+static int iio_counter_channels_alloc(struct iio_counter *const counter)
+{
+ const struct list_head *pos;
+ size_t num_channels = 0;
+ int err;
+ struct iio_chan_spec *channels;
+ struct iio_counter_value *value_pos;
+ size_t i = counter->num_channels;
+ const struct iio_counter_signal *signal_pos;
+
+ mutex_lock(&counter->signal_list_lock);
+
+ list_for_each(pos, &counter->signal_list)
+ num_channels++;
+
+ if (!num_channels) {
+ err = -EINVAL;
+ goto err_no_signals;
+ }
+
+ mutex_lock(&counter->value_list_lock);
+
+ list_for_each(pos, &counter->value_list)
+ num_channels++;
+
+ num_channels += counter->num_channels;
+
+ channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels) {
+ err = -ENOMEM;
+ goto err_channels_alloc;
+ }
+
+ memcpy(channels, counter->channels,
+ counter->num_channels * sizeof(*counter->channels));
+
+ list_for_each_entry(value_pos, &counter->value_list, list) {
+ channels[i].type = IIO_COUNT;
+ channels[i].channel = counter->id;
+ channels[i].channel2 = value_pos->id;
+ channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ channels[i].indexed = 1;
+ channels[i].counter = 1;
+
+ err = iio_counter_value_ext_info_alloc(channels + i, value_pos);
+ if (err)
+ goto err_value_ext_info_alloc;
+
+ i++;
+ }
+
+ mutex_unlock(&counter->value_list_lock);
+
+ list_for_each_entry(signal_pos, &counter->signal_list, list) {
+ channels[i].type = IIO_SIGNAL;
+ channels[i].channel = counter->id;
+ channels[i].channel2 = signal_pos->id;
+ channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ channels[i].indexed = 1;
+ channels[i].counter = 1;
+ channels[i].ext_info = iio_counter_signal_ext_info;
+
+ i++;
+ }
+
+ mutex_unlock(&counter->signal_list_lock);
+
+ counter->indio_dev->num_channels = num_channels;
+ counter->indio_dev->channels = channels;
+
+ return 0;
+
+err_value_ext_info_alloc:
+ while (i-- > counter->num_channels)
+ iio_counter_value_ext_info_free(channels + i);
+ kfree(channels);
+err_channels_alloc:
+ mutex_unlock(&counter->value_list_lock);
+err_no_signals:
+ mutex_unlock(&counter->signal_list_lock);
+ return err;
+}
+
+static void iio_counter_channels_free(const struct iio_counter *const counter)
+{
+ size_t i = counter->num_channels + counter->indio_dev->num_channels;
+ const struct iio_chan_spec *const chans = counter->indio_dev->channels;
+
+ while (i-- > counter->num_channels)
+ if (chans[i].type == IIO_COUNT)
+ iio_counter_value_ext_info_free(chans + i);
+
+ kfree(chans);
+}
+
+static int iio_counter_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_signal *signal;
+ int retval;
+ struct iio_counter_value *value;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_SIGNAL:
+ if (!counter->ops->signal_read)
+ return -EINVAL;
+
+ mutex_lock(&counter->signal_list_lock);
+ signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+ if (!signal) {
+ mutex_unlock(&counter->signal_list_lock);
+ return -EINVAL;
+ }
+
+ retval = counter->ops->signal_read(counter, signal, val, val2);
+ mutex_unlock(&counter->signal_list_lock);
+
+ return retval;
+ case IIO_COUNT:
+ if (!counter->ops->value_read)
+ return -EINVAL;
+
+ mutex_lock(&counter->value_list_lock);
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ mutex_unlock(&counter->value_list_lock);
+ return -EINVAL;
+ }
+
+ retval = counter->ops->value_read(counter, value, val, val2);
+ mutex_unlock(&counter->value_list_lock);
+
+ return retval;
+ default:
+ if (counter->info && counter->info->read_raw)
+ return counter->info->read_raw(indio_dev, chan, val,
+ val2, mask);
+ }
+
+ return -EINVAL;
+}
+
+static int iio_counter_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct iio_counter *const counter = iio_priv(indio_dev);
+ struct iio_counter_signal *signal;
+ int retval;
+ struct iio_counter_value *value;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_SIGNAL:
+ if (!counter->ops->signal_write)
+ return -EINVAL;
+
+ mutex_lock(&counter->signal_list_lock);
+ signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+ if (!signal) {
+ mutex_unlock(&counter->signal_list_lock);
+ return -EINVAL;
+ }
+
+ retval = counter->ops->signal_write(counter, signal, val, val2);
+ mutex_unlock(&counter->signal_list_lock);
+
+ return retval;
+ case IIO_COUNT:
+ if (!counter->ops->value_write)
+ return -EINVAL;
+
+ mutex_lock(&counter->value_list_lock);
+ value = iio_counter_value_find_by_id(counter, chan->channel2);
+ if (!value) {
+ mutex_unlock(&counter->value_list_lock);
+ return -EINVAL;
+ }
+
+ retval = counter->ops->value_write(counter, value, val, val2);
+ mutex_unlock(&counter->value_list_lock);
+
+ return retval;
+ default:
+ if (counter->info && counter->info->write_raw)
+ return counter->info->write_raw(indio_dev, chan, val,
+ val2, mask);
+ }
+
+ return -EINVAL;
+}
+
+static int iio_counter_signal_register(struct iio_counter *const counter,
+ struct iio_counter_signal *const signal)
+{
+ int err;
+
+ if (!counter || !signal)
+ return -EINVAL;
+
+ mutex_lock(&counter->signal_list_lock);
+ if (iio_counter_signal_find_by_id(counter, signal->id)) {
+ pr_err("Duplicate counter signal ID '%d'\n", signal->id);
+ err = -EEXIST;
+ goto err_duplicate_id;
+ }
+ list_add_tail(&signal->list, &counter->signal_list);
+ mutex_unlock(&counter->signal_list_lock);
+
+ return 0;
+
+err_duplicate_id:
+ mutex_unlock(&counter->signal_list_lock);
+ return err;
+}
+
+static void iio_counter_signal_unregister(struct iio_counter *const counter,
+ struct iio_counter_signal *const signal)
+{
+ if (!counter || !signal)
+ return;
+
+ mutex_lock(&counter->signal_list_lock);
+ list_del(&signal->list);
+ mutex_unlock(&counter->signal_list_lock);
+}
+
+static int iio_counter_signals_register(struct iio_counter *const counter,
+ struct iio_counter_signal *const signals, const size_t num_signals)
+{
+ size_t i;
+ int err;
+
+ if (!counter || !signals)
+ return -EINVAL;
+
+ for (i = 0; i < num_signals; i++) {
+ err = iio_counter_signal_register(counter, signals + i);
+ if (err)
+ goto err_signal_register;
+ }
+
+ return 0;
+
+err_signal_register:
+ while (i--)
+ iio_counter_signal_unregister(counter, signals + i);
+ return err;
+}
+
+static void iio_counter_signals_unregister(struct iio_counter *const counter,
+ struct iio_counter_signal *signals, size_t num_signals)
+{
+ if (!counter || !signals)
+ return;
+
+ while (num_signals--) {
+ iio_counter_signal_unregister(counter, signals);
+ signals++;
+ }
+}
+
+/**
+ * iio_counter_trigger_register - register Trigger to Value
+ * @value: pointer to IIO Counter Value for association
+ * @trigger: pointer to IIO Counter Trigger to register
+ *
+ * The Trigger is added to the Value's trigger_list. A check is first performed
+ * to verify that the respective Signal is not already linked to the Value; if
+ * the respective Signal is already linked to the Value, the Trigger is not
+ * added to the Value's trigger_list.
+ *
+ * NOTE: This function will acquire and release the Value's trigger_list_lock
+ * during execution.
+ */
+int iio_counter_trigger_register(struct iio_counter_value *const value,
+ struct iio_counter_trigger *const trigger)
+{
+ if (!value || !trigger || !trigger->signal)
+ return -EINVAL;
+
+ mutex_lock(&value->trigger_list_lock);
+ if (iio_counter_trigger_find_by_id(value, trigger->signal->id)) {
+ pr_err("Signal%d is already linked to counter value%d\n",
+ trigger->signal->id, value->id);
+ return -EEXIST;
+ }
+ list_add_tail(&trigger->list, &value->trigger_list);
+ mutex_unlock(&value->trigger_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(iio_counter_trigger_register);
+
+/**
+ * iio_counter_trigger_unregister - unregister Trigger from Value
+ * @value: pointer to IIO Counter Value of association
+ * @trigger: pointer to IIO Counter Trigger to unregister
+ *
+ * The Trigger is removed from the Value's trigger_list.
+ *
+ * NOTE: This function will acquire and release the Value's trigger_list_lock
+ * during execution.
+ */
+void iio_counter_trigger_unregister(struct iio_counter_value *const value,
+ struct iio_counter_trigger *const trigger)
+{
+ if (!value || !trigger || !trigger->signal)
+ return;
+
+ mutex_lock(&value->trigger_list_lock);
+ list_del(&trigger->list);
+ mutex_unlock(&value->trigger_list_lock);
+}
+EXPORT_SYMBOL(iio_counter_trigger_unregister);
+
+/**
+ * iio_counter_triggers_register - register an array of Triggers to Value
+ * @value: pointer to IIO Counter Value for association
+ * @triggers: array of pointers to IIO Counter Triggers to register
+ *
+ * The iio_counter_trigger_register function is called for each Trigger in the
+ * array. The @triggers array is traversed for the first @num_triggers Triggers.
+ *
+ * NOTE: @num_triggers must not be greater than the size of the @triggers array.
+ */
+int iio_counter_triggers_register(struct iio_counter_value *const value,
+ struct iio_counter_trigger *const triggers, const size_t num_triggers)
+{
+ size_t i;
+ int err;
+
+ if (!value || !triggers)
+ return -EINVAL;
+
+ for (i = 0; i < num_triggers; i++) {
+ err = iio_counter_trigger_register(value, triggers + i);
+ if (err)
+ goto err_trigger_register;
+ }
+
+ return 0;
+
+err_trigger_register:
+ while (i--)
+ iio_counter_trigger_unregister(value, triggers + i);
+ return err;
+}
+EXPORT_SYMBOL(iio_counter_triggers_register);
+
+/**
+ * iio_counter_triggers_unregister - unregister Triggers from Value
+ * @value: pointer to IIO Counter Value of association
+ * @triggers: array of pointers to IIO Counter Triggers to unregister
+ *
+ * The iio_counter_trigger_unregister function is called for each Trigger in the
+ * array. The @triggers array is traversed for the first @num_triggers Triggers.
+ *
+ * NOTE: @num_triggers must not be greater than the size of the @triggers array.
+ */
+void iio_counter_triggers_unregister(struct iio_counter_value *const value,
+ struct iio_counter_trigger *triggers, size_t num_triggers)
+{
+ if (!value || !triggers)
+ return;
+
+ while (num_triggers--) {
+ iio_counter_trigger_unregister(value, triggers);
+ triggers++;
+ }
+}
+EXPORT_SYMBOL(iio_counter_triggers_unregister);
+
+/**
+ * iio_counter_value_register - register Value to Counter
+ * @counter: pointer to IIO Counter for association
+ * @value: pointer to IIO Counter Value to register
+ *
+ * The registration process occurs in two major steps. First, the Value is
+ * initialized: trigger_list_lock is initialized, trigger_list is initialized,
+ * and init_triggers if not NULL is passed to iio_counter_triggers_register.
+ * Second, the Value is added to the Counter's value_list. A check is first
+ * performed to verify that the Value is not already associated to the Counter
+ * (via the Value's unique ID); if the Value is already associated to the
+ * Counter, the Value is not added to the Counter's value_list and all of the
+ * Value's Triggers are unregistered.
+ *
+ * NOTE: This function will acquire and release the Counter's value_list_lock
+ * during execution.
+ */
+int iio_counter_value_register(struct iio_counter *const counter,
+ struct iio_counter_value *const value)
+{
+ int err;
+
+ if (!counter || !value)
+ return -EINVAL;
+
+ mutex_init(&value->trigger_list_lock);
+ INIT_LIST_HEAD(&value->trigger_list);
+
+ if (value->init_triggers) {
+ err = iio_counter_triggers_register(value,
+ value->init_triggers, value->num_init_triggers);
+ if (err)
+ return err;
+ }
+
+ mutex_lock(&counter->value_list_lock);
+ if (iio_counter_value_find_by_id(counter, value->id)) {
+ pr_err("Duplicate counter value ID '%d'\n", value->id);
+ err = -EEXIST;
+ goto err_duplicate_id;
+ }
+ list_add_tail(&value->list, &counter->value_list);
+ mutex_unlock(&counter->value_list_lock);
+
+ return 0;
+
+err_duplicate_id:
+ mutex_unlock(&counter->value_list_lock);
+ iio_counter_trigger_unregister_all(value);
+ return err;
+}
+EXPORT_SYMBOL(iio_counter_value_register);
+
+/**
+ * iio_counter_value_unregister - unregister Value from Counter
+ * @counter: pointer to IIO Counter of association
+ * @value: pointer to IIO Counter Value to unregister
+ *
+ * The Value is removed from the Counter's value_list and all of the Value's
+ * Triggers are unregistered.
+ *
+ * NOTE: This function will acquire and release the Counter's value_list_lock
+ * during execution.
+ */
+void iio_counter_value_unregister(struct iio_counter *const counter,
+ struct iio_counter_value *const value)
+{
+ if (!counter || !value)
+ return;
+
+ mutex_lock(&counter->value_list_lock);
+ list_del(&value->list);
+ mutex_unlock(&counter->value_list_lock);
+
+ iio_counter_trigger_unregister_all(value);
+}
+EXPORT_SYMBOL(iio_counter_value_unregister);
+
+/**
+ * iio_counter_values_register - register an array of Values to Counter
+ * @counter: pointer to IIO Counter for association
+ * @values: array of pointers to IIO Counter Values to register
+ *
+ * The iio_counter_value_register function is called for each Value in the
+ * array. The @values array is traversed for the first @num_values Values.
+ *
+ * NOTE: @num_values must not be greater than the size of the @values array.
+ */
+int iio_counter_values_register(struct iio_counter *const counter,
+ struct iio_counter_value *const values, const size_t num_values)
+{
+ size_t i;
+ int err;
+
+ if (!counter || !values)
+ return -EINVAL;
+
+ for (i = 0; i < num_values; i++) {
+ err = iio_counter_value_register(counter, values + i);
+ if (err)
+ goto err_values_register;
+ }
+
+ return 0;
+
+err_values_register:
+ while (i--)
+ iio_counter_value_unregister(counter, values + i);
+ return err;
+}
+EXPORT_SYMBOL(iio_counter_values_register);
+
+/**
+ * iio_counter_values_unregister - unregister Values from Counter
+ * @counter: pointer to IIO Counter of association
+ * @values: array of pointers to IIO Counter Values to unregister
+ *
+ * The iio_counter_value_unregister function is called for each Value in the
+ * array. The @values array is traversed for the first @num_values Values.
+ *
+ * NOTE: @num_values must not be greater than the size of the @values array.
+ */
+void iio_counter_values_unregister(struct iio_counter *const counter,
+ struct iio_counter_value *values, size_t num_values)
+{
+ if (!counter || !values)
+ return;
+
+ while (num_values--) {
+ iio_counter_value_unregister(counter, values);
+ values++;
+ }
+}
+EXPORT_SYMBOL(iio_counter_values_unregister);
+
+/**
+ * iio_counter_register - register Counter to the system
+ * @counter: pointer to IIO Counter to register
+ *
+ * This function piggybacks off of iio_device_register. First, the relevant
+ * Counter members are initialized; if init_signals is not NULL it is passed to
+ * iio_counter_signals_register, and similarly if init_values is not NULL it is
+ * passed to iio_counter_values_register. Next, a struct iio_dev is allocated by
+ * a call to iio_device_alloc and initialized for the Counter, IIO channels are
+ * allocated, the Counter is copied as the private data, and finally
+ * iio_device_register is called.
+ */
+int iio_counter_register(struct iio_counter *const counter)
+{
+ const struct iio_info info_default = {
+ .read_raw = iio_counter_read_raw,
+ .write_raw = iio_counter_write_raw
+ };
+ int err;
+ struct iio_info *info;
+ struct iio_counter *priv;
+
+ if (!counter)
+ return -EINVAL;
+
+ mutex_init(&counter->signal_list_lock);
+ INIT_LIST_HEAD(&counter->signal_list);
+
+ if (counter->init_signals) {
+ err = iio_counter_signals_register(counter,
+ counter->init_signals, counter->num_init_signals);
+ if (err)
+ return err;
+ }
+
+ mutex_init(&counter->value_list_lock);
+ INIT_LIST_HEAD(&counter->value_list);
+
+ if (counter->init_values) {
+ err = iio_counter_values_register(counter,
+ counter->init_values, counter->num_init_values);
+ if (err)
+ goto err_values_register;
+ }
+
+ counter->indio_dev = iio_device_alloc(sizeof(*counter));
+ if (!counter->indio_dev) {
+ err = -ENOMEM;
+ goto err_iio_device_alloc;
+ }
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ err = -ENOMEM;
+ goto err_info_alloc;
+ }
+ if (counter->info) {
+ memcpy(info, counter->info, sizeof(*counter->info));
+ info->read_raw = iio_counter_read_raw;
+ info->write_raw = iio_counter_write_raw;
+ } else {
+ memcpy(info, &info_default, sizeof(info_default));
+ }
+
+ counter->indio_dev->info = info;
+ counter->indio_dev->modes = INDIO_DIRECT_MODE;
+ counter->indio_dev->name = counter->name;
+ counter->indio_dev->dev.parent = counter->dev;
+
+ err = iio_counter_channels_alloc(counter);
+ if (err)
+ goto err_channels_alloc;
+
+ priv = iio_priv(counter->indio_dev);
+ memcpy(priv, counter, sizeof(*priv));
+
+ err = iio_device_register(priv->indio_dev);
+ if (err)
+ goto err_iio_device_register;
+
+ return 0;
+
+err_iio_device_register:
+ iio_counter_channels_free(counter);
+err_channels_alloc:
+ kfree(info);
+err_info_alloc:
+ iio_device_free(counter->indio_dev);
+err_iio_device_alloc:
+ iio_counter_values_unregister(counter, counter->init_values,
+ counter->num_init_values);
+err_values_register:
+ iio_counter_signals_unregister(counter, counter->init_signals,
+ counter->num_init_signals);
+ return err;
+}
+EXPORT_SYMBOL(iio_counter_register);
+
+/**
+ * iio_counter_unregister - unregister Counter from the system
+ * @counter: pointer to IIO Counter to unregister
+ *
+ * The Counter is unregistered from the system. The indio_dev is unregistered,
+ * allocated memory is freed, and all associated Values and Signals are
+ * unregistered.
+ */
+void iio_counter_unregister(struct iio_counter *const counter)
+{
+ const struct iio_info *const info = counter->indio_dev->info;
+
+ if (!counter)
+ return;
+
+ iio_device_unregister(counter->indio_dev);
+
+ iio_counter_channels_free(counter);
+
+ kfree(info);
+ iio_device_free(counter->indio_dev);
+
+ iio_counter_value_unregister_all(counter);
+ iio_counter_signal_unregister_all(counter);
+}
+EXPORT_SYMBOL(iio_counter_unregister);
diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
new file mode 100644
index 000000000000..1c1ca13a6053
--- /dev/null
+++ b/include/linux/iio/counter.h
@@ -0,0 +1,221 @@
+/*
+ * Industrial I/O counter interface
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#ifndef _IIO_COUNTER_H_
+#define _IIO_COUNTER_H_
+
+#ifdef CONFIG_IIO_COUNTER
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+/**
+ * struct iio_counter_signal - IIO Counter Signal node
+ * @id: [DRIVER] unique ID used to identify signal
+ * @name: [DRIVER] device-specific signal name
+ * @list: [INTERN] list of all signals currently registered to counter
+ */
+struct iio_counter_signal {
+ int id;
+ const char *name;
+
+ struct list_head list;
+};
+
+/**
+ * struct iio_counter_trigger - IIO Counter Trigger node
+ * @mode: [DRIVER] current trigger mode state
+ * @trigger_modes: [DRIVER] available trigger modes
+ * @num_trigger_modes: [DRIVER] number of modes specified in @trigger_modes
+ * @signal: [DRIVER] pointer to associated signal
+ * @list: [INTERN] list of all triggers currently registered to
+ * value
+ */
+struct iio_counter_trigger {
+ unsigned int mode;
+ const char *const *trigger_modes;
+ unsigned int num_trigger_modes;
+ struct iio_counter_signal *signal;
+
+ struct list_head list;
+};
+
+/**
+ * struct iio_counter_value - IIO Counter Value node
+ * @id: [DRIVER] unique ID used to identify value
+ * @name: [DRIVER] device-specific value name
+ * @mode: [DRIVER] current function mode state
+ * @function_modes: [DRIVER] available function modes
+ * @num_function_modes: [DRIVER] number of modes specified in @function_modes
+ * @init_triggers: [DRIVER] array of triggers for initialization
+ * @num_init_triggers: [DRIVER] number of triggers specified in @init_triggers
+ * @function_enum: [INTERN] used internally to generate function attributes
+ * @trigger_list_lock: [INTERN] lock for accessing @trigger_list
+ * @trigger_list: [INTERN] list of all triggers currently registered to
+ * value
+ * @list: [INTERN] list of all values currently registered to
+ * counter
+ */
+struct iio_counter_value {
+ int id;
+ const char *name;
+ unsigned int mode;
+ const char *const *function_modes;
+ unsigned int num_function_modes;
+
+ struct iio_counter_trigger *init_triggers;
+ size_t num_init_triggers;
+
+ struct iio_enum function_enum;
+ struct mutex trigger_list_lock;
+ struct list_head trigger_list;
+
+ struct list_head list;
+};
+
+struct iio_counter;
+
+/**
+ * struct iio_counter_ops - IIO Counter related callbacks
+ * @signal_read: function to request a signal value from the device.
+ * Return value will specify the type of value returned by
+ * the device. val and val2 will contain the elements
+ * making up the returned value. Note that the counter
+ * signal_list_lock is acquired before this function is
+ * called, and released after this function returns.
+ * @signal_write: function to write a signal value to the device.
+ * Parameters and locking behavior are the same as
+ * signal_read.
+ * @trigger_mode_set: function to set the trigger mode. mode is the index of
+ * the requested mode from the value trigger_modes array.
+ * Note that the counter value_list_lock and value
+ * trigger_list_lock are acquired before this function is
+ * called, and released after this function returns.
+ * @trigger_mode_get: function to get the current trigger mode. Return value
+ * will specify the index of the current mode from the
+ * value trigger_modes array. Locking behavior is the same
+ * as trigger_mode_set.
+ * @value_read: function to request a value value from the device.
+ * Return value will specify the type of value returned by
+ * the device. val and val2 will contain the elements
+ * making up the returned value. Note that the counter
+ * value_list_lock is acquired before this function is
+ * called, and released after this function returns.
+ * @value_write: function to write a value value to the device.
+ * Parameters and locking behavior are the same as
+ * value_read.
+ * @value_function_set: function to set the value function mode. mode is the
+ * index of the requested mode from the value
+ * function_modes array. Note that the counter
+ * value_list_lock is acquired before this function is
+ * called, and released after this function returns.
+ * @value_function_get: function to get the current value function mode. Return
+ * value will specify the index of the current mode from
+ * the value function_modes array. Locking behavior is the
+ * same as value_function_get.
+ */
+struct iio_counter_ops {
+ int (*signal_read)(struct iio_counter *counter,
+ struct iio_counter_signal *signal, int *val, int *val2);
+ int (*signal_write)(struct iio_counter *counter,
+ struct iio_counter_signal *signal, int val, int val2);
+ int (*trigger_mode_set)(struct iio_counter *counter,
+ struct iio_counter_value *value,
+ struct iio_counter_trigger *trigger, unsigned int mode);
+ int (*trigger_mode_get)(struct iio_counter *counter,
+ struct iio_counter_value *value,
+ struct iio_counter_trigger *trigger);
+ int (*value_read)(struct iio_counter *counter,
+ struct iio_counter_value *value, int *val, int *val2);
+ int (*value_write)(struct iio_counter *counter,
+ struct iio_counter_value *value, int val, int val2);
+ int (*value_function_set)(struct iio_counter *counter,
+ struct iio_counter_value *value, unsigned int mode);
+ int (*value_function_get)(struct iio_counter *counter,
+ struct iio_counter_value *value);
+};
+
+/**
+ * struct iio_counter - IIO Counter data structure
+ * @id: [DRIVER] unique ID used to identify counter
+ * @name: [DRIVER] name of the device
+ * @dev: [DRIVER] device structure, should be assigned a parent
+ * and owner
+ * @ops: [DRIVER] callbacks from driver for counter components
+ * @init_signals: [DRIVER] array of signals for initialization
+ * @num_init_signals: [DRIVER] number of signals specified in @init_signals
+ * @init_values: [DRIVER] array of values for initialization
+ * @num_init_values: [DRIVER] number of values specified in @init_values
+ * @signal_list_lock: [INTERN] lock for accessing @signal_list
+ * @signal_list: [INTERN] list of all signals currently registered to
+ * counter
+ * @value_list_lock: [INTERN] lock for accessing @value_list
+ * @value_list: [INTERN] list of all values currently registered to
+ * counter
+ * @channels: [DRIVER] channel specification structure table
+ * @num_channels: [DRIVER] number of channels specified in @channels
+ * @info: [DRIVER] callbacks and constant info from driver
+ * @indio_dev: [INTERN] industrial I/O device structure
+ * @driver_data: [DRIVER] driver data
+ */
+struct iio_counter {
+ int id;
+ const char *name;
+ struct device *dev;
+ const struct iio_counter_ops *ops;
+
+ struct iio_counter_signal *init_signals;
+ size_t num_init_signals;
+ struct iio_counter_value *init_values;
+ size_t num_init_values;
+
+ struct mutex signal_list_lock;
+ struct list_head signal_list;
+ struct mutex value_list_lock;
+ struct list_head value_list;
+
+ const struct iio_chan_spec *channels;
+ size_t num_channels;
+ const struct iio_info *info;
+
+ struct iio_dev *indio_dev;
+ void *driver_data;
+};
+
+int iio_counter_trigger_register(struct iio_counter_value *const value,
+ struct iio_counter_trigger *const trigger);
+void iio_counter_trigger_unregister(struct iio_counter_value *const value,
+ struct iio_counter_trigger *const trigger);
+int iio_counter_triggers_register(struct iio_counter_value *const value,
+ struct iio_counter_trigger *const triggers, const size_t num_triggers);
+void iio_counter_triggers_unregister(struct iio_counter_value *const value,
+ struct iio_counter_trigger *triggers, size_t num_triggers);
+
+int iio_counter_value_register(struct iio_counter *const counter,
+ struct iio_counter_value *const value);
+void iio_counter_value_unregister(struct iio_counter *const counter,
+ struct iio_counter_value *const value);
+int iio_counter_values_register(struct iio_counter *const counter,
+ struct iio_counter_value *const values, const size_t num_values);
+void iio_counter_values_unregister(struct iio_counter *const counter,
+ struct iio_counter_value *values, size_t num_values);
+
+int iio_counter_register(struct iio_counter *const counter);
+void iio_counter_unregister(struct iio_counter *const counter);
+
+#endif /* CONFIG_IIO_COUNTER */
+
+#endif /* _IIO_COUNTER_H_ */
--
2.14.1

2017-09-25 18:08:26

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v2 1/5] iio: Implement counter channel specification and IIO_SIGNAL constant

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

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

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/industrialio-core.c | 14 +++++++++++++-
include/linux/iio/iio.h | 2 ++
include/uapi/linux/iio/types.h | 1 +
3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 7a5aa127c52e..ee508f2070a7 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_COUNT] = "count",
[IIO_INDEX] = "index",
[IIO_GRAVITY] = "gravity",
+ [IIO_SIGNAL] = "signal",
};

static const char * const iio_modifier_names[] = {
@@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
break;

case IIO_SEPARATE:
- if (chan->indexed)
+ if (chan->counter) {
+ if (!chan->indexed) {
+ WARN(1, "Counter channels must be indexed\n");
+ ret = -EINVAL;
+ goto error_free_full_postfix;
+ }
+ name = kasprintf(GFP_KERNEL, "%s%d-%d_%s",
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ chan->channel2,
+ full_postfix);
+ } else if (chan->indexed)
name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 486ffbb1a926..de4dc2a0e4e3 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -263,6 +263,7 @@ struct iio_event_spec {
* attributes but not for event codes.
* @output: Channel is output.
* @differential: Channel is differential.
+ * @counter: Channel is part of a counter.
*/
struct iio_chan_spec {
enum iio_chan_type type;
@@ -295,6 +296,7 @@ struct iio_chan_spec {
unsigned indexed:1;
unsigned output:1;
unsigned differential:1;
+ unsigned counter:1;
};


diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index ffafd6c25a48..313899652ca7 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -43,6 +43,7 @@ enum iio_chan_type {
IIO_COUNT,
IIO_INDEX,
IIO_GRAVITY,
+ IIO_SIGNAL,
};

enum iio_modifier {
--
2.14.1

2017-09-25 18:09:11

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v2 3/5] iio: Documentation: Add IIO Generic Counter sysfs documentation

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

Signed-off-by: William Breathitt Gray <[email protected]>
---
.../testing/sysfs-bus-iio-generic-counter-sysfs | 63 ++++++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs

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

2017-09-25 18:09:35

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v2 4/5] docs: Add IIO Generic Counter Interface documentation

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

Signed-off-by: William Breathitt Gray <[email protected]>
---
Documentation/driver-api/iio/generic-counter.txt | 555 +++++++++++++++++++++++
1 file changed, 555 insertions(+)
create mode 100644 Documentation/driver-api/iio/generic-counter.txt

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

2017-09-25 18:09:48

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v2 5/5] iio: 104-quad-8: Add IIO generic counter interface support

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

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

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

A quad8_remove function is defined to call iio_counter_unregister. This
function can be eliminated once a devm_iio_counter_register function is
defined.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/counter/104-quad-8.c | 306 ++++++++++++++++++++++++++++++++++++---
1 file changed, 289 insertions(+), 17 deletions(-)

diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c
index b56985078d8c..625b49fe91cf 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 iio_counter
* @preset: array of preset values
* @count_mode: array of count mode configurations
* @quadrature_mode: array of quadrature mode configurations
@@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
* @base: base port address of the IIO device
*/
struct quad8_iio {
+ struct iio_counter counter;
unsigned int preset[QUAD8_NUM_COUNTERS];
unsigned int count_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
@@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {
QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
};

+static int quad8_signal_read(struct iio_counter *counter,
+ struct iio_counter_signal *signal, int *val, int *val2)
+{
+ struct quad8_iio *const priv = counter->driver_data;
+
+ if (signal->id < 16)
+ return -EINVAL;
+
+ *val = !!(inb(priv->base + 0x16) & BIT(signal->id - 16));
+
+ return IIO_VAL_INT;
+}
+
+static int quad8_trigger_mode_get(struct iio_counter *counter,
+ struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+ struct quad8_iio *const priv = counter->driver_data;
+ const unsigned int mode = priv->quadrature_mode[value->id];
+ const unsigned int scale = priv->quadrature_scale[value->id];
+ unsigned int direction;
+ const unsigned int flag_addr = priv->base + 2 * value->id + 1;
+ const int signal_id = trigger->signal->id % 2;
+
+ if (mode)
+ switch (scale) {
+ case 0:
+ /* U/D flag: 1 = up, 0 = down */
+ /* direction: 0 = up, 1 = down */
+ direction = !(inb(flag_addr) & BIT(5));
+ if (!signal_id)
+ return direction + 1;
+ break;
+ case 1:
+ if (!signal_id)
+ return 3;
+ break;
+ case 2:
+ return 3;
+ }
+ else
+ if (!signal_id)
+ return 1;
+
+ return 0;
+}
+
+static int quad8_value_read(struct iio_counter *counter,
+ struct iio_counter_value *value, int *val, int *val2)
+{
+ struct quad8_iio *const priv = counter->driver_data;
+ const int base_offset = priv->base + 2 * value->id;
+ unsigned int flags;
+ unsigned int borrow;
+ unsigned int carry;
+ int i;
+
+ flags = inb(base_offset + 1);
+ borrow = flags & BIT(0);
+ carry = !!(flags & BIT(1));
+
+ /* Borrow XOR Carry effectively doubles count range */
+ *val = (borrow ^ carry) << 24;
+
+ /* Reset Byte Pointer; transfer Counter to Output Latch */
+ outb(0x11, base_offset + 1);
+
+ for (i = 0; i < 3; i++)
+ *val |= (unsigned int)inb(base_offset) << (8 * i);
+
+ return IIO_VAL_INT;
+}
+
+static int quad8_value_write(struct iio_counter *counter,
+ struct iio_counter_value *value, int val, int val2)
+{
+ struct quad8_iio *const priv = counter->driver_data;
+ const int base_offset = priv->base + 2 * value->id;
+ int i;
+
+ /* Only 24-bit values are supported */
+ if ((unsigned int)val > 0xFFFFFF)
+ return -EINVAL;
+
+ /* Reset Byte Pointer */
+ outb(0x01, base_offset + 1);
+
+ /* Counter can only be set via Preset Register */
+ for (i = 0; i < 3; i++)
+ outb(val >> (8 * i), base_offset);
+
+ /* Transfer Preset Register to Counter */
+ outb(0x08, base_offset + 1);
+
+ /* Reset Byte Pointer */
+ outb(0x01, base_offset + 1);
+
+ /* Set Preset Register back to original value */
+ val = priv->preset[value->id];
+ for (i = 0; i < 3; i++)
+ outb(val >> (8 * i), base_offset);
+
+ /* Reset Borrow, Carry, Compare, and Sign flags */
+ outb(0x02, base_offset + 1);
+ /* Reset Error flag */
+ outb(0x06, base_offset + 1);
+
+ return 0;
+}
+
+static int quad8_value_function_set(struct iio_counter *counter,
+ struct iio_counter_value *value, unsigned int mode)
+{
+ struct quad8_iio *const priv = counter->driver_data;
+ const unsigned int mode_cfg = mode << 3 |
+ priv->count_mode[value->id] << 1;
+ const unsigned int idr_cfg = priv->index_polarity[value->id] << 1;
+ const int base_offset = priv->base + 2 * value->id + 1;
+
+ if (mode)
+ priv->quadrature_scale[value->id] = mode - 1;
+ else {
+ /* Quadrature scaling only available in quadrature mode */
+ priv->quadrature_scale[value->id] = 0;
+
+ /* Synchronous function not supported in non-quadrature mode */
+ if (priv->synchronous_mode[value->id]) {
+ priv->synchronous_mode[value->id] = 0;
+ outb(0x60 | idr_cfg, base_offset);
+ }
+ }
+
+ priv->quadrature_mode[value->id] = !!mode;
+
+ /* Load mode configuration to Counter Mode Register */
+ outb(0x20 | mode_cfg, base_offset);
+
+ return 0;
+}
+
+static int quad8_value_function_get(struct iio_counter *counter,
+ struct iio_counter_value *value)
+{
+ struct quad8_iio *const priv = counter->driver_data;
+ unsigned int quadrature_mode = priv->quadrature_mode[value->id];
+
+ return (quadrature_mode) ? priv->quadrature_scale[value->id] + 1 : 0;
+}
+
+static const struct iio_counter_ops quad8_ops = {
+ .signal_read = quad8_signal_read,
+ .trigger_mode_get = quad8_trigger_mode_get,
+ .value_read = quad8_value_read,
+ .value_write = quad8_value_write,
+ .value_function_set = quad8_value_function_set,
+ .value_function_get = quad8_value_function_get
+};
+
+static const char *const quad8_function_modes[] = {
+ "non-quadrature",
+ "quadrature x1",
+ "quadrature x2",
+ "quadrature x4"
+};
+
+#define QUAD8_SIGNAL(_id, _name) { \
+ .id = _id, \
+ .name = _name \
+}
+
+static const struct iio_counter_signal quad8_signals[] = {
+ QUAD8_SIGNAL(0, "Channel 1 Quadrature A"),
+ QUAD8_SIGNAL(1, "Channel 1 Quadrature B"),
+ QUAD8_SIGNAL(2, "Channel 2 Quadrature A"),
+ QUAD8_SIGNAL(3, "Channel 2 Quadrature B"),
+ QUAD8_SIGNAL(4, "Channel 3 Quadrature A"),
+ QUAD8_SIGNAL(5, "Channel 3 Quadrature B"),
+ QUAD8_SIGNAL(6, "Channel 4 Quadrature A"),
+ QUAD8_SIGNAL(7, "Channel 4 Quadrature B"),
+ QUAD8_SIGNAL(8, "Channel 5 Quadrature A"),
+ QUAD8_SIGNAL(9, "Channel 5 Quadrature B"),
+ QUAD8_SIGNAL(10, "Channel 6 Quadrature A"),
+ QUAD8_SIGNAL(11, "Channel 6 Quadrature B"),
+ QUAD8_SIGNAL(12, "Channel 7 Quadrature A"),
+ QUAD8_SIGNAL(13, "Channel 7 Quadrature B"),
+ QUAD8_SIGNAL(14, "Channel 8 Quadrature A"),
+ QUAD8_SIGNAL(15, "Channel 8 Quadrature B"),
+ QUAD8_SIGNAL(16, "Channel 1 Index"),
+ QUAD8_SIGNAL(17, "Channel 2 Index"),
+ QUAD8_SIGNAL(18, "Channel 3 Index"),
+ QUAD8_SIGNAL(19, "Channel 4 Index"),
+ QUAD8_SIGNAL(20, "Channel 5 Index"),
+ QUAD8_SIGNAL(21, "Channel 6 Index"),
+ QUAD8_SIGNAL(22, "Channel 7 Index"),
+ QUAD8_SIGNAL(23, "Channel 8 Index")
+};
+
+#define QUAD8_VALUE(_id, _name) { \
+ .id = _id, \
+ .name = _name, \
+ .mode = 0, \
+ .function_modes = quad8_function_modes, \
+ .num_function_modes = ARRAY_SIZE(quad8_function_modes) \
+}
+
+static const struct iio_counter_value quad8_values[] = {
+ QUAD8_VALUE(0, "Channel 1 Count"), QUAD8_VALUE(1, "Channel 2 Count"),
+ QUAD8_VALUE(2, "Channel 3 Count"), QUAD8_VALUE(3, "Channel 4 Count"),
+ QUAD8_VALUE(4, "Channel 5 Count"), QUAD8_VALUE(5, "Channel 6 Count"),
+ QUAD8_VALUE(6, "Channel 7 Count"), QUAD8_VALUE(7, "Channel 8 Count")
+};
+
+static const char *const quad8_trigger_modes[] = {
+ "none",
+ "rising edge",
+ "falling edge",
+ "both edges"
+};
+
static int quad8_probe(struct device *dev, unsigned int id)
{
- struct iio_dev *indio_dev;
- struct quad8_iio *priv;
+ struct iio_counter_signal *init_signals;
+ const size_t num_init_signals = ARRAY_SIZE(quad8_signals);
+ struct iio_counter_value *init_values;
+ const size_t num_init_values = ARRAY_SIZE(quad8_values);
+ struct iio_counter_trigger *triggers;
+ struct quad8_iio *quad8iio;
int i, j;
unsigned int base_offset;

- indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
- if (!indio_dev)
- return -ENOMEM;
-
- if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
- dev_name(dev))) {
+ if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
base[id], base[id] + QUAD8_EXTENT);
return -EBUSY;
}

- indio_dev->info = &quad8_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
- indio_dev->channels = quad8_channels;
- indio_dev->name = dev_name(dev);
- indio_dev->dev.parent = dev;
+ init_signals = devm_kmalloc(dev, sizeof(quad8_signals), GFP_KERNEL);
+ if (!init_signals)
+ return -ENOMEM;
+
+ memcpy(init_signals, quad8_signals, sizeof(quad8_signals));
+
+ init_values = devm_kmalloc(dev, sizeof(quad8_values), GFP_KERNEL);
+ if (!init_values)
+ return -ENOMEM;
+
+ memcpy(init_values, quad8_values, sizeof(quad8_values));
+
+ /* Associate values with their respective signals */
+ for (i = 0; i < num_init_values; i++) {
+ triggers = devm_kmalloc(dev, 2 * sizeof(*triggers), GFP_KERNEL);
+ if (!triggers)
+ return -ENOMEM;
+
+ /* Starts up in non-quadrature mode */
+ triggers[0].mode = 1;
+ triggers[0].trigger_modes = quad8_trigger_modes;
+ triggers[0].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+ triggers[0].signal = &init_signals[2 * i];
+ triggers[1].mode = 0;
+ triggers[1].trigger_modes = quad8_trigger_modes;
+ triggers[1].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+ triggers[1].signal = &init_signals[2 * i + 1];
+
+ init_values[i].init_triggers = triggers;
+ init_values[i].num_init_triggers = 2;
+ }
+
+ quad8iio = devm_kzalloc(dev, sizeof(*quad8iio), GFP_KERNEL);
+ if (!quad8iio)
+ return -ENOMEM;

- priv = iio_priv(indio_dev);
- priv->base = base[id];
+ quad8iio->counter.name = dev_name(dev);
+ quad8iio->counter.dev = dev;
+ quad8iio->counter.ops = &quad8_ops;
+ quad8iio->counter.init_signals = init_signals;
+ quad8iio->counter.num_init_signals = num_init_signals;
+ quad8iio->counter.init_values = init_values;
+ quad8iio->counter.num_init_values = num_init_values;
+ quad8iio->counter.channels = quad8_channels;
+ quad8iio->counter.num_channels = ARRAY_SIZE(quad8_channels);
+ quad8iio->counter.info = &quad8_info;
+ quad8iio->counter.driver_data = quad8iio;
+ quad8iio->base = base[id];

/* Reset all counters and disable interrupt function */
outb(0x01, base[id] + 0x11);
@@ -579,11 +839,23 @@ 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);
+ dev_set_drvdata(dev, &quad8iio->counter);
+
+ return iio_counter_register(&quad8iio->counter);
+}
+
+static int quad8_remove(struct device *dev, unsigned int id)
+{
+ struct iio_counter *counter = dev_get_drvdata(dev);
+
+ iio_counter_unregister(counter);
+
+ return 0;
}

static struct isa_driver quad8_driver = {
.probe = quad8_probe,
+ .remove = quad8_remove,
.driver = {
.name = "104-quad-8"
}
--
2.14.1

2017-09-29 13:42:09

by Benjamin Gaignard

[permalink] [raw]
Subject: Re: [PATCH v2 2/5] iio: Introduce the generic counter interface

2017-09-25 20:08 GMT+02:00 William Breathitt Gray <[email protected]>:
> This patch introduces the IIO generic counter interface for supporting
> counter devices. The generic counter interface serves as a catch-all to
> enable rudimentary support for devices that qualify as counters. More
> specific and apt counter interfaces may be developed on top of the
> generic counter interface, and those interfaces should be used by
> drivers when possible rather than the generic counter interface.
>
> In the context of the IIO generic counter interface, a counter is
> defined as a device that reports one or more "counter values" based on
> the state changes of one or more "counter signals" as evaluated by a
> defined "counter function."
>
> The IIO generic counter interface piggybacks off of the IIO core. This
> is primarily used to leverage the existing sysfs setup: the IIO_COUNT
> channel attributes represent the "counter value," while the IIO_SIGNAL
> channel attributes represent the "counter signal;" auxilary IIO_COUNT
> attributes represent the "counter signal" connections and their
> respective state change configurations which trigger an associated
> "counter function" evaluation.
>
> The iio_counter_ops structure serves as a container for driver callbacks
> to communicate with the device; function callbacks are provided to read
> and write various "counter signals" and "counter values," and set and
> get the "trigger mode" and "function mode" for various "counter signals"
> and "counter values" respectively.
>
> To support a counter device, a driver must first allocate the available
> "counter signals" via iio_counter_signal structures. These "counter
> signals" should be stored as an array and set to the init_signals member
> of an allocated iio_counter structure before the counter is registered.
>
> "Counter values" may be allocated via iio_counter_value structures, and
> respective "counter signal" associations made via iio_counter_trigger
> structures. Initial associated iio_counter_trigger structures may be
> stored as an array and set to the the init_triggers member of the
> respective iio_counter_value structure. These iio_counter_value
> structures may be set to the init_values member of an allocated
> iio_counter structure before the counter is registered if so desired.
>
> A counter device is registered to the system by passing the respective
> initialized iio_counter structure to the iio_counter_register function;
> similarly, the iio_counter_unregister function unregisters the
> respective counter.
>
> Signed-off-by: William Breathitt Gray <[email protected]>

Hi William,

Thanks for your patches, I start try to implement them in stm32-timer driver
but I think it will take me some time before understand all your code.

I have some warning when compiling the code (see below).

About wording, I think that using "trigger" to describe signal active
states/edges
could be confusing in IIO context but I haven't found yet a better name.

I also not very sure about what you expect from iio_counter_ops signal_read and
signal_write functions, do you think get/set the value of the signal ?
(i.e read gpio level ?)

I will continue to review and implement your patches, I hope that end
of next week
have something functionnal to share with you.

Thansk to have propose this, I do believe it will be helpful

Benjamin
> ---
> MAINTAINERS | 7 +
> drivers/iio/Kconfig | 8 +
> drivers/iio/Makefile | 1 +
> drivers/iio/counter/Kconfig | 1 +
> drivers/iio/industrialio-counter.c | 1151 ++++++++++++++++++++++++++++++++++++
> include/linux/iio/counter.h | 221 +++++++
> 6 files changed, 1389 insertions(+)
> create mode 100644 drivers/iio/industrialio-counter.c
> create mode 100644 include/linux/iio/counter.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6f7721d1634c..24fc2dcf1995 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6577,6 +6577,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
> F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> F: drivers/iio/adc/envelope-detector.c
>
> +IIO GENERIC COUNTER INTERFACE
> +M: William Breathitt Gray <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/industrialio-counter.c
> +F: include/linux/iio/counter.h
> +
> IIO MULTIPLEXER
> M: Peter Rosin <[email protected]>
> L: [email protected]
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index b3c8c6ef0dff..78e01f4f5937 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -30,6 +30,14 @@ config IIO_CONFIGFS
> (e.g. software triggers). For more info see
> Documentation/iio/iio_configfs.txt.
>
> +config IIO_COUNTER
> + bool "Enable IIO counter support"
> + help
> + Provides IIO core support for counters. This API provides
> + a generic interface that serves as the building blocks to
> + create more complex counter interfaces. Rudimentary support
> + for counters is enabled.
> +
> config IIO_TRIGGER
> bool "Enable triggered sampling support"
> help
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 93c769cd99bf..6427ff38f964 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -5,6 +5,7 @@
> obj-$(CONFIG_IIO) += industrialio.o
> industrialio-y := industrialio-core.o industrialio-event.o inkern.o
> industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
> +industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
> industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>
> obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index b37e5fc03149..3d46a790d8db 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -4,6 +4,7 @@
> # When adding new entries keep the list in alphabetical order
>
> menu "Counters"
> + depends on IIO_COUNTER
>
> config 104_QUAD_8
> tristate "ACCES 104-QUAD-8 driver"
> diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
> new file mode 100644
> index 000000000000..bdf190d010e4
> --- /dev/null
> +++ b/drivers/iio/industrialio-counter.c
> @@ -0,0 +1,1151 @@
> +/*
> + * Industrial I/O counter interface functions
> + * Copyright (C) 2017 William Breathitt Gray
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/types.h>
> +
> +#include <linux/iio/counter.h>
> +
> +static struct iio_counter_signal *iio_counter_signal_find_by_id(
> + const struct iio_counter *const counter, const int id)
> +{
> + struct iio_counter_signal *iter;
> +
> + list_for_each_entry(iter, &counter->signal_list, list)
> + if (iter->id == id)
> + return iter;
> +
> + return NULL;
> +}
> +
> +static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
> + const struct iio_counter_value *const value, const int id)
> +{
> + struct iio_counter_trigger *iter;
> +
> + list_for_each_entry(iter, &value->trigger_list, list)
> + if (iter->signal->id == id)
> + return iter;
> +
> + return NULL;
> +}
> +
> +static struct iio_counter_value *iio_counter_value_find_by_id(
> + const struct iio_counter *const counter, const int id)
> +{
> + struct iio_counter_value *iter;
> +
> + list_for_each_entry(iter, &counter->value_list, list)
> + if (iter->id == id)
> + return iter;
> +
> + return NULL;
> +}
> +
> +static void iio_counter_trigger_unregister_all(
> + struct iio_counter_value *const value)
> +{
> + struct iio_counter_trigger *iter, *tmp_iter;
> +
> + mutex_lock(&value->trigger_list_lock);
> + list_for_each_entry_safe(iter, tmp_iter, &value->trigger_list, list)
> + list_del(&iter->list);
> + mutex_unlock(&value->trigger_list_lock);
> +}
> +
> +static void iio_counter_signal_unregister_all(struct iio_counter *const counter)
> +{
> + struct iio_counter_signal *iter, *tmp_iter;
> +
> + mutex_lock(&counter->signal_list_lock);
> + list_for_each_entry_safe(iter, tmp_iter, &counter->signal_list, list)
> + list_del(&iter->list);
> + mutex_unlock(&counter->signal_list_lock);
> +}
> +
> +static void iio_counter_value_unregister_all(struct iio_counter *const counter)
> +{
> + struct iio_counter_value *iter, *tmp_iter;
> +
> + mutex_lock(&counter->value_list_lock);
> + list_for_each_entry_safe(iter, tmp_iter, &counter->value_list, list) {
> + iio_counter_trigger_unregister_all(iter);
> +
> + list_del(&iter->list);
> + }
> + mutex_unlock(&counter->value_list_lock);
> +}
> +
> +static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + const struct iio_counter_signal *signal;
> +
> + mutex_lock(&counter->signal_list_lock);
> + signal = iio_counter_signal_find_by_id(counter, chan->channel2);
> + mutex_unlock(&counter->signal_list_lock);
> + if (!signal)
> + return -EINVAL;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
> +}
> +
> +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + const struct iio_counter_value *value;
> +
> + mutex_lock(&counter->value_list_lock);
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + mutex_unlock(&counter->value_list_lock);
> + if (!value)
> + return -EINVAL;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
> +}
> +
> +static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_value *value;
> + const struct iio_counter_trigger *trigger;
> + ssize_t len = 0;
> +
> + mutex_lock(&counter->value_list_lock);
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + len = -EINVAL;
> + goto err_find_value;
> + }
> +
> + mutex_lock(&value->trigger_list_lock);
> + list_for_each_entry(trigger, &value->trigger_list, list) {
> + len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
> + trigger->signal->id, trigger->signal->name,
> + trigger->trigger_modes[trigger->mode]);
> + if (len >= PAGE_SIZE) {
> + len = -ENOMEM;
> + goto err_no_buffer_space;
> + }
> + }
> +err_no_buffer_space:
> + mutex_unlock(&value->trigger_list_lock);
> +
> +err_find_value:
> + mutex_unlock(&counter->value_list_lock);
> +
> + return len;
> +}
> +
> +static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_value *value;
> + ssize_t ret;
> + struct iio_counter_trigger *trigger;
> + const int signal_id = *(int *)((void *)priv);
> + int mode;
> +
> + if (!counter->ops->trigger_mode_get)
> + return -EINVAL;
> +
> + mutex_lock(&counter->value_list_lock);
> +
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + ret = -EINVAL;
> + goto err_value;
> + }
> +
> + mutex_lock(&value->trigger_list_lock);
> +
> + trigger = iio_counter_trigger_find_by_id(value, signal_id);
> + if (!trigger) {
> + ret = -EINVAL;
> + goto err_trigger;
> + }
> +
> + mode = counter->ops->trigger_mode_get(counter, value, trigger);
> +
> + if (mode < 0) {
> + ret = mode;
> + goto err_trigger;
> + } else if (mode >= trigger->num_trigger_modes) {
> + ret = -EINVAL;
> + goto err_trigger;
> + }
> +
> + trigger->mode = mode;
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
> +
> + mutex_unlock(&value->trigger_list_lock);
> +
> + mutex_unlock(&counter->value_list_lock);
> +
> + return ret;
> +
> +err_trigger:
> + mutex_unlock(&value->trigger_list_lock);
> +err_value:
> + mutex_unlock(&counter->value_list_lock);
> + return ret;
> +}
> +
> +static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
> + uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
> + size_t len)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_value *value;
> + ssize_t err;
> + struct iio_counter_trigger *trigger;
> + const int signal_id = *(int *)((void *)priv);
> + unsigned int mode;
> +
> + if (!counter->ops->trigger_mode_set)
> + return -EINVAL;
> +
> + mutex_lock(&counter->value_list_lock);
> +
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + err = -EINVAL;
> + goto err_value;
> + }
> +
> + mutex_lock(&value->trigger_list_lock);
> +
> + trigger = iio_counter_trigger_find_by_id(value, signal_id);
> + if (!trigger) {
> + err = -EINVAL;
> + goto err_trigger;
> + }
> +
> + for (mode = 0; mode < trigger->num_trigger_modes; mode++)
> + if (sysfs_streq(buf, trigger->trigger_modes[mode]))
> + break;
> +
> + if (mode >= trigger->num_trigger_modes) {
> + err = -EINVAL;
> + goto err_trigger;
> + }
> +
> + err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
> + if (err)
> + goto err_trigger;
> +
> + trigger->mode = mode;
> +
> + mutex_unlock(&value->trigger_list_lock);
> +
> + mutex_unlock(&counter->value_list_lock);
> +
> + return len;
> +
> +err_trigger:
> + mutex_unlock(&value->trigger_list_lock);
> +err_value:
> + mutex_unlock(&counter->value_list_lock);
> + return err;
> +}
> +
> +static ssize_t iio_counter_trigger_mode_available_read(
> + struct iio_dev *indio_dev, uintptr_t priv,
> + const struct iio_chan_spec *chan, char *buf)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_value *value;
> + ssize_t len = 0;
> + struct iio_counter_trigger *trigger;
> + const int signal_id = *(int *)((void *)priv);
> + unsigned int i;
> +
> + mutex_lock(&counter->value_list_lock);
> +
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + len = -EINVAL;
> + goto err_no_value;
> + }
> +
> + mutex_lock(&value->trigger_list_lock);
> +
> + trigger = iio_counter_trigger_find_by_id(value, signal_id);
> + if (!trigger) {
> + len = -EINVAL;
> + goto err_no_trigger;
> + }
> +
> + for (i = 0; i < trigger->num_trigger_modes; i++)
> + len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
> + trigger->trigger_modes[i]);
> +
> + mutex_unlock(&value->trigger_list_lock);
> +
> + mutex_unlock(&counter->value_list_lock);
> +
> + buf[len - 1] = '\n';
> +
> + return len;
> +
> +err_no_trigger:
> + mutex_unlock(&value->trigger_list_lock);
> +err_no_value:
> + mutex_unlock(&counter->value_list_lock);
> + return len;
> +}
> +
> +static int iio_counter_value_function_set(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan, unsigned int mode)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_value *value;
> + int err;
> +
> + if (!counter->ops->value_function_set)
> + return -EINVAL;
> +
> + mutex_lock(&counter->value_list_lock);
> +
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + err = -EINVAL;
> + goto err_value;
> + }
> +
> + err = counter->ops->value_function_set(counter, value, mode);
> + if (err)
> + goto err_value;
> +
> + value->mode = mode;
> +
> +err_value:
> + mutex_unlock(&counter->value_list_lock);
> +
> + return err;
> +}
> +
> +static int iio_counter_value_function_get(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_value *value;
> + int retval;
> +
> + if (!counter->ops->value_function_get)
> + return -EINVAL;
> +
> + mutex_lock(&counter->value_list_lock);
> +
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + retval = -EINVAL;
> + goto err_value;
> + }
> +
> + retval = counter->ops->value_function_get(counter, value);
> + if (retval < 0)
> + goto err_value;
> + else if (retval >= value->num_function_modes) {
> + retval = -EINVAL;
> + goto err_value;
> + }
> +
> + value->mode = retval;
> +
> +err_value:
> + mutex_unlock(&counter->value_list_lock);
> +
> + return retval;
> +}
> +
> +static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
> + struct iio_counter_value *const value)
> +{
> + const struct iio_chan_spec_ext_info ext_info_default[] = {
> + {
> + .name = "name",
> + .shared = IIO_SEPARATE,
> + .read = iio_counter_value_name_read
> + },
> + IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
> + {
> + .name = "function_available",
> + .shared = IIO_SEPARATE,
> + .read = iio_enum_available_read,
> + .private = (void *)&value->function_enum
I have this warning the 3 times that private field is set
drivers/iio/industrialio-counter.c:401:15: warning: initialization
makes integer from pointer without a cast [-Wint-conversion]
.private = (void *)&value->function_enum

I think you can change it to "void *" instead of a uintptr_t
> + },
> + {
> + .name = "triggers",
> + .shared = IIO_SEPARATE,
> + .read = iio_counter_value_triggers_read
> + }
> + };
> + const size_t num_default = ARRAY_SIZE(ext_info_default);
> + const struct iio_chan_spec_ext_info ext_info_trigger[] = {
> + {
> + .shared = IIO_SEPARATE,
> + .read = iio_counter_trigger_mode_read,
> + .write = iio_counter_trigger_mode_write
> + },
> + {
> + .shared = IIO_SEPARATE,
> + .read = iio_counter_trigger_mode_available_read
> + }
> + };
> + const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
> + const struct list_head *pos;
> + size_t num_triggers = 0;
> + size_t num_triggers_ext_info;
> + size_t num_ext_info;
> + int err;
> + struct iio_chan_spec_ext_info *ext_info;
> + const struct iio_counter_trigger *trigger_pos;
> + size_t i;
> +
> + value->function_enum.items = value->function_modes;
> + value->function_enum.num_items = value->num_function_modes;
> + value->function_enum.set = iio_counter_value_function_set;
> + value->function_enum.get = iio_counter_value_function_get;
> +
> + mutex_lock(&value->trigger_list_lock);
> +
> + list_for_each(pos, &value->trigger_list)
> + num_triggers++;
> +
> + num_triggers_ext_info = num_ext_info_trigger * num_triggers;
> + num_ext_info = num_default + num_triggers_ext_info + 1;
> +
> + ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
> + if (!ext_info) {
> + err = -ENOMEM;
> + goto err_ext_info_alloc;
> + }
> + ext_info[num_ext_info - 1].name = NULL;
> +
> + memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
> + for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
> + memcpy(ext_info + num_default + i, ext_info_trigger,
> + sizeof(ext_info_trigger));
> +
> + i = num_default;
> + list_for_each_entry(trigger_pos, &value->trigger_list, list) {
> + ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
> + chan->channel, trigger_pos->signal->id);
> + if (!ext_info[i].name) {
> + err = -ENOMEM;
> + goto err_name_alloc;
> + }
> + ext_info[i].private = (void *)&trigger_pos->signal->id;
> + i++;
> +
> + ext_info[i].name = kasprintf(GFP_KERNEL,
> + "trigger_signal%d-%d_available",
> + chan->channel, trigger_pos->signal->id);
> + if (!ext_info[i].name) {
> + err = -ENOMEM;
> + goto err_name_alloc;
> + }
> + ext_info[i].private = (void *)&trigger_pos->signal->id;
> + i++;
> + }
> +
> + chan->ext_info = ext_info;
> +
> + mutex_unlock(&value->trigger_list_lock);
> +
> + return 0;
> +
> +err_name_alloc:
> + while (i-- > num_default)
> + kfree(ext_info[i].name);
> + kfree(ext_info);
> +err_ext_info_alloc:
> + mutex_unlock(&value->trigger_list_lock);
> + return err;
> +}
> +
> +static void iio_counter_value_ext_info_free(
> + const struct iio_chan_spec *const channel)
> +{
> + size_t i;
> + const char *const prefix = "trigger_signal";
> + const size_t prefix_len = strlen(prefix);
> +
> + for (i = 0; channel->ext_info[i].name; i++)
> + if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
> + kfree(channel->ext_info[i].name);
> + kfree(channel->ext_info);
> +}
> +
> +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
> + {
> + .name = "name",
> + .shared = IIO_SEPARATE,
> + .read = iio_counter_signal_name_read
> + },
> + {}
> +};
> +
> +static int iio_counter_channels_alloc(struct iio_counter *const counter)
> +{
> + const struct list_head *pos;
> + size_t num_channels = 0;
> + int err;
> + struct iio_chan_spec *channels;
> + struct iio_counter_value *value_pos;
> + size_t i = counter->num_channels;
> + const struct iio_counter_signal *signal_pos;
> +
> + mutex_lock(&counter->signal_list_lock);
> +
> + list_for_each(pos, &counter->signal_list)
> + num_channels++;
> +
> + if (!num_channels) {
> + err = -EINVAL;
> + goto err_no_signals;
> + }
> +
> + mutex_lock(&counter->value_list_lock);
> +
> + list_for_each(pos, &counter->value_list)
> + num_channels++;
> +
> + num_channels += counter->num_channels;
> +
> + channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
> + if (!channels) {
> + err = -ENOMEM;
> + goto err_channels_alloc;
> + }
> +
> + memcpy(channels, counter->channels,
> + counter->num_channels * sizeof(*counter->channels));
> +
> + list_for_each_entry(value_pos, &counter->value_list, list) {
> + channels[i].type = IIO_COUNT;
> + channels[i].channel = counter->id;
> + channels[i].channel2 = value_pos->id;
> + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> + channels[i].indexed = 1;
> + channels[i].counter = 1;
> +
> + err = iio_counter_value_ext_info_alloc(channels + i, value_pos);
> + if (err)
> + goto err_value_ext_info_alloc;
> +
> + i++;
> + }
> +
> + mutex_unlock(&counter->value_list_lock);
> +
> + list_for_each_entry(signal_pos, &counter->signal_list, list) {
> + channels[i].type = IIO_SIGNAL;
> + channels[i].channel = counter->id;
> + channels[i].channel2 = signal_pos->id;
> + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> + channels[i].indexed = 1;
> + channels[i].counter = 1;
> + channels[i].ext_info = iio_counter_signal_ext_info;
> +
> + i++;
> + }
> +
> + mutex_unlock(&counter->signal_list_lock);
> +
> + counter->indio_dev->num_channels = num_channels;
> + counter->indio_dev->channels = channels;
> +
> + return 0;
> +
> +err_value_ext_info_alloc:
> + while (i-- > counter->num_channels)
> + iio_counter_value_ext_info_free(channels + i);
> + kfree(channels);
> +err_channels_alloc:
> + mutex_unlock(&counter->value_list_lock);
> +err_no_signals:
> + mutex_unlock(&counter->signal_list_lock);
> + return err;
> +}
> +
> +static void iio_counter_channels_free(const struct iio_counter *const counter)
> +{
> + size_t i = counter->num_channels + counter->indio_dev->num_channels;
> + const struct iio_chan_spec *const chans = counter->indio_dev->channels;
> +
> + while (i-- > counter->num_channels)
> + if (chans[i].type == IIO_COUNT)
> + iio_counter_value_ext_info_free(chans + i);
> +
> + kfree(chans);
> +}
> +
> +static int iio_counter_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_signal *signal;
> + int retval;
> + struct iio_counter_value *value;
> +
> + if (mask != IIO_CHAN_INFO_RAW)
> + return -EINVAL;
> +
> + switch (chan->type) {
> + case IIO_SIGNAL:
> + if (!counter->ops->signal_read)
> + return -EINVAL;
> +
> + mutex_lock(&counter->signal_list_lock);
> + signal = iio_counter_signal_find_by_id(counter, chan->channel2);
> + if (!signal) {
> + mutex_unlock(&counter->signal_list_lock);
> + return -EINVAL;
> + }
> +
> + retval = counter->ops->signal_read(counter, signal, val, val2);
> + mutex_unlock(&counter->signal_list_lock);
> +
> + return retval;
> + case IIO_COUNT:
> + if (!counter->ops->value_read)
> + return -EINVAL;
> +
> + mutex_lock(&counter->value_list_lock);
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + mutex_unlock(&counter->value_list_lock);
> + return -EINVAL;
> + }
> +
> + retval = counter->ops->value_read(counter, value, val, val2);
> + mutex_unlock(&counter->value_list_lock);
> +
> + return retval;
> + default:
> + if (counter->info && counter->info->read_raw)
> + return counter->info->read_raw(indio_dev, chan, val,
> + val2, mask);
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int iio_counter_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2, long mask)
> +{
> + struct iio_counter *const counter = iio_priv(indio_dev);
> + struct iio_counter_signal *signal;
> + int retval;
> + struct iio_counter_value *value;
> +
> + if (mask != IIO_CHAN_INFO_RAW)
> + return -EINVAL;
> +
> + switch (chan->type) {
> + case IIO_SIGNAL:
> + if (!counter->ops->signal_write)
> + return -EINVAL;
> +
> + mutex_lock(&counter->signal_list_lock);
> + signal = iio_counter_signal_find_by_id(counter, chan->channel2);
> + if (!signal) {
> + mutex_unlock(&counter->signal_list_lock);
> + return -EINVAL;
> + }
> +
> + retval = counter->ops->signal_write(counter, signal, val, val2);
> + mutex_unlock(&counter->signal_list_lock);
> +
> + return retval;
> + case IIO_COUNT:
> + if (!counter->ops->value_write)
> + return -EINVAL;
> +
> + mutex_lock(&counter->value_list_lock);
> + value = iio_counter_value_find_by_id(counter, chan->channel2);
> + if (!value) {
> + mutex_unlock(&counter->value_list_lock);
> + return -EINVAL;
> + }
> +
> + retval = counter->ops->value_write(counter, value, val, val2);
> + mutex_unlock(&counter->value_list_lock);
> +
> + return retval;
> + default:
> + if (counter->info && counter->info->write_raw)
> + return counter->info->write_raw(indio_dev, chan, val,
> + val2, mask);
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int iio_counter_signal_register(struct iio_counter *const counter,
> + struct iio_counter_signal *const signal)
> +{
> + int err;
> +
> + if (!counter || !signal)
> + return -EINVAL;
> +
> + mutex_lock(&counter->signal_list_lock);
> + if (iio_counter_signal_find_by_id(counter, signal->id)) {
> + pr_err("Duplicate counter signal ID '%d'\n", signal->id);
> + err = -EEXIST;
> + goto err_duplicate_id;
> + }
> + list_add_tail(&signal->list, &counter->signal_list);
> + mutex_unlock(&counter->signal_list_lock);
> +
> + return 0;
> +
> +err_duplicate_id:
> + mutex_unlock(&counter->signal_list_lock);
> + return err;
> +}
> +
> +static void iio_counter_signal_unregister(struct iio_counter *const counter,
> + struct iio_counter_signal *const signal)
> +{
> + if (!counter || !signal)
> + return;
> +
> + mutex_lock(&counter->signal_list_lock);
> + list_del(&signal->list);
> + mutex_unlock(&counter->signal_list_lock);
> +}
> +
> +static int iio_counter_signals_register(struct iio_counter *const counter,
> + struct iio_counter_signal *const signals, const size_t num_signals)
> +{
> + size_t i;
> + int err;
> +
> + if (!counter || !signals)
> + return -EINVAL;
> +
> + for (i = 0; i < num_signals; i++) {
> + err = iio_counter_signal_register(counter, signals + i);
> + if (err)
> + goto err_signal_register;
> + }
> +
> + return 0;
> +
> +err_signal_register:
> + while (i--)
> + iio_counter_signal_unregister(counter, signals + i);
> + return err;
> +}
> +
> +static void iio_counter_signals_unregister(struct iio_counter *const counter,
> + struct iio_counter_signal *signals, size_t num_signals)
> +{
> + if (!counter || !signals)
> + return;
> +
> + while (num_signals--) {
> + iio_counter_signal_unregister(counter, signals);
> + signals++;
> + }
> +}
> +
> +/**
> + * iio_counter_trigger_register - register Trigger to Value
> + * @value: pointer to IIO Counter Value for association
> + * @trigger: pointer to IIO Counter Trigger to register
> + *
> + * The Trigger is added to the Value's trigger_list. A check is first performed
> + * to verify that the respective Signal is not already linked to the Value; if
> + * the respective Signal is already linked to the Value, the Trigger is not
> + * added to the Value's trigger_list.
> + *
> + * NOTE: This function will acquire and release the Value's trigger_list_lock
> + * during execution.
> + */
> +int iio_counter_trigger_register(struct iio_counter_value *const value,
> + struct iio_counter_trigger *const trigger)
> +{
> + if (!value || !trigger || !trigger->signal)
> + return -EINVAL;
> +
> + mutex_lock(&value->trigger_list_lock);
> + if (iio_counter_trigger_find_by_id(value, trigger->signal->id)) {
> + pr_err("Signal%d is already linked to counter value%d\n",
> + trigger->signal->id, value->id);
> + return -EEXIST;
> + }
> + list_add_tail(&trigger->list, &value->trigger_list);
> + mutex_unlock(&value->trigger_list_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(iio_counter_trigger_register);
> +
> +/**
> + * iio_counter_trigger_unregister - unregister Trigger from Value
> + * @value: pointer to IIO Counter Value of association
> + * @trigger: pointer to IIO Counter Trigger to unregister
> + *
> + * The Trigger is removed from the Value's trigger_list.
> + *
> + * NOTE: This function will acquire and release the Value's trigger_list_lock
> + * during execution.
> + */
> +void iio_counter_trigger_unregister(struct iio_counter_value *const value,
> + struct iio_counter_trigger *const trigger)
> +{
> + if (!value || !trigger || !trigger->signal)
> + return;
> +
> + mutex_lock(&value->trigger_list_lock);
> + list_del(&trigger->list);
> + mutex_unlock(&value->trigger_list_lock);
> +}
> +EXPORT_SYMBOL(iio_counter_trigger_unregister);
> +
> +/**
> + * iio_counter_triggers_register - register an array of Triggers to Value
> + * @value: pointer to IIO Counter Value for association
> + * @triggers: array of pointers to IIO Counter Triggers to register
> + *
> + * The iio_counter_trigger_register function is called for each Trigger in the
> + * array. The @triggers array is traversed for the first @num_triggers Triggers.
> + *
> + * NOTE: @num_triggers must not be greater than the size of the @triggers array.
> + */
> +int iio_counter_triggers_register(struct iio_counter_value *const value,
> + struct iio_counter_trigger *const triggers, const size_t num_triggers)
> +{
> + size_t i;
> + int err;
> +
> + if (!value || !triggers)
> + return -EINVAL;
> +
> + for (i = 0; i < num_triggers; i++) {
> + err = iio_counter_trigger_register(value, triggers + i);
> + if (err)
> + goto err_trigger_register;
> + }
> +
> + return 0;
> +
> +err_trigger_register:
> + while (i--)
> + iio_counter_trigger_unregister(value, triggers + i);
> + return err;
> +}
> +EXPORT_SYMBOL(iio_counter_triggers_register);
> +
> +/**
> + * iio_counter_triggers_unregister - unregister Triggers from Value
> + * @value: pointer to IIO Counter Value of association
> + * @triggers: array of pointers to IIO Counter Triggers to unregister
> + *
> + * The iio_counter_trigger_unregister function is called for each Trigger in the
> + * array. The @triggers array is traversed for the first @num_triggers Triggers.
> + *
> + * NOTE: @num_triggers must not be greater than the size of the @triggers array.
> + */
> +void iio_counter_triggers_unregister(struct iio_counter_value *const value,
> + struct iio_counter_trigger *triggers, size_t num_triggers)
> +{
> + if (!value || !triggers)
> + return;
> +
> + while (num_triggers--) {
> + iio_counter_trigger_unregister(value, triggers);
> + triggers++;
> + }
> +}
> +EXPORT_SYMBOL(iio_counter_triggers_unregister);
> +
> +/**
> + * iio_counter_value_register - register Value to Counter
> + * @counter: pointer to IIO Counter for association
> + * @value: pointer to IIO Counter Value to register
> + *
> + * The registration process occurs in two major steps. First, the Value is
> + * initialized: trigger_list_lock is initialized, trigger_list is initialized,
> + * and init_triggers if not NULL is passed to iio_counter_triggers_register.
> + * Second, the Value is added to the Counter's value_list. A check is first
> + * performed to verify that the Value is not already associated to the Counter
> + * (via the Value's unique ID); if the Value is already associated to the
> + * Counter, the Value is not added to the Counter's value_list and all of the
> + * Value's Triggers are unregistered.
> + *
> + * NOTE: This function will acquire and release the Counter's value_list_lock
> + * during execution.
> + */
> +int iio_counter_value_register(struct iio_counter *const counter,
> + struct iio_counter_value *const value)
> +{
> + int err;
> +
> + if (!counter || !value)
> + return -EINVAL;
> +
> + mutex_init(&value->trigger_list_lock);
> + INIT_LIST_HEAD(&value->trigger_list);
> +
> + if (value->init_triggers) {
> + err = iio_counter_triggers_register(value,
> + value->init_triggers, value->num_init_triggers);
> + if (err)
> + return err;
> + }
> +
> + mutex_lock(&counter->value_list_lock);
> + if (iio_counter_value_find_by_id(counter, value->id)) {
> + pr_err("Duplicate counter value ID '%d'\n", value->id);
> + err = -EEXIST;
> + goto err_duplicate_id;
> + }
> + list_add_tail(&value->list, &counter->value_list);
> + mutex_unlock(&counter->value_list_lock);
> +
> + return 0;
> +
> +err_duplicate_id:
> + mutex_unlock(&counter->value_list_lock);
> + iio_counter_trigger_unregister_all(value);
> + return err;
> +}
> +EXPORT_SYMBOL(iio_counter_value_register);
> +
> +/**
> + * iio_counter_value_unregister - unregister Value from Counter
> + * @counter: pointer to IIO Counter of association
> + * @value: pointer to IIO Counter Value to unregister
> + *
> + * The Value is removed from the Counter's value_list and all of the Value's
> + * Triggers are unregistered.
> + *
> + * NOTE: This function will acquire and release the Counter's value_list_lock
> + * during execution.
> + */
> +void iio_counter_value_unregister(struct iio_counter *const counter,
> + struct iio_counter_value *const value)
> +{
> + if (!counter || !value)
> + return;
> +
> + mutex_lock(&counter->value_list_lock);
> + list_del(&value->list);
> + mutex_unlock(&counter->value_list_lock);
> +
> + iio_counter_trigger_unregister_all(value);
> +}
> +EXPORT_SYMBOL(iio_counter_value_unregister);
> +
> +/**
> + * iio_counter_values_register - register an array of Values to Counter
> + * @counter: pointer to IIO Counter for association
> + * @values: array of pointers to IIO Counter Values to register
> + *
> + * The iio_counter_value_register function is called for each Value in the
> + * array. The @values array is traversed for the first @num_values Values.
> + *
> + * NOTE: @num_values must not be greater than the size of the @values array.
> + */
> +int iio_counter_values_register(struct iio_counter *const counter,
> + struct iio_counter_value *const values, const size_t num_values)
> +{
> + size_t i;
> + int err;
> +
> + if (!counter || !values)
> + return -EINVAL;
> +
> + for (i = 0; i < num_values; i++) {
> + err = iio_counter_value_register(counter, values + i);
> + if (err)
> + goto err_values_register;
> + }
> +
> + return 0;
> +
> +err_values_register:
> + while (i--)
> + iio_counter_value_unregister(counter, values + i);
> + return err;
> +}
> +EXPORT_SYMBOL(iio_counter_values_register);
> +
> +/**
> + * iio_counter_values_unregister - unregister Values from Counter
> + * @counter: pointer to IIO Counter of association
> + * @values: array of pointers to IIO Counter Values to unregister
> + *
> + * The iio_counter_value_unregister function is called for each Value in the
> + * array. The @values array is traversed for the first @num_values Values.
> + *
> + * NOTE: @num_values must not be greater than the size of the @values array.
> + */
> +void iio_counter_values_unregister(struct iio_counter *const counter,
> + struct iio_counter_value *values, size_t num_values)
> +{
> + if (!counter || !values)
> + return;
> +
> + while (num_values--) {
> + iio_counter_value_unregister(counter, values);
> + values++;
> + }
> +}
> +EXPORT_SYMBOL(iio_counter_values_unregister);
> +
> +/**
> + * iio_counter_register - register Counter to the system
> + * @counter: pointer to IIO Counter to register
> + *
> + * This function piggybacks off of iio_device_register. First, the relevant
> + * Counter members are initialized; if init_signals is not NULL it is passed to
> + * iio_counter_signals_register, and similarly if init_values is not NULL it is
> + * passed to iio_counter_values_register. Next, a struct iio_dev is allocated by
> + * a call to iio_device_alloc and initialized for the Counter, IIO channels are
> + * allocated, the Counter is copied as the private data, and finally
> + * iio_device_register is called.
> + */
> +int iio_counter_register(struct iio_counter *const counter)
> +{
> + const struct iio_info info_default = {
> + .read_raw = iio_counter_read_raw,
> + .write_raw = iio_counter_write_raw
> + };
> + int err;
> + struct iio_info *info;
> + struct iio_counter *priv;
> +
> + if (!counter)
> + return -EINVAL;
> +
> + mutex_init(&counter->signal_list_lock);
> + INIT_LIST_HEAD(&counter->signal_list);
> +
> + if (counter->init_signals) {
> + err = iio_counter_signals_register(counter,
> + counter->init_signals, counter->num_init_signals);
> + if (err)
> + return err;
> + }
> +
> + mutex_init(&counter->value_list_lock);
> + INIT_LIST_HEAD(&counter->value_list);
> +
> + if (counter->init_values) {
> + err = iio_counter_values_register(counter,
> + counter->init_values, counter->num_init_values);
> + if (err)
> + goto err_values_register;
> + }
> +
> + counter->indio_dev = iio_device_alloc(sizeof(*counter));
> + if (!counter->indio_dev) {
> + err = -ENOMEM;
> + goto err_iio_device_alloc;
> + }
> +
> + info = kmalloc(sizeof(*info), GFP_KERNEL);
> + if (!info) {
> + err = -ENOMEM;
> + goto err_info_alloc;
> + }
> + if (counter->info) {
> + memcpy(info, counter->info, sizeof(*counter->info));
> + info->read_raw = iio_counter_read_raw;
> + info->write_raw = iio_counter_write_raw;
> + } else {
> + memcpy(info, &info_default, sizeof(info_default));
> + }
> +
> + counter->indio_dev->info = info;
> + counter->indio_dev->modes = INDIO_DIRECT_MODE;
> + counter->indio_dev->name = counter->name;
> + counter->indio_dev->dev.parent = counter->dev;
> +
> + err = iio_counter_channels_alloc(counter);
> + if (err)
> + goto err_channels_alloc;
> +
> + priv = iio_priv(counter->indio_dev);
> + memcpy(priv, counter, sizeof(*priv));
> +
> + err = iio_device_register(priv->indio_dev);
> + if (err)
> + goto err_iio_device_register;
> +
> + return 0;
> +
> +err_iio_device_register:
> + iio_counter_channels_free(counter);
> +err_channels_alloc:
> + kfree(info);
> +err_info_alloc:
> + iio_device_free(counter->indio_dev);
> +err_iio_device_alloc:
> + iio_counter_values_unregister(counter, counter->init_values,
> + counter->num_init_values);
> +err_values_register:
> + iio_counter_signals_unregister(counter, counter->init_signals,
> + counter->num_init_signals);
> + return err;
> +}
> +EXPORT_SYMBOL(iio_counter_register);
> +
> +/**
> + * iio_counter_unregister - unregister Counter from the system
> + * @counter: pointer to IIO Counter to unregister
> + *
> + * The Counter is unregistered from the system. The indio_dev is unregistered,
> + * allocated memory is freed, and all associated Values and Signals are
> + * unregistered.
> + */
> +void iio_counter_unregister(struct iio_counter *const counter)
> +{
> + const struct iio_info *const info = counter->indio_dev->info;
> +
> + if (!counter)
> + return;
> +
> + iio_device_unregister(counter->indio_dev);
> +
> + iio_counter_channels_free(counter);
> +
> + kfree(info);
> + iio_device_free(counter->indio_dev);
> +
> + iio_counter_value_unregister_all(counter);
> + iio_counter_signal_unregister_all(counter);
> +}
> +EXPORT_SYMBOL(iio_counter_unregister);
> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
> new file mode 100644
> index 000000000000..1c1ca13a6053
> --- /dev/null
> +++ b/include/linux/iio/counter.h
> @@ -0,0 +1,221 @@
> +/*
> + * Industrial I/O counter interface
> + * Copyright (C) 2017 William Breathitt Gray
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +#ifndef _IIO_COUNTER_H_
> +#define _IIO_COUNTER_H_
> +
> +#ifdef CONFIG_IIO_COUNTER
> +
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +
> +#include <linux/iio/iio.h>
> +
> +/**
> + * struct iio_counter_signal - IIO Counter Signal node
> + * @id: [DRIVER] unique ID used to identify signal
> + * @name: [DRIVER] device-specific signal name
> + * @list: [INTERN] list of all signals currently registered to counter
> + */
> +struct iio_counter_signal {
> + int id;
> + const char *name;
> +
> + struct list_head list;
> +};
> +
> +/**
> + * struct iio_counter_trigger - IIO Counter Trigger node
> + * @mode: [DRIVER] current trigger mode state
> + * @trigger_modes: [DRIVER] available trigger modes
> + * @num_trigger_modes: [DRIVER] number of modes specified in @trigger_modes
> + * @signal: [DRIVER] pointer to associated signal
> + * @list: [INTERN] list of all triggers currently registered to
> + * value
> + */
> +struct iio_counter_trigger {
> + unsigned int mode;
> + const char *const *trigger_modes;
> + unsigned int num_trigger_modes;
> + struct iio_counter_signal *signal;
> +
> + struct list_head list;
> +};
> +
> +/**
> + * struct iio_counter_value - IIO Counter Value node
> + * @id: [DRIVER] unique ID used to identify value
> + * @name: [DRIVER] device-specific value name
> + * @mode: [DRIVER] current function mode state
> + * @function_modes: [DRIVER] available function modes
> + * @num_function_modes: [DRIVER] number of modes specified in @function_modes
> + * @init_triggers: [DRIVER] array of triggers for initialization
> + * @num_init_triggers: [DRIVER] number of triggers specified in @init_triggers
> + * @function_enum: [INTERN] used internally to generate function attributes
> + * @trigger_list_lock: [INTERN] lock for accessing @trigger_list
> + * @trigger_list: [INTERN] list of all triggers currently registered to
> + * value
> + * @list: [INTERN] list of all values currently registered to
> + * counter
> + */
> +struct iio_counter_value {
> + int id;
> + const char *name;
> + unsigned int mode;
> + const char *const *function_modes;
> + unsigned int num_function_modes;
> +
> + struct iio_counter_trigger *init_triggers;
> + size_t num_init_triggers;
> +
> + struct iio_enum function_enum;
> + struct mutex trigger_list_lock;
> + struct list_head trigger_list;
> +
> + struct list_head list;
> +};
> +
> +struct iio_counter;
> +
> +/**
> + * struct iio_counter_ops - IIO Counter related callbacks
> + * @signal_read: function to request a signal value from the device.
> + * Return value will specify the type of value returned by
> + * the device. val and val2 will contain the elements
> + * making up the returned value. Note that the counter
> + * signal_list_lock is acquired before this function is
> + * called, and released after this function returns.
> + * @signal_write: function to write a signal value to the device.
> + * Parameters and locking behavior are the same as
> + * signal_read.
> + * @trigger_mode_set: function to set the trigger mode. mode is the index of
> + * the requested mode from the value trigger_modes array.
> + * Note that the counter value_list_lock and value
> + * trigger_list_lock are acquired before this function is
> + * called, and released after this function returns.
> + * @trigger_mode_get: function to get the current trigger mode. Return value
> + * will specify the index of the current mode from the
> + * value trigger_modes array. Locking behavior is the same
> + * as trigger_mode_set.
> + * @value_read: function to request a value value from the device.
> + * Return value will specify the type of value returned by
> + * the device. val and val2 will contain the elements
> + * making up the returned value. Note that the counter
> + * value_list_lock is acquired before this function is
> + * called, and released after this function returns.
> + * @value_write: function to write a value value to the device.
> + * Parameters and locking behavior are the same as
> + * value_read.
> + * @value_function_set: function to set the value function mode. mode is the
> + * index of the requested mode from the value
> + * function_modes array. Note that the counter
> + * value_list_lock is acquired before this function is
> + * called, and released after this function returns.
> + * @value_function_get: function to get the current value function mode. Return
> + * value will specify the index of the current mode from
> + * the value function_modes array. Locking behavior is the
> + * same as value_function_get.
> + */
> +struct iio_counter_ops {
> + int (*signal_read)(struct iio_counter *counter,
> + struct iio_counter_signal *signal, int *val, int *val2);
> + int (*signal_write)(struct iio_counter *counter,
> + struct iio_counter_signal *signal, int val, int val2);
> + int (*trigger_mode_set)(struct iio_counter *counter,
> + struct iio_counter_value *value,
> + struct iio_counter_trigger *trigger, unsigned int mode);
> + int (*trigger_mode_get)(struct iio_counter *counter,
> + struct iio_counter_value *value,
> + struct iio_counter_trigger *trigger);
> + int (*value_read)(struct iio_counter *counter,
> + struct iio_counter_value *value, int *val, int *val2);
> + int (*value_write)(struct iio_counter *counter,
> + struct iio_counter_value *value, int val, int val2);
> + int (*value_function_set)(struct iio_counter *counter,
> + struct iio_counter_value *value, unsigned int mode);
> + int (*value_function_get)(struct iio_counter *counter,
> + struct iio_counter_value *value);
> +};
> +
> +/**
> + * struct iio_counter - IIO Counter data structure
> + * @id: [DRIVER] unique ID used to identify counter
> + * @name: [DRIVER] name of the device
> + * @dev: [DRIVER] device structure, should be assigned a parent
> + * and owner
> + * @ops: [DRIVER] callbacks from driver for counter components
> + * @init_signals: [DRIVER] array of signals for initialization
> + * @num_init_signals: [DRIVER] number of signals specified in @init_signals
> + * @init_values: [DRIVER] array of values for initialization
> + * @num_init_values: [DRIVER] number of values specified in @init_values
> + * @signal_list_lock: [INTERN] lock for accessing @signal_list
> + * @signal_list: [INTERN] list of all signals currently registered to
> + * counter
> + * @value_list_lock: [INTERN] lock for accessing @value_list
> + * @value_list: [INTERN] list of all values currently registered to
> + * counter
> + * @channels: [DRIVER] channel specification structure table
> + * @num_channels: [DRIVER] number of channels specified in @channels
> + * @info: [DRIVER] callbacks and constant info from driver
> + * @indio_dev: [INTERN] industrial I/O device structure
> + * @driver_data: [DRIVER] driver data
> + */
> +struct iio_counter {
> + int id;
> + const char *name;
> + struct device *dev;
> + const struct iio_counter_ops *ops;
> +
> + struct iio_counter_signal *init_signals;
> + size_t num_init_signals;
> + struct iio_counter_value *init_values;
> + size_t num_init_values;
> +
> + struct mutex signal_list_lock;
> + struct list_head signal_list;
> + struct mutex value_list_lock;
> + struct list_head value_list;
> +
> + const struct iio_chan_spec *channels;
> + size_t num_channels;
> + const struct iio_info *info;
> +
> + struct iio_dev *indio_dev;
> + void *driver_data;
> +};
> +
> +int iio_counter_trigger_register(struct iio_counter_value *const value,
> + struct iio_counter_trigger *const trigger);
> +void iio_counter_trigger_unregister(struct iio_counter_value *const value,
> + struct iio_counter_trigger *const trigger);
> +int iio_counter_triggers_register(struct iio_counter_value *const value,
> + struct iio_counter_trigger *const triggers, const size_t num_triggers);
> +void iio_counter_triggers_unregister(struct iio_counter_value *const value,
> + struct iio_counter_trigger *triggers, size_t num_triggers);
> +
> +int iio_counter_value_register(struct iio_counter *const counter,
> + struct iio_counter_value *const value);
> +void iio_counter_value_unregister(struct iio_counter *const counter,
> + struct iio_counter_value *const value);
> +int iio_counter_values_register(struct iio_counter *const counter,
> + struct iio_counter_value *const values, const size_t num_values);
> +void iio_counter_values_unregister(struct iio_counter *const counter,
> + struct iio_counter_value *values, size_t num_values);
> +
> +int iio_counter_register(struct iio_counter *const counter);
> +void iio_counter_unregister(struct iio_counter *const counter);
> +
> +#endif /* CONFIG_IIO_COUNTER */
> +
> +#endif /* _IIO_COUNTER_H_ */
> --
> 2.14.1
>



--
Benjamin Gaignard

Graphic Study Group

Linaro.org │ Open source software for ARM SoCs

Follow Linaro: Facebook | Twitter | Blog

2017-09-29 20:01:19

by William Breathitt Gray

[permalink] [raw]
Subject: Re: [PATCH v2 2/5] iio: Introduce the generic counter interface

On Fri, Sep 29, 2017 at 03:42:02PM +0200, Benjamin Gaignard wrote:
>2017-09-25 20:08 GMT+02:00 William Breathitt Gray <[email protected]>:
>> This patch introduces the IIO generic counter interface for supporting
>> counter devices. The generic counter interface serves as a catch-all to
>> enable rudimentary support for devices that qualify as counters. More
>> specific and apt counter interfaces may be developed on top of the
>> generic counter interface, and those interfaces should be used by
>> drivers when possible rather than the generic counter interface.
>>
>> In the context of the IIO generic counter interface, a counter is
>> defined as a device that reports one or more "counter values" based on
>> the state changes of one or more "counter signals" as evaluated by a
>> defined "counter function."
>>
>> The IIO generic counter interface piggybacks off of the IIO core. This
>> is primarily used to leverage the existing sysfs setup: the IIO_COUNT
>> channel attributes represent the "counter value," while the IIO_SIGNAL
>> channel attributes represent the "counter signal;" auxilary IIO_COUNT
>> attributes represent the "counter signal" connections and their
>> respective state change configurations which trigger an associated
>> "counter function" evaluation.
>>
>> The iio_counter_ops structure serves as a container for driver callbacks
>> to communicate with the device; function callbacks are provided to read
>> and write various "counter signals" and "counter values," and set and
>> get the "trigger mode" and "function mode" for various "counter signals"
>> and "counter values" respectively.
>>
>> To support a counter device, a driver must first allocate the available
>> "counter signals" via iio_counter_signal structures. These "counter
>> signals" should be stored as an array and set to the init_signals member
>> of an allocated iio_counter structure before the counter is registered.
>>
>> "Counter values" may be allocated via iio_counter_value structures, and
>> respective "counter signal" associations made via iio_counter_trigger
>> structures. Initial associated iio_counter_trigger structures may be
>> stored as an array and set to the the init_triggers member of the
>> respective iio_counter_value structure. These iio_counter_value
>> structures may be set to the init_values member of an allocated
>> iio_counter structure before the counter is registered if so desired.
>>
>> A counter device is registered to the system by passing the respective
>> initialized iio_counter structure to the iio_counter_register function;
>> similarly, the iio_counter_unregister function unregisters the
>> respective counter.
>>
>> Signed-off-by: William Breathitt Gray <[email protected]>
>
>Hi William,
>
>Thanks for your patches, I start try to implement them in stm32-timer driver
>but I think it will take me some time before understand all your code.
>
>I have some warning when compiling the code (see below).

Hi Benjamin,

Thank you for trying out the patches. I've been focusing on the main API
documentation thus far, so unfortunately the current code is still a bit
hard to follow -- hopefully I can clean it up some soon. Regardless,
I'll be happy to answer any questions you may encounter while reviewing
the patches.

Regarding the compilation warnings: see my response inline below.

>
>About wording, I think that using "trigger" to describe signal active
>states/edges
>could be confusing in IIO context but I haven't found yet a better name.

I agree very much with this. "Trigger" was a bad name choice when I
developed the Generic Counter paradigm. I would like to come up with a
better name for this component before this patchset is merged.

The "Trigger" component essentially describes an association between a
Signal and Value. The association typically has a defined Signal data
condition which triggers the respective Value function operation (or
occasionally the trigger condition is just left as "None"), but
ultimately it's the association itself that is the key aspect this
component.

I have considered changing it to "Association" or "Connection" as the
name, but both of these seem to sound too vague of terms. I shall keep
thinking of various alternative names for this component and change the
documentation in a subsequent patchset version once we have a more
fitting name.

>
>I also not very sure about what you expect from iio_counter_ops signal_read and
>signal_write functions, do you think get/set the value of the signal ?
>(i.e read gpio level ?)

Yes, for an ordinary simple counter, signal_read and signal_write would
typically expose control of the respective hardware input line level.

However, it is important to remember that within the context of the
Generic Counter paradigm, Signals are abstract components: a single
Signal may not directly correlate to a single hardware input line; this
is the primary reason for providing the signal_read/signal_write
functions.

For example, suppose we have a counter with a single Value associated to
a single Signal. The Signal data is a 3-bit decimal value, and the Value
is an ongoing count of the number of times the Signal data decimal value
has had a value greater than "5."

The hardware itself could consist of three individual input lines
(one for each bit), but there is ultimately only one Signal. In this
scenario, I would expect signal_read to simply return the 3-bit decimal
value -- not the three physical hardware input line level. Notice the
abstraction which has occurred: the Signal is the data represented by
the hardware input, but not the raw hardware input itself.

Sincerely,

William Breathitt Gray

>
>I will continue to review and implement your patches, I hope that end
>of next week
>have something functionnal to share with you.
>
>Thansk to have propose this, I do believe it will be helpful
>
>Benjamin
>> ---
>> MAINTAINERS | 7 +
>> drivers/iio/Kconfig | 8 +
>> drivers/iio/Makefile | 1 +
>> drivers/iio/counter/Kconfig | 1 +
>> drivers/iio/industrialio-counter.c | 1151 ++++++++++++++++++++++++++++++++++++
>> include/linux/iio/counter.h | 221 +++++++
>> 6 files changed, 1389 insertions(+)
>> create mode 100644 drivers/iio/industrialio-counter.c
>> create mode 100644 include/linux/iio/counter.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 6f7721d1634c..24fc2dcf1995 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6577,6 +6577,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>> F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
>> F: drivers/iio/adc/envelope-detector.c
>>
>> +IIO GENERIC COUNTER INTERFACE
>> +M: William Breathitt Gray <[email protected]>
>> +L: [email protected]
>> +S: Maintained
>> +F: drivers/iio/industrialio-counter.c
>> +F: include/linux/iio/counter.h
>> +
>> IIO MULTIPLEXER
>> M: Peter Rosin <[email protected]>
>> L: [email protected]
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index b3c8c6ef0dff..78e01f4f5937 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -30,6 +30,14 @@ config IIO_CONFIGFS
>> (e.g. software triggers). For more info see
>> Documentation/iio/iio_configfs.txt.
>>
>> +config IIO_COUNTER
>> + bool "Enable IIO counter support"
>> + help
>> + Provides IIO core support for counters. This API provides
>> + a generic interface that serves as the building blocks to
>> + create more complex counter interfaces. Rudimentary support
>> + for counters is enabled.
>> +
>> config IIO_TRIGGER
>> bool "Enable triggered sampling support"
>> help
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 93c769cd99bf..6427ff38f964 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -5,6 +5,7 @@
>> obj-$(CONFIG_IIO) += industrialio.o
>> industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>> industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>> +industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
>> industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>>
>> obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index b37e5fc03149..3d46a790d8db 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -4,6 +4,7 @@
>> # When adding new entries keep the list in alphabetical order
>>
>> menu "Counters"
>> + depends on IIO_COUNTER
>>
>> config 104_QUAD_8
>> tristate "ACCES 104-QUAD-8 driver"
>> diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
>> new file mode 100644
>> index 000000000000..bdf190d010e4
>> --- /dev/null
>> +++ b/drivers/iio/industrialio-counter.c
>> @@ -0,0 +1,1151 @@
>> +/*
>> + * Industrial I/O counter interface functions
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + * General Public License for more details.
>> + */
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/gfp.h>
>> +#include <linux/kernel.h>
>> +#include <linux/list.h>
>> +#include <linux/mutex.h>
>> +#include <linux/printk.h>
>> +#include <linux/slab.h>
>> +#include <linux/string.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/types.h>
>> +
>> +#include <linux/iio/counter.h>
>> +
>> +static struct iio_counter_signal *iio_counter_signal_find_by_id(
>> + const struct iio_counter *const counter, const int id)
>> +{
>> + struct iio_counter_signal *iter;
>> +
>> + list_for_each_entry(iter, &counter->signal_list, list)
>> + if (iter->id == id)
>> + return iter;
>> +
>> + return NULL;
>> +}
>> +
>> +static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
>> + const struct iio_counter_value *const value, const int id)
>> +{
>> + struct iio_counter_trigger *iter;
>> +
>> + list_for_each_entry(iter, &value->trigger_list, list)
>> + if (iter->signal->id == id)
>> + return iter;
>> +
>> + return NULL;
>> +}
>> +
>> +static struct iio_counter_value *iio_counter_value_find_by_id(
>> + const struct iio_counter *const counter, const int id)
>> +{
>> + struct iio_counter_value *iter;
>> +
>> + list_for_each_entry(iter, &counter->value_list, list)
>> + if (iter->id == id)
>> + return iter;
>> +
>> + return NULL;
>> +}
>> +
>> +static void iio_counter_trigger_unregister_all(
>> + struct iio_counter_value *const value)
>> +{
>> + struct iio_counter_trigger *iter, *tmp_iter;
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> + list_for_each_entry_safe(iter, tmp_iter, &value->trigger_list, list)
>> + list_del(&iter->list);
>> + mutex_unlock(&value->trigger_list_lock);
>> +}
>> +
>> +static void iio_counter_signal_unregister_all(struct iio_counter *const counter)
>> +{
>> + struct iio_counter_signal *iter, *tmp_iter;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> + list_for_each_entry_safe(iter, tmp_iter, &counter->signal_list, list)
>> + list_del(&iter->list);
>> + mutex_unlock(&counter->signal_list_lock);
>> +}
>> +
>> +static void iio_counter_value_unregister_all(struct iio_counter *const counter)
>> +{
>> + struct iio_counter_value *iter, *tmp_iter;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + list_for_each_entry_safe(iter, tmp_iter, &counter->value_list, list) {
>> + iio_counter_trigger_unregister_all(iter);
>> +
>> + list_del(&iter->list);
>> + }
>> + mutex_unlock(&counter->value_list_lock);
>> +}
>> +
>> +static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
>> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + const struct iio_counter_signal *signal;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> + signal = iio_counter_signal_find_by_id(counter, chan->channel2);
>> + mutex_unlock(&counter->signal_list_lock);
>> + if (!signal)
>> + return -EINVAL;
>> +
>> + return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
>> +}
>> +
>> +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
>> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + const struct iio_counter_value *value;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + mutex_unlock(&counter->value_list_lock);
>> + if (!value)
>> + return -EINVAL;
>> +
>> + return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
>> +}
>> +
>> +static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
>> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_value *value;
>> + const struct iio_counter_trigger *trigger;
>> + ssize_t len = 0;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + len = -EINVAL;
>> + goto err_find_value;
>> + }
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> + list_for_each_entry(trigger, &value->trigger_list, list) {
>> + len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
>> + trigger->signal->id, trigger->signal->name,
>> + trigger->trigger_modes[trigger->mode]);
>> + if (len >= PAGE_SIZE) {
>> + len = -ENOMEM;
>> + goto err_no_buffer_space;
>> + }
>> + }
>> +err_no_buffer_space:
>> + mutex_unlock(&value->trigger_list_lock);
>> +
>> +err_find_value:
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return len;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
>> + uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_value *value;
>> + ssize_t ret;
>> + struct iio_counter_trigger *trigger;
>> + const int signal_id = *(int *)((void *)priv);
>> + int mode;
>> +
>> + if (!counter->ops->trigger_mode_get)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> +
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + ret = -EINVAL;
>> + goto err_value;
>> + }
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> +
>> + trigger = iio_counter_trigger_find_by_id(value, signal_id);
>> + if (!trigger) {
>> + ret = -EINVAL;
>> + goto err_trigger;
>> + }
>> +
>> + mode = counter->ops->trigger_mode_get(counter, value, trigger);
>> +
>> + if (mode < 0) {
>> + ret = mode;
>> + goto err_trigger;
>> + } else if (mode >= trigger->num_trigger_modes) {
>> + ret = -EINVAL;
>> + goto err_trigger;
>> + }
>> +
>> + trigger->mode = mode;
>> +
>> + ret = scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
>> +
>> + mutex_unlock(&value->trigger_list_lock);
>> +
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return ret;
>> +
>> +err_trigger:
>> + mutex_unlock(&value->trigger_list_lock);
>> +err_value:
>> + mutex_unlock(&counter->value_list_lock);
>> + return ret;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
>> + uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
>> + size_t len)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_value *value;
>> + ssize_t err;
>> + struct iio_counter_trigger *trigger;
>> + const int signal_id = *(int *)((void *)priv);
>> + unsigned int mode;
>> +
>> + if (!counter->ops->trigger_mode_set)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> +
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + err = -EINVAL;
>> + goto err_value;
>> + }
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> +
>> + trigger = iio_counter_trigger_find_by_id(value, signal_id);
>> + if (!trigger) {
>> + err = -EINVAL;
>> + goto err_trigger;
>> + }
>> +
>> + for (mode = 0; mode < trigger->num_trigger_modes; mode++)
>> + if (sysfs_streq(buf, trigger->trigger_modes[mode]))
>> + break;
>> +
>> + if (mode >= trigger->num_trigger_modes) {
>> + err = -EINVAL;
>> + goto err_trigger;
>> + }
>> +
>> + err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
>> + if (err)
>> + goto err_trigger;
>> +
>> + trigger->mode = mode;
>> +
>> + mutex_unlock(&value->trigger_list_lock);
>> +
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return len;
>> +
>> +err_trigger:
>> + mutex_unlock(&value->trigger_list_lock);
>> +err_value:
>> + mutex_unlock(&counter->value_list_lock);
>> + return err;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_available_read(
>> + struct iio_dev *indio_dev, uintptr_t priv,
>> + const struct iio_chan_spec *chan, char *buf)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_value *value;
>> + ssize_t len = 0;
>> + struct iio_counter_trigger *trigger;
>> + const int signal_id = *(int *)((void *)priv);
>> + unsigned int i;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> +
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + len = -EINVAL;
>> + goto err_no_value;
>> + }
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> +
>> + trigger = iio_counter_trigger_find_by_id(value, signal_id);
>> + if (!trigger) {
>> + len = -EINVAL;
>> + goto err_no_trigger;
>> + }
>> +
>> + for (i = 0; i < trigger->num_trigger_modes; i++)
>> + len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
>> + trigger->trigger_modes[i]);
>> +
>> + mutex_unlock(&value->trigger_list_lock);
>> +
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + buf[len - 1] = '\n';
>> +
>> + return len;
>> +
>> +err_no_trigger:
>> + mutex_unlock(&value->trigger_list_lock);
>> +err_no_value:
>> + mutex_unlock(&counter->value_list_lock);
>> + return len;
>> +}
>> +
>> +static int iio_counter_value_function_set(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan, unsigned int mode)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_value *value;
>> + int err;
>> +
>> + if (!counter->ops->value_function_set)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> +
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + err = -EINVAL;
>> + goto err_value;
>> + }
>> +
>> + err = counter->ops->value_function_set(counter, value, mode);
>> + if (err)
>> + goto err_value;
>> +
>> + value->mode = mode;
>> +
>> +err_value:
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return err;
>> +}
>> +
>> +static int iio_counter_value_function_get(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_value *value;
>> + int retval;
>> +
>> + if (!counter->ops->value_function_get)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> +
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + retval = -EINVAL;
>> + goto err_value;
>> + }
>> +
>> + retval = counter->ops->value_function_get(counter, value);
>> + if (retval < 0)
>> + goto err_value;
>> + else if (retval >= value->num_function_modes) {
>> + retval = -EINVAL;
>> + goto err_value;
>> + }
>> +
>> + value->mode = retval;
>> +
>> +err_value:
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return retval;
>> +}
>> +
>> +static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
>> + struct iio_counter_value *const value)
>> +{
>> + const struct iio_chan_spec_ext_info ext_info_default[] = {
>> + {
>> + .name = "name",
>> + .shared = IIO_SEPARATE,
>> + .read = iio_counter_value_name_read
>> + },
>> + IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
>> + {
>> + .name = "function_available",
>> + .shared = IIO_SEPARATE,
>> + .read = iio_enum_available_read,
>> + .private = (void *)&value->function_enum
>I have this warning the 3 times that private field is set
>drivers/iio/industrialio-counter.c:401:15: warning: initialization
>makes integer from pointer without a cast [-Wint-conversion]
> .private = (void *)&value->function_enum
>
>I think you can change it to "void *" instead of a uintptr_t

These are pretty benign warnings which you may ignore: I'm doing an
implicit conversion to uintptr_t which is ultimately an integer type; in
general, pointer to integer conversations are unsafe, but there is a
special exception in the C language for the uintptr_t data type. I could
have used an explicit cast to avoid the warning but opted to leave it
implicit to keep the code easier to read.

However, it would be nice if the private member was void *, since the
intent of my code would be clearer in that case (passing a pointer for
later dereference). Unfortunately, I'm hesitant to submit a patch to
change the "private" member data type to void * since other drivers may
require the uintptr_t data type -- my suspicion is that there are some
drivers out there which do since the choice to introduce the "private"
member as a uintptr_t appears deliberate.

>> + },
>> + {
>> + .name = "triggers",
>> + .shared = IIO_SEPARATE,
>> + .read = iio_counter_value_triggers_read
>> + }
>> + };
>> + const size_t num_default = ARRAY_SIZE(ext_info_default);
>> + const struct iio_chan_spec_ext_info ext_info_trigger[] = {
>> + {
>> + .shared = IIO_SEPARATE,
>> + .read = iio_counter_trigger_mode_read,
>> + .write = iio_counter_trigger_mode_write
>> + },
>> + {
>> + .shared = IIO_SEPARATE,
>> + .read = iio_counter_trigger_mode_available_read
>> + }
>> + };
>> + const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
>> + const struct list_head *pos;
>> + size_t num_triggers = 0;
>> + size_t num_triggers_ext_info;
>> + size_t num_ext_info;
>> + int err;
>> + struct iio_chan_spec_ext_info *ext_info;
>> + const struct iio_counter_trigger *trigger_pos;
>> + size_t i;
>> +
>> + value->function_enum.items = value->function_modes;
>> + value->function_enum.num_items = value->num_function_modes;
>> + value->function_enum.set = iio_counter_value_function_set;
>> + value->function_enum.get = iio_counter_value_function_get;
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> +
>> + list_for_each(pos, &value->trigger_list)
>> + num_triggers++;
>> +
>> + num_triggers_ext_info = num_ext_info_trigger * num_triggers;
>> + num_ext_info = num_default + num_triggers_ext_info + 1;
>> +
>> + ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
>> + if (!ext_info) {
>> + err = -ENOMEM;
>> + goto err_ext_info_alloc;
>> + }
>> + ext_info[num_ext_info - 1].name = NULL;
>> +
>> + memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
>> + for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
>> + memcpy(ext_info + num_default + i, ext_info_trigger,
>> + sizeof(ext_info_trigger));
>> +
>> + i = num_default;
>> + list_for_each_entry(trigger_pos, &value->trigger_list, list) {
>> + ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
>> + chan->channel, trigger_pos->signal->id);
>> + if (!ext_info[i].name) {
>> + err = -ENOMEM;
>> + goto err_name_alloc;
>> + }
>> + ext_info[i].private = (void *)&trigger_pos->signal->id;
>> + i++;
>> +
>> + ext_info[i].name = kasprintf(GFP_KERNEL,
>> + "trigger_signal%d-%d_available",
>> + chan->channel, trigger_pos->signal->id);
>> + if (!ext_info[i].name) {
>> + err = -ENOMEM;
>> + goto err_name_alloc;
>> + }
>> + ext_info[i].private = (void *)&trigger_pos->signal->id;
>> + i++;
>> + }
>> +
>> + chan->ext_info = ext_info;
>> +
>> + mutex_unlock(&value->trigger_list_lock);
>> +
>> + return 0;
>> +
>> +err_name_alloc:
>> + while (i-- > num_default)
>> + kfree(ext_info[i].name);
>> + kfree(ext_info);
>> +err_ext_info_alloc:
>> + mutex_unlock(&value->trigger_list_lock);
>> + return err;
>> +}
>> +
>> +static void iio_counter_value_ext_info_free(
>> + const struct iio_chan_spec *const channel)
>> +{
>> + size_t i;
>> + const char *const prefix = "trigger_signal";
>> + const size_t prefix_len = strlen(prefix);
>> +
>> + for (i = 0; channel->ext_info[i].name; i++)
>> + if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
>> + kfree(channel->ext_info[i].name);
>> + kfree(channel->ext_info);
>> +}
>> +
>> +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
>> + {
>> + .name = "name",
>> + .shared = IIO_SEPARATE,
>> + .read = iio_counter_signal_name_read
>> + },
>> + {}
>> +};
>> +
>> +static int iio_counter_channels_alloc(struct iio_counter *const counter)
>> +{
>> + const struct list_head *pos;
>> + size_t num_channels = 0;
>> + int err;
>> + struct iio_chan_spec *channels;
>> + struct iio_counter_value *value_pos;
>> + size_t i = counter->num_channels;
>> + const struct iio_counter_signal *signal_pos;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> +
>> + list_for_each(pos, &counter->signal_list)
>> + num_channels++;
>> +
>> + if (!num_channels) {
>> + err = -EINVAL;
>> + goto err_no_signals;
>> + }
>> +
>> + mutex_lock(&counter->value_list_lock);
>> +
>> + list_for_each(pos, &counter->value_list)
>> + num_channels++;
>> +
>> + num_channels += counter->num_channels;
>> +
>> + channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
>> + if (!channels) {
>> + err = -ENOMEM;
>> + goto err_channels_alloc;
>> + }
>> +
>> + memcpy(channels, counter->channels,
>> + counter->num_channels * sizeof(*counter->channels));
>> +
>> + list_for_each_entry(value_pos, &counter->value_list, list) {
>> + channels[i].type = IIO_COUNT;
>> + channels[i].channel = counter->id;
>> + channels[i].channel2 = value_pos->id;
>> + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> + channels[i].indexed = 1;
>> + channels[i].counter = 1;
>> +
>> + err = iio_counter_value_ext_info_alloc(channels + i, value_pos);
>> + if (err)
>> + goto err_value_ext_info_alloc;
>> +
>> + i++;
>> + }
>> +
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + list_for_each_entry(signal_pos, &counter->signal_list, list) {
>> + channels[i].type = IIO_SIGNAL;
>> + channels[i].channel = counter->id;
>> + channels[i].channel2 = signal_pos->id;
>> + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> + channels[i].indexed = 1;
>> + channels[i].counter = 1;
>> + channels[i].ext_info = iio_counter_signal_ext_info;
>> +
>> + i++;
>> + }
>> +
>> + mutex_unlock(&counter->signal_list_lock);
>> +
>> + counter->indio_dev->num_channels = num_channels;
>> + counter->indio_dev->channels = channels;
>> +
>> + return 0;
>> +
>> +err_value_ext_info_alloc:
>> + while (i-- > counter->num_channels)
>> + iio_counter_value_ext_info_free(channels + i);
>> + kfree(channels);
>> +err_channels_alloc:
>> + mutex_unlock(&counter->value_list_lock);
>> +err_no_signals:
>> + mutex_unlock(&counter->signal_list_lock);
>> + return err;
>> +}
>> +
>> +static void iio_counter_channels_free(const struct iio_counter *const counter)
>> +{
>> + size_t i = counter->num_channels + counter->indio_dev->num_channels;
>> + const struct iio_chan_spec *const chans = counter->indio_dev->channels;
>> +
>> + while (i-- > counter->num_channels)
>> + if (chans[i].type == IIO_COUNT)
>> + iio_counter_value_ext_info_free(chans + i);
>> +
>> + kfree(chans);
>> +}
>> +
>> +static int iio_counter_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan, int *val, int *val2, long mask)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_signal *signal;
>> + int retval;
>> + struct iio_counter_value *value;
>> +
>> + if (mask != IIO_CHAN_INFO_RAW)
>> + return -EINVAL;
>> +
>> + switch (chan->type) {
>> + case IIO_SIGNAL:
>> + if (!counter->ops->signal_read)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> + signal = iio_counter_signal_find_by_id(counter, chan->channel2);
>> + if (!signal) {
>> + mutex_unlock(&counter->signal_list_lock);
>> + return -EINVAL;
>> + }
>> +
>> + retval = counter->ops->signal_read(counter, signal, val, val2);
>> + mutex_unlock(&counter->signal_list_lock);
>> +
>> + return retval;
>> + case IIO_COUNT:
>> + if (!counter->ops->value_read)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + mutex_unlock(&counter->value_list_lock);
>> + return -EINVAL;
>> + }
>> +
>> + retval = counter->ops->value_read(counter, value, val, val2);
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return retval;
>> + default:
>> + if (counter->info && counter->info->read_raw)
>> + return counter->info->read_raw(indio_dev, chan, val,
>> + val2, mask);
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int iio_counter_write_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan, int val, int val2, long mask)
>> +{
>> + struct iio_counter *const counter = iio_priv(indio_dev);
>> + struct iio_counter_signal *signal;
>> + int retval;
>> + struct iio_counter_value *value;
>> +
>> + if (mask != IIO_CHAN_INFO_RAW)
>> + return -EINVAL;
>> +
>> + switch (chan->type) {
>> + case IIO_SIGNAL:
>> + if (!counter->ops->signal_write)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> + signal = iio_counter_signal_find_by_id(counter, chan->channel2);
>> + if (!signal) {
>> + mutex_unlock(&counter->signal_list_lock);
>> + return -EINVAL;
>> + }
>> +
>> + retval = counter->ops->signal_write(counter, signal, val, val2);
>> + mutex_unlock(&counter->signal_list_lock);
>> +
>> + return retval;
>> + case IIO_COUNT:
>> + if (!counter->ops->value_write)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + value = iio_counter_value_find_by_id(counter, chan->channel2);
>> + if (!value) {
>> + mutex_unlock(&counter->value_list_lock);
>> + return -EINVAL;
>> + }
>> +
>> + retval = counter->ops->value_write(counter, value, val, val2);
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return retval;
>> + default:
>> + if (counter->info && counter->info->write_raw)
>> + return counter->info->write_raw(indio_dev, chan, val,
>> + val2, mask);
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int iio_counter_signal_register(struct iio_counter *const counter,
>> + struct iio_counter_signal *const signal)
>> +{
>> + int err;
>> +
>> + if (!counter || !signal)
>> + return -EINVAL;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> + if (iio_counter_signal_find_by_id(counter, signal->id)) {
>> + pr_err("Duplicate counter signal ID '%d'\n", signal->id);
>> + err = -EEXIST;
>> + goto err_duplicate_id;
>> + }
>> + list_add_tail(&signal->list, &counter->signal_list);
>> + mutex_unlock(&counter->signal_list_lock);
>> +
>> + return 0;
>> +
>> +err_duplicate_id:
>> + mutex_unlock(&counter->signal_list_lock);
>> + return err;
>> +}
>> +
>> +static void iio_counter_signal_unregister(struct iio_counter *const counter,
>> + struct iio_counter_signal *const signal)
>> +{
>> + if (!counter || !signal)
>> + return;
>> +
>> + mutex_lock(&counter->signal_list_lock);
>> + list_del(&signal->list);
>> + mutex_unlock(&counter->signal_list_lock);
>> +}
>> +
>> +static int iio_counter_signals_register(struct iio_counter *const counter,
>> + struct iio_counter_signal *const signals, const size_t num_signals)
>> +{
>> + size_t i;
>> + int err;
>> +
>> + if (!counter || !signals)
>> + return -EINVAL;
>> +
>> + for (i = 0; i < num_signals; i++) {
>> + err = iio_counter_signal_register(counter, signals + i);
>> + if (err)
>> + goto err_signal_register;
>> + }
>> +
>> + return 0;
>> +
>> +err_signal_register:
>> + while (i--)
>> + iio_counter_signal_unregister(counter, signals + i);
>> + return err;
>> +}
>> +
>> +static void iio_counter_signals_unregister(struct iio_counter *const counter,
>> + struct iio_counter_signal *signals, size_t num_signals)
>> +{
>> + if (!counter || !signals)
>> + return;
>> +
>> + while (num_signals--) {
>> + iio_counter_signal_unregister(counter, signals);
>> + signals++;
>> + }
>> +}
>> +
>> +/**
>> + * iio_counter_trigger_register - register Trigger to Value
>> + * @value: pointer to IIO Counter Value for association
>> + * @trigger: pointer to IIO Counter Trigger to register
>> + *
>> + * The Trigger is added to the Value's trigger_list. A check is first performed
>> + * to verify that the respective Signal is not already linked to the Value; if
>> + * the respective Signal is already linked to the Value, the Trigger is not
>> + * added to the Value's trigger_list.
>> + *
>> + * NOTE: This function will acquire and release the Value's trigger_list_lock
>> + * during execution.
>> + */
>> +int iio_counter_trigger_register(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *const trigger)
>> +{
>> + if (!value || !trigger || !trigger->signal)
>> + return -EINVAL;
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> + if (iio_counter_trigger_find_by_id(value, trigger->signal->id)) {
>> + pr_err("Signal%d is already linked to counter value%d\n",
>> + trigger->signal->id, value->id);
>> + return -EEXIST;
>> + }
>> + list_add_tail(&trigger->list, &value->trigger_list);
>> + mutex_unlock(&value->trigger_list_lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(iio_counter_trigger_register);
>> +
>> +/**
>> + * iio_counter_trigger_unregister - unregister Trigger from Value
>> + * @value: pointer to IIO Counter Value of association
>> + * @trigger: pointer to IIO Counter Trigger to unregister
>> + *
>> + * The Trigger is removed from the Value's trigger_list.
>> + *
>> + * NOTE: This function will acquire and release the Value's trigger_list_lock
>> + * during execution.
>> + */
>> +void iio_counter_trigger_unregister(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *const trigger)
>> +{
>> + if (!value || !trigger || !trigger->signal)
>> + return;
>> +
>> + mutex_lock(&value->trigger_list_lock);
>> + list_del(&trigger->list);
>> + mutex_unlock(&value->trigger_list_lock);
>> +}
>> +EXPORT_SYMBOL(iio_counter_trigger_unregister);
>> +
>> +/**
>> + * iio_counter_triggers_register - register an array of Triggers to Value
>> + * @value: pointer to IIO Counter Value for association
>> + * @triggers: array of pointers to IIO Counter Triggers to register
>> + *
>> + * The iio_counter_trigger_register function is called for each Trigger in the
>> + * array. The @triggers array is traversed for the first @num_triggers Triggers.
>> + *
>> + * NOTE: @num_triggers must not be greater than the size of the @triggers array.
>> + */
>> +int iio_counter_triggers_register(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *const triggers, const size_t num_triggers)
>> +{
>> + size_t i;
>> + int err;
>> +
>> + if (!value || !triggers)
>> + return -EINVAL;
>> +
>> + for (i = 0; i < num_triggers; i++) {
>> + err = iio_counter_trigger_register(value, triggers + i);
>> + if (err)
>> + goto err_trigger_register;
>> + }
>> +
>> + return 0;
>> +
>> +err_trigger_register:
>> + while (i--)
>> + iio_counter_trigger_unregister(value, triggers + i);
>> + return err;
>> +}
>> +EXPORT_SYMBOL(iio_counter_triggers_register);
>> +
>> +/**
>> + * iio_counter_triggers_unregister - unregister Triggers from Value
>> + * @value: pointer to IIO Counter Value of association
>> + * @triggers: array of pointers to IIO Counter Triggers to unregister
>> + *
>> + * The iio_counter_trigger_unregister function is called for each Trigger in the
>> + * array. The @triggers array is traversed for the first @num_triggers Triggers.
>> + *
>> + * NOTE: @num_triggers must not be greater than the size of the @triggers array.
>> + */
>> +void iio_counter_triggers_unregister(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *triggers, size_t num_triggers)
>> +{
>> + if (!value || !triggers)
>> + return;
>> +
>> + while (num_triggers--) {
>> + iio_counter_trigger_unregister(value, triggers);
>> + triggers++;
>> + }
>> +}
>> +EXPORT_SYMBOL(iio_counter_triggers_unregister);
>> +
>> +/**
>> + * iio_counter_value_register - register Value to Counter
>> + * @counter: pointer to IIO Counter for association
>> + * @value: pointer to IIO Counter Value to register
>> + *
>> + * The registration process occurs in two major steps. First, the Value is
>> + * initialized: trigger_list_lock is initialized, trigger_list is initialized,
>> + * and init_triggers if not NULL is passed to iio_counter_triggers_register.
>> + * Second, the Value is added to the Counter's value_list. A check is first
>> + * performed to verify that the Value is not already associated to the Counter
>> + * (via the Value's unique ID); if the Value is already associated to the
>> + * Counter, the Value is not added to the Counter's value_list and all of the
>> + * Value's Triggers are unregistered.
>> + *
>> + * NOTE: This function will acquire and release the Counter's value_list_lock
>> + * during execution.
>> + */
>> +int iio_counter_value_register(struct iio_counter *const counter,
>> + struct iio_counter_value *const value)
>> +{
>> + int err;
>> +
>> + if (!counter || !value)
>> + return -EINVAL;
>> +
>> + mutex_init(&value->trigger_list_lock);
>> + INIT_LIST_HEAD(&value->trigger_list);
>> +
>> + if (value->init_triggers) {
>> + err = iio_counter_triggers_register(value,
>> + value->init_triggers, value->num_init_triggers);
>> + if (err)
>> + return err;
>> + }
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + if (iio_counter_value_find_by_id(counter, value->id)) {
>> + pr_err("Duplicate counter value ID '%d'\n", value->id);
>> + err = -EEXIST;
>> + goto err_duplicate_id;
>> + }
>> + list_add_tail(&value->list, &counter->value_list);
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + return 0;
>> +
>> +err_duplicate_id:
>> + mutex_unlock(&counter->value_list_lock);
>> + iio_counter_trigger_unregister_all(value);
>> + return err;
>> +}
>> +EXPORT_SYMBOL(iio_counter_value_register);
>> +
>> +/**
>> + * iio_counter_value_unregister - unregister Value from Counter
>> + * @counter: pointer to IIO Counter of association
>> + * @value: pointer to IIO Counter Value to unregister
>> + *
>> + * The Value is removed from the Counter's value_list and all of the Value's
>> + * Triggers are unregistered.
>> + *
>> + * NOTE: This function will acquire and release the Counter's value_list_lock
>> + * during execution.
>> + */
>> +void iio_counter_value_unregister(struct iio_counter *const counter,
>> + struct iio_counter_value *const value)
>> +{
>> + if (!counter || !value)
>> + return;
>> +
>> + mutex_lock(&counter->value_list_lock);
>> + list_del(&value->list);
>> + mutex_unlock(&counter->value_list_lock);
>> +
>> + iio_counter_trigger_unregister_all(value);
>> +}
>> +EXPORT_SYMBOL(iio_counter_value_unregister);
>> +
>> +/**
>> + * iio_counter_values_register - register an array of Values to Counter
>> + * @counter: pointer to IIO Counter for association
>> + * @values: array of pointers to IIO Counter Values to register
>> + *
>> + * The iio_counter_value_register function is called for each Value in the
>> + * array. The @values array is traversed for the first @num_values Values.
>> + *
>> + * NOTE: @num_values must not be greater than the size of the @values array.
>> + */
>> +int iio_counter_values_register(struct iio_counter *const counter,
>> + struct iio_counter_value *const values, const size_t num_values)
>> +{
>> + size_t i;
>> + int err;
>> +
>> + if (!counter || !values)
>> + return -EINVAL;
>> +
>> + for (i = 0; i < num_values; i++) {
>> + err = iio_counter_value_register(counter, values + i);
>> + if (err)
>> + goto err_values_register;
>> + }
>> +
>> + return 0;
>> +
>> +err_values_register:
>> + while (i--)
>> + iio_counter_value_unregister(counter, values + i);
>> + return err;
>> +}
>> +EXPORT_SYMBOL(iio_counter_values_register);
>> +
>> +/**
>> + * iio_counter_values_unregister - unregister Values from Counter
>> + * @counter: pointer to IIO Counter of association
>> + * @values: array of pointers to IIO Counter Values to unregister
>> + *
>> + * The iio_counter_value_unregister function is called for each Value in the
>> + * array. The @values array is traversed for the first @num_values Values.
>> + *
>> + * NOTE: @num_values must not be greater than the size of the @values array.
>> + */
>> +void iio_counter_values_unregister(struct iio_counter *const counter,
>> + struct iio_counter_value *values, size_t num_values)
>> +{
>> + if (!counter || !values)
>> + return;
>> +
>> + while (num_values--) {
>> + iio_counter_value_unregister(counter, values);
>> + values++;
>> + }
>> +}
>> +EXPORT_SYMBOL(iio_counter_values_unregister);
>> +
>> +/**
>> + * iio_counter_register - register Counter to the system
>> + * @counter: pointer to IIO Counter to register
>> + *
>> + * This function piggybacks off of iio_device_register. First, the relevant
>> + * Counter members are initialized; if init_signals is not NULL it is passed to
>> + * iio_counter_signals_register, and similarly if init_values is not NULL it is
>> + * passed to iio_counter_values_register. Next, a struct iio_dev is allocated by
>> + * a call to iio_device_alloc and initialized for the Counter, IIO channels are
>> + * allocated, the Counter is copied as the private data, and finally
>> + * iio_device_register is called.
>> + */
>> +int iio_counter_register(struct iio_counter *const counter)
>> +{
>> + const struct iio_info info_default = {
>> + .read_raw = iio_counter_read_raw,
>> + .write_raw = iio_counter_write_raw
>> + };
>> + int err;
>> + struct iio_info *info;
>> + struct iio_counter *priv;
>> +
>> + if (!counter)
>> + return -EINVAL;
>> +
>> + mutex_init(&counter->signal_list_lock);
>> + INIT_LIST_HEAD(&counter->signal_list);
>> +
>> + if (counter->init_signals) {
>> + err = iio_counter_signals_register(counter,
>> + counter->init_signals, counter->num_init_signals);
>> + if (err)
>> + return err;
>> + }
>> +
>> + mutex_init(&counter->value_list_lock);
>> + INIT_LIST_HEAD(&counter->value_list);
>> +
>> + if (counter->init_values) {
>> + err = iio_counter_values_register(counter,
>> + counter->init_values, counter->num_init_values);
>> + if (err)
>> + goto err_values_register;
>> + }
>> +
>> + counter->indio_dev = iio_device_alloc(sizeof(*counter));
>> + if (!counter->indio_dev) {
>> + err = -ENOMEM;
>> + goto err_iio_device_alloc;
>> + }
>> +
>> + info = kmalloc(sizeof(*info), GFP_KERNEL);
>> + if (!info) {
>> + err = -ENOMEM;
>> + goto err_info_alloc;
>> + }
>> + if (counter->info) {
>> + memcpy(info, counter->info, sizeof(*counter->info));
>> + info->read_raw = iio_counter_read_raw;
>> + info->write_raw = iio_counter_write_raw;
>> + } else {
>> + memcpy(info, &info_default, sizeof(info_default));
>> + }
>> +
>> + counter->indio_dev->info = info;
>> + counter->indio_dev->modes = INDIO_DIRECT_MODE;
>> + counter->indio_dev->name = counter->name;
>> + counter->indio_dev->dev.parent = counter->dev;
>> +
>> + err = iio_counter_channels_alloc(counter);
>> + if (err)
>> + goto err_channels_alloc;
>> +
>> + priv = iio_priv(counter->indio_dev);
>> + memcpy(priv, counter, sizeof(*priv));
>> +
>> + err = iio_device_register(priv->indio_dev);
>> + if (err)
>> + goto err_iio_device_register;
>> +
>> + return 0;
>> +
>> +err_iio_device_register:
>> + iio_counter_channels_free(counter);
>> +err_channels_alloc:
>> + kfree(info);
>> +err_info_alloc:
>> + iio_device_free(counter->indio_dev);
>> +err_iio_device_alloc:
>> + iio_counter_values_unregister(counter, counter->init_values,
>> + counter->num_init_values);
>> +err_values_register:
>> + iio_counter_signals_unregister(counter, counter->init_signals,
>> + counter->num_init_signals);
>> + return err;
>> +}
>> +EXPORT_SYMBOL(iio_counter_register);
>> +
>> +/**
>> + * iio_counter_unregister - unregister Counter from the system
>> + * @counter: pointer to IIO Counter to unregister
>> + *
>> + * The Counter is unregistered from the system. The indio_dev is unregistered,
>> + * allocated memory is freed, and all associated Values and Signals are
>> + * unregistered.
>> + */
>> +void iio_counter_unregister(struct iio_counter *const counter)
>> +{
>> + const struct iio_info *const info = counter->indio_dev->info;
>> +
>> + if (!counter)
>> + return;
>> +
>> + iio_device_unregister(counter->indio_dev);
>> +
>> + iio_counter_channels_free(counter);
>> +
>> + kfree(info);
>> + iio_device_free(counter->indio_dev);
>> +
>> + iio_counter_value_unregister_all(counter);
>> + iio_counter_signal_unregister_all(counter);
>> +}
>> +EXPORT_SYMBOL(iio_counter_unregister);
>> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
>> new file mode 100644
>> index 000000000000..1c1ca13a6053
>> --- /dev/null
>> +++ b/include/linux/iio/counter.h
>> @@ -0,0 +1,221 @@
>> +/*
>> + * Industrial I/O counter interface
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + * General Public License for more details.
>> + */
>> +#ifndef _IIO_COUNTER_H_
>> +#define _IIO_COUNTER_H_
>> +
>> +#ifdef CONFIG_IIO_COUNTER
>> +
>> +#include <linux/device.h>
>> +#include <linux/mutex.h>
>> +#include <linux/types.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +/**
>> + * struct iio_counter_signal - IIO Counter Signal node
>> + * @id: [DRIVER] unique ID used to identify signal
>> + * @name: [DRIVER] device-specific signal name
>> + * @list: [INTERN] list of all signals currently registered to counter
>> + */
>> +struct iio_counter_signal {
>> + int id;
>> + const char *name;
>> +
>> + struct list_head list;
>> +};
>> +
>> +/**
>> + * struct iio_counter_trigger - IIO Counter Trigger node
>> + * @mode: [DRIVER] current trigger mode state
>> + * @trigger_modes: [DRIVER] available trigger modes
>> + * @num_trigger_modes: [DRIVER] number of modes specified in @trigger_modes
>> + * @signal: [DRIVER] pointer to associated signal
>> + * @list: [INTERN] list of all triggers currently registered to
>> + * value
>> + */
>> +struct iio_counter_trigger {
>> + unsigned int mode;
>> + const char *const *trigger_modes;
>> + unsigned int num_trigger_modes;
>> + struct iio_counter_signal *signal;
>> +
>> + struct list_head list;
>> +};
>> +
>> +/**
>> + * struct iio_counter_value - IIO Counter Value node
>> + * @id: [DRIVER] unique ID used to identify value
>> + * @name: [DRIVER] device-specific value name
>> + * @mode: [DRIVER] current function mode state
>> + * @function_modes: [DRIVER] available function modes
>> + * @num_function_modes: [DRIVER] number of modes specified in @function_modes
>> + * @init_triggers: [DRIVER] array of triggers for initialization
>> + * @num_init_triggers: [DRIVER] number of triggers specified in @init_triggers
>> + * @function_enum: [INTERN] used internally to generate function attributes
>> + * @trigger_list_lock: [INTERN] lock for accessing @trigger_list
>> + * @trigger_list: [INTERN] list of all triggers currently registered to
>> + * value
>> + * @list: [INTERN] list of all values currently registered to
>> + * counter
>> + */
>> +struct iio_counter_value {
>> + int id;
>> + const char *name;
>> + unsigned int mode;
>> + const char *const *function_modes;
>> + unsigned int num_function_modes;
>> +
>> + struct iio_counter_trigger *init_triggers;
>> + size_t num_init_triggers;
>> +
>> + struct iio_enum function_enum;
>> + struct mutex trigger_list_lock;
>> + struct list_head trigger_list;
>> +
>> + struct list_head list;
>> +};
>> +
>> +struct iio_counter;
>> +
>> +/**
>> + * struct iio_counter_ops - IIO Counter related callbacks
>> + * @signal_read: function to request a signal value from the device.
>> + * Return value will specify the type of value returned by
>> + * the device. val and val2 will contain the elements
>> + * making up the returned value. Note that the counter
>> + * signal_list_lock is acquired before this function is
>> + * called, and released after this function returns.
>> + * @signal_write: function to write a signal value to the device.
>> + * Parameters and locking behavior are the same as
>> + * signal_read.
>> + * @trigger_mode_set: function to set the trigger mode. mode is the index of
>> + * the requested mode from the value trigger_modes array.
>> + * Note that the counter value_list_lock and value
>> + * trigger_list_lock are acquired before this function is
>> + * called, and released after this function returns.
>> + * @trigger_mode_get: function to get the current trigger mode. Return value
>> + * will specify the index of the current mode from the
>> + * value trigger_modes array. Locking behavior is the same
>> + * as trigger_mode_set.
>> + * @value_read: function to request a value value from the device.
>> + * Return value will specify the type of value returned by
>> + * the device. val and val2 will contain the elements
>> + * making up the returned value. Note that the counter
>> + * value_list_lock is acquired before this function is
>> + * called, and released after this function returns.
>> + * @value_write: function to write a value value to the device.
>> + * Parameters and locking behavior are the same as
>> + * value_read.
>> + * @value_function_set: function to set the value function mode. mode is the
>> + * index of the requested mode from the value
>> + * function_modes array. Note that the counter
>> + * value_list_lock is acquired before this function is
>> + * called, and released after this function returns.
>> + * @value_function_get: function to get the current value function mode. Return
>> + * value will specify the index of the current mode from
>> + * the value function_modes array. Locking behavior is the
>> + * same as value_function_get.
>> + */
>> +struct iio_counter_ops {
>> + int (*signal_read)(struct iio_counter *counter,
>> + struct iio_counter_signal *signal, int *val, int *val2);
>> + int (*signal_write)(struct iio_counter *counter,
>> + struct iio_counter_signal *signal, int val, int val2);
>> + int (*trigger_mode_set)(struct iio_counter *counter,
>> + struct iio_counter_value *value,
>> + struct iio_counter_trigger *trigger, unsigned int mode);
>> + int (*trigger_mode_get)(struct iio_counter *counter,
>> + struct iio_counter_value *value,
>> + struct iio_counter_trigger *trigger);
>> + int (*value_read)(struct iio_counter *counter,
>> + struct iio_counter_value *value, int *val, int *val2);
>> + int (*value_write)(struct iio_counter *counter,
>> + struct iio_counter_value *value, int val, int val2);
>> + int (*value_function_set)(struct iio_counter *counter,
>> + struct iio_counter_value *value, unsigned int mode);
>> + int (*value_function_get)(struct iio_counter *counter,
>> + struct iio_counter_value *value);
>> +};
>> +
>> +/**
>> + * struct iio_counter - IIO Counter data structure
>> + * @id: [DRIVER] unique ID used to identify counter
>> + * @name: [DRIVER] name of the device
>> + * @dev: [DRIVER] device structure, should be assigned a parent
>> + * and owner
>> + * @ops: [DRIVER] callbacks from driver for counter components
>> + * @init_signals: [DRIVER] array of signals for initialization
>> + * @num_init_signals: [DRIVER] number of signals specified in @init_signals
>> + * @init_values: [DRIVER] array of values for initialization
>> + * @num_init_values: [DRIVER] number of values specified in @init_values
>> + * @signal_list_lock: [INTERN] lock for accessing @signal_list
>> + * @signal_list: [INTERN] list of all signals currently registered to
>> + * counter
>> + * @value_list_lock: [INTERN] lock for accessing @value_list
>> + * @value_list: [INTERN] list of all values currently registered to
>> + * counter
>> + * @channels: [DRIVER] channel specification structure table
>> + * @num_channels: [DRIVER] number of channels specified in @channels
>> + * @info: [DRIVER] callbacks and constant info from driver
>> + * @indio_dev: [INTERN] industrial I/O device structure
>> + * @driver_data: [DRIVER] driver data
>> + */
>> +struct iio_counter {
>> + int id;
>> + const char *name;
>> + struct device *dev;
>> + const struct iio_counter_ops *ops;
>> +
>> + struct iio_counter_signal *init_signals;
>> + size_t num_init_signals;
>> + struct iio_counter_value *init_values;
>> + size_t num_init_values;
>> +
>> + struct mutex signal_list_lock;
>> + struct list_head signal_list;
>> + struct mutex value_list_lock;
>> + struct list_head value_list;
>> +
>> + const struct iio_chan_spec *channels;
>> + size_t num_channels;
>> + const struct iio_info *info;
>> +
>> + struct iio_dev *indio_dev;
>> + void *driver_data;
>> +};
>> +
>> +int iio_counter_trigger_register(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *const trigger);
>> +void iio_counter_trigger_unregister(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *const trigger);
>> +int iio_counter_triggers_register(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *const triggers, const size_t num_triggers);
>> +void iio_counter_triggers_unregister(struct iio_counter_value *const value,
>> + struct iio_counter_trigger *triggers, size_t num_triggers);
>> +
>> +int iio_counter_value_register(struct iio_counter *const counter,
>> + struct iio_counter_value *const value);
>> +void iio_counter_value_unregister(struct iio_counter *const counter,
>> + struct iio_counter_value *const value);
>> +int iio_counter_values_register(struct iio_counter *const counter,
>> + struct iio_counter_value *const values, const size_t num_values);
>> +void iio_counter_values_unregister(struct iio_counter *const counter,
>> + struct iio_counter_value *values, size_t num_values);
>> +
>> +int iio_counter_register(struct iio_counter *const counter);
>> +void iio_counter_unregister(struct iio_counter *const counter);
>> +
>> +#endif /* CONFIG_IIO_COUNTER */
>> +
>> +#endif /* _IIO_COUNTER_H_ */
>> --
>> 2.14.1
>>
>
>
>
>--
>Benjamin Gaignard
>
>Graphic Study Group
>
>Linaro.org │ Open source software for ARM SoCs
>
>Follow Linaro: Facebook | Twitter | Blog