2017-09-25 20:08 GMT+02:00 William Breathitt Gray <vilhelm.gray@xxxxxxxxx>: > 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 <vilhelm.gray@xxxxxxxxx> 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 <vilhelm.gray@xxxxxxxxx> > +L: linux-iio@xxxxxxxxxxxxxxx > +S: Maintained > +F: drivers/iio/industrialio-counter.c > +F: include/linux/iio/counter.h > + > IIO MULTIPLEXER > M: Peter Rosin <peda@xxxxxxxxxx> > L: linux-iio@xxxxxxxxxxxxxxx > 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 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html