iio: Introduce the generic counter interface
Subsystems (2)
v3 cover iio: Introduce the generic counter interface
There have been some significant implementation changes for this version of the patchset. Here's a brief summary of the updates:
- Inline comments throughout the industrial-counter.c file; this should help clarify some of the code and aid in the review of the system architecture
- Simplification of the driver API; primarily, dynamic updates to the Counter Values, Triggers, and Signals lists are no longer allowed -- all relationships must be defined before the Counter is registered to the system
- Dynamic component registration functions (iio_counter_value_register, et al.) have been removed, and now the signals, values, and triggers array members serve to register the components; signals, values, and triggers were previously named init_signals, init_values, and init_triggers respectively -- the "init_" prefix was removed now that dynamic registration is no longer allowed
- Requiring static component relationships means linked lists are no longer necessary so all the list related code has been removed; Signals, Triggers, and Values are now accessed directly from the arrays supplied in the iio_counter structure
- No dynamic additions/substraction of components eliminates the need for mutex locks, so those have been removed as well
- Previously, the entire iio_counter structure was copied and stored as the iio_dev private data; now, only a pointer to the supplied iio_counter structure is stored
- devm_iio_counter_register and devm_iio_counter_unregister functions were incorporated from Benjamin Gaignard's suggestions
- Dummy counter driver provided as a reference for a simple counter implementation utilizing the Generic Counter interface
In a previous discussion, Benjamin noted some conflicts between the Generic Counter and the underlying IIO core code: mapping does not always work out seemlessly and sometimes the IIO core functionality exposure provided via iio_counter is not quite flexible enough. I intend to address these shortcomings in the next version of this patchset hopefully.
In particular, I'm leaning towards separating the Generic Counter interface from the IIO core dependency entirely. Since the Generic Counter inteface focuses on a more abstract representation of a counter device, I don't think it provides a suitable interface to map onto IIO core, which appears to focus more on a representation of the physical hardware itself.
Separating Generic Counter from IIO core should allow a driver to use the IIO core functions (iio_device_register, et al.) to represent the physical nature of their hardware (voltages, currents, etc.), while also utilize the Generic Counter interface to represent the abstract relationships between those Signals and their ultimate counter Values.
In particular, I'm hoping for the Generic Counter system implementation itself to not require all this hoop-jumping (mapping to IIO core functions, jumping back to the parent iio_counter, handling non-matching parameter lists between iio_counter and iio_dev, etc.); that should make the code simpler to debug, more efficient, and more stable in the long-run. I will consider these advantages and disadvantages before committing to a separation however.
Regarding this version of the patchset in particular, I decided to remove the dynamic component registration functionality since I doubt that many devices would require such; almost all, if not all, hardware counters I encountered have static relationships between input lines and the count value (i.e. specific input lines correlate to specific count registers).
The requirement that Counter component relationships are static has opened the possibility of some optimizations in the Generic Counter interface code:
- Signals, Triggers, and Values are provided as arrays; these arrays could benefit from some amount of sorting based on component IDs
- Searching for a component can be more efficient in a sorted array; currently, the code just walks down the array elements and compare the IDs
- Similar to general searching, the arrays are initially verified to gurantee unique IDs; currently, the code walks down the array elements and compares the current element ID with all the elements before and after -- this could be made more efficient with some sorting
There are likely other areas of improvement, so let me know and I'll see what I can do.
William Breathitt Gray (6):
iio: Implement counter channel specification and IIO_SIGNAL constant
iio: Introduce the generic counter interface
iio: Documentation: Add IIO Generic Counter sysfs documentation
docs: Add IIO Generic Counter Interface documentation
iio: Add dummy counter driver
iio: 104-quad-8: Add IIO generic counter interface support
.../testing/sysfs-bus-iio-generic-counter-sysfs | 63 ++ Documentation/driver-api/iio/generic-counter.txt | 526 ++++++++++++ MAINTAINERS | 7 + drivers/iio/Kconfig | 8 + drivers/iio/Makefile | 1 + drivers/iio/counter/104-quad-8.c | 294 ++++++- drivers/iio/counter/Kconfig | 16 + drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 293 +++++++ drivers/iio/industrialio-core.c | 14 +- drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++ include/linux/iio/counter.h | 166 ++++ include/linux/iio/iio.h | 2 + include/uapi/linux/iio/types.h | 1 + 14 files changed, 2274 insertions(+), 18 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs create mode 100644 Documentation/driver-api/iio/generic-counter.txt create mode 100644 drivers/iio/counter/dummy-counter.c create mode 100644 drivers/iio/industrialio-counter.c create mode 100644 include/linux/iio/counter.h
Signature · 1 line
2.14.1
PATCH v3 1/6 iio: Implement counter channel specification and IIO_SIGNAL constant
Counters are IIO devices that are exposed via a generic counter interface consisting of one or more counter signals (IIO_SIGNAL) linked to one or more counter values (IIO_COUNT).
This patch introduces the IIO_SIGNAL constant which represents a counter device signal line. Additionally, a new "counter" member is added to struct iio_chan_spec, with relevant support, to indicate that a channel is part of a counter.
--- drivers/iio/industrialio-core.c | 14 +++++++++++++- include/linux/iio/iio.h | 2 ++ include/uapi/linux/iio/types.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-)
drivers/iio/industrialio-core.c +13 -1
@@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_COUNT] = "count", [IIO_INDEX] = "index", [IIO_GRAVITY] = "gravity", + [IIO_SIGNAL] = "signal", };static const char *const iio_modifier_names[] = {
@@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,break ;case IIO_SEPARATE : -if (chan->indexed) +if (chan->counter) { +if (!chan->indexed) { +WARN (1 ,"Counter channels must be indexed\n" ); + ret = -EINVAL ; + goto error_free_full_postfix; + } + name =kasprintf (GFP_KERNEL ,"%s%d-%d_%s" , + iio_chan_type_name_spec[chan->type], + chan->channel, + chan->channel2, + full_postfix); + }else if (chan->indexed) name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type],
include/linux/iio/iio.h +2 -0
@@ -263,6 +263,7 @@ struct iio_event_spec {* attributes but not for event codes. * @output: Channel is output. * @differential: Channel is differential. +* @counter: Channel is part of a counter. */ struct iio_chan_spec {enum iio_chan_type type;
@@ -295,6 +296,7 @@ struct iio_chan_spec { unsigned indexed:1; unsigned output:1; unsigned differential:1; + unsigned counter:1; };
include/uapi/linux/iio/types.h +1 -0
@@ -43,6 +43,7 @@ enum iio_chan_type {IIO_COUNT ,IIO_INDEX ,IIO_GRAVITY , + IIO_SIGNAL, }; enum iio_modifier {
Signature · 1 line
2.14.1
Jonathan Cameron
maintainerRe: [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant
Quoted 10 lines · On Thu, 5 Oct 2017 14:13:30 -0400 William Breathitt Gray…
Counters are IIO devices that are exposed via a generic counter interface consisting of one or more counter signals (
IIO_SIGNAL) linked to one or more counter values (IIO_COUNT).This patch introduces the
IIO_SIGNALconstant which represents a counter device signal line. Additionally, a new "counter" member is added to structiio_chan_spec, with relevant support, to indicate that a channel is part of a counter.Signed-off-by William Breathitt Gray <[email protected]>
I have a feeling this support could be useful more generically
than just for counters. There are other cases where a couple
of different input signals are combined to give one result and we don't currently represent that combination.
A complex example would be differential channels where we have some elements specific to the individual lines (the ability to apply front end PGA type effects separately) - Not sure I've seen one of these though ;)
Simpler case is light sensors - two light sensors are combined to provide an illuminance output (one is typically all wavelengths and one is infrared only - as we want to remove that from signal).
Some of these devices have multiple sensor inputs and right now we don't make it clear which ones feed which illuminance values and we should do.
Now this introduces another issue - those channels are indexed and modified so already using both channel and channel2.
So... Do we ever need to represent provide the 'counter' element in an event, (which is where we are short on space in the ABI)? I don't think we do as channel is unique anyway (I think) so this isn't a problem.
If not - define a new element to take this index rather than overloading channel2 (again).
Jonathan
Quoted 20 lines
--- drivers/iio/industrialio-core.c | 14 +++++++++++++- include/linux/iio/iio.h | 2 ++ include/uapi/linux/iio/types.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-)drivers/iio/industrialio-core.c +1
-0@@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_COUNT] = "count", [IIO_INDEX] = "index", [IIO_GRAVITY] = "gravity", + [IIO_SIGNAL] = "signal", };static const char *const iio_modifier_names[] = {@@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,break ;
drivers/iio/industrialio-core.c int __iio_device_attr_init(struct device_attribute *dev_attr, · line 999
Quoted 10 lines
drivers/iio/industrialio-core.c +7
-1case IIO_SEPARATE : -if (chan->indexed) +if (chan->counter) { +if (!chan->indexed) { +WARN (1 ,"Counter channels must be indexed\n" ); + ret = -EINVAL ; + goto error_free_full_postfix; + } + name = kasprintf(GFP_KERNEL, "%s%d-%d_%s",
Hmm. - has a meaning in IIO already - perhaps we need a different symbol to represent this grouping concept?
drivers/iio/industrialio-core.c int __iio_device_attr_init(struct device_attribute *dev_attr, · line 1004
Quoted 5 lines
drivers/iio/industrialio-core.c +5
-0+ iio_chan_type_name_spec[chan->type], + chan->channel, + chan->channel2, + full_postfix); + }else if (chan->indexed)
Ultimately probably want to cover the various shared cases as well.
Quoted 35 lines
drivers/iio/industrialio-core.c +0
-0name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type],include/linux/iio/iio.h +2
-0@@ -263,6 +263,7 @@ struct iio_event_spec {* attributes but not for event codes. * @output: Channel is output. * @differential: Channel is differential. +* @counter: Channel is part of a counter. */ struct iio_chan_spec {enum iio_chan_type type;@@ -295,6 +296,7 @@ struct iio_chan_spec { unsigned indexed:1; unsigned output:1; unsigned differential:1; + unsigned counter:1; };include/uapi/linux/iio/types.h +1
-0@@ -43,6 +43,7 @@ enum iio_chan_type {IIO_COUNT ,IIO_INDEX ,IIO_GRAVITY , + IIO_SIGNAL, }; enum iio_modifier {
PATCH v3 2/6 iio: Introduce the generic counter interface
In reply to William Breathitt GrayThis patch introduces the IIO generic counter interface for supporting counter devices. The generic counter interface serves as a catch-all to enable rudimentary support for devices that qualify as counters. More specific and apt counter interfaces may be developed on top of the generic counter interface, and those interfaces should be used by drivers when possible rather than the generic counter interface.
In the context of the IIO generic counter interface, a counter is defined as a device that reports one or more "counter values" based on the state changes of one or more "counter signals" as evaluated by a defined "counter function."
The IIO generic counter interface piggybacks off of the IIO core. This is primarily used to leverage the existing sysfs setup: the IIO_COUNT channel attributes represent the Counter Value, while the IIO_SIGNAL channel attributes represent the Counter Signal; auxilary IIO_COUNT attributes represent the Counter Signal connections (Triggers) and their respective state change configurations which trigger an associated "counter function" evaluation.
The iio_counter_ops structure serves as a container for driver callbacks to communicate with the device; function callbacks are provided to read and write various Signals and Values, and set and get the "trigger mode" and "function mode" for various Triggers and Values respectively.
To support a counter device, a driver must first allocate the available Counter Signals via iio_counter_signal structures. These Signals should be stored as an array and set to the signals array member of an allocated iio_counter structure before the Counter is registered to the system.
Counter Values may be allocated via iio_counter_value structures, and respective Counter Signal associations (Triggers) made via iio_counter_trigger structures. Associated iio_counter_trigger structures are stored as an array and set to the the triggers array member of the respective iio_counter_value structure. These iio_counter_value structures are set to the values array member of an allocated iio_counter structure before the Counter is registered to the system.
A counter device is registered to the system by passing the respective initialized iio_counter structure to the iio_counter_register function; similarly, the iio_counter_unregister function unregisters the respective counter. The devm_iio_counter_register and iio_devm_iio_counter_unregister functions serve as device memory-managed versions of the iio_counter_register and iio_counter unregister functions respectively.
--- MAINTAINERS | 7 + drivers/iio/Kconfig | 8 + drivers/iio/Makefile | 1 + drivers/iio/counter/Kconfig | 1 + drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++++++++++++++++++ include/linux/iio/counter.h | 166 +++++++ 6 files changed, 1083 insertions(+) create mode 100644 drivers/iio/industrialio-counter.c create mode 100644 include/linux/iio/counter.h
MAINTAINERS +7 -0
@@ -6693,6 +6693,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt F: drivers/iio/adc/envelope-detector.c +IIO GENERIC COUNTER INTERFACE +M: William Breathitt Gray <[email protected]> +L: [email protected] +S: Maintained +F: drivers/iio/industrialio-counter.c +F: include/linux/iio/counter.h + IIO MULTIPLEXER M: Peter Rosin <[email protected]> L: [email protected]
drivers/iio/Kconfig +8 -0
@@ -30,6 +30,14 @@ config IIO_CONFIGFS (e.g. software triggers). For more info see Documentation/iio/iio_configfs.txt. +config IIO_COUNTER + bool "Enable IIO counter support" + help + Provides IIO core support for counters. This API provides + a generic interface that serves as the building blocks to + create more complex counter interfaces. Rudimentary support + for counters is enabled. + config IIO_TRIGGER bool "Enable triggered sampling support" help
drivers/iio/Makefile +1 -0
@@ -5,6 +5,7 @@ obj-$(CONFIG_IIO) += industrialio.o industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o +industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
drivers/iio/counter/Kconfig +1 -0
@@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order menu "Counters" + depends on IIO_COUNTER config 104_QUAD_8 tristate "ACCES 104-QUAD-8 driver"
drivers/iio/industrialio-counter.c new file +900 -0
900 lines not on this page · raw message
include/linux/iio/counter.h new file +166 -0
166 lines not on this page · raw message
Signature · 1 line
2.14.1
Jonathan Cameron
maintainerQuoted 6 lines · On Thu, 5 Oct 2017 14:13:44 -0400 William Breathitt Gray…
This patch introduces the IIO generic counter interface for supporting counter devices. The generic counter interface serves as a catch-all to enable rudimentary support for devices that qualify as counters. More specific and apt counter interfaces may be developed on top of the generic counter interface, and those interfaces should be used by drivers when possible rather than the generic counter interface.
I think you probably want to avoid any additional layering and try to push things down here (maybe with a few utility functions to simplify common cases), but that's a question for another day!
Quoted 34 lines
In the context of the IIO generic counter interface, a counter is defined as a device that reports one or more "counter values" based on the state changes of one or more "counter signals" as evaluated by a defined "counter function."
The IIO generic counter interface piggybacks off of the IIO core. This is primarily used to leverage the existing sysfs setup: the
IIO_COUNTchannel attributes represent the Counter Value, while theIIO_SIGNALchannel attributes represent the Counter Signal; auxilaryIIO_COUNTattributes represent the Counter Signal connections (Triggers) and their respective state change configurations which trigger an associated "counter function" evaluation.The
iio_counter_opsstructure serves as a container for driver callbacks to communicate with the device; function callbacks are provided to read and write various Signals and Values, and set and get the "trigger mode" and "function mode" for various Triggers and Values respectively.To support a counter device, a driver must first allocate the available Counter Signals via
iio_counter_signalstructures. These Signals should be stored as an array and set to the signals array member of an allocatediio_counterstructure before the Counter is registered to the system.Counter Values may be allocated via
iio_counter_valuestructures, and respective Counter Signal associations (Triggers) made viaiio_counter_triggerstructures. Associatediio_counter_triggerstructures are stored as an array and set to the the triggers array member of the respectiveiio_counter_valuestructure. Theseiio_counter_valuestructures are set to the values array member of an allocatediio_counterstructure before the Counter is registered to the system.
Quoted 7 lines
A counter device is registered to the system by passing the respective initialized
iio_counterstructure to theiio_counter_registerfunction; similarly, theiio_counter_unregisterfunction unregisters the respective counter. Thedevm_iio_counter_registerandiio_devm_iio_counter_unregisterfunctions serve as device memory-managed versions of theiio_counter_registerandiio_counterunregister functions respectively.
All the find by ids are the main complexity introduced by layering this on IIO that you probably wouldn't have if it wasn't so layered. Hmm. I suggest inline that you could allow drivers to provide fast versions of that search given they can probably infer it directly from the index. Might be something to ignore for now though in the interests of initial simplicity.
Otherwise a few comments inline, but mostly this is coming together pretty well. I'm actually surprised the layering on top of IIO didn't end up more painful than it did. It's not horrendous - which is not to say you wouldn't be better breaking free of that entirely... (I'm not sure either way).
I think the big remaining questions are around naming more than anything else. I don't like the name value when a user will think they are looking for a counter. If you use counter though the current use of counter needs to change.
Trigger is also, as has been raised, a rather overloaded term. Not totally obvious what a better term is but we shouldn't reuse that one...
I'll try and think of something if you have no luck!
Jonathan
Quoted 18 lines
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 | 900 +++++++++++++++++++++++++++++++++++++ include/linux/iio/counter.h | 166 +++++++ 6 files changed, 1083 insertions(+) create mode 100644 drivers/iio/industrialio-counter.c create mode 100644 include/linux/iio/counter.hMAINTAINERS +0
-0
MAINTAINERS F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector · line 6701
Quoted 9 lines
MAINTAINERS +6
-0@@ -6693,6 +6693,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt F: drivers/iio/adc/envelope-detector.c +IIO GENERIC COUNTER INTERFACE +M: William Breathitt Gray <[email protected]> +L: [email protected] +S: Maintained +F: drivers/iio/industrialio-counter.c +F: include/linux/iio/counter.h
Don't forget the directory the drivers are going in ;) *laughs manically*
Quoted 108 lines
MAINTAINERS +1
-0+ IIO MULTIPLEXER M: Peter Rosin <[email protected]> L: [email protected]drivers/iio/Kconfig +8
-0@@ -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" helpdrivers/iio/Makefile +1
-0@@ -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.odrivers/iio/counter/Kconfig +1
-0@@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order menu "Counters" + depends on IIO_COUNTER config 104_QUAD_8 tristate "ACCES 104-QUAD-8 driver"drivers/iio/industrialio-counter.c new file +55
-0@@ -0,0 +1,900 @@ +/* +* Industrial I/O counter interface functions +* Copyright (C) 2017 William Breathitt Gray +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, version 2, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#include <linux/iio/counter.h> + +#define iio_priv_get_counter (_indio_dev) \ + (*(struct iio_counter **)iio_priv(_indio_dev)) + +static struct iio_counter_signal *iio_counter_signal_find_by_id ( +const struct iio_counter *const counter,const int id) +{ +size_t i; +struct iio_counter_signal *signal; + +for (i =0 ; i < counter->num_signals; i++) { + signal = counter->signals + i; +if (signal->id == id) +return signal; + } + +return NULL ; +} + +static struct iio_counter_trigger *iio_counter_trigger_find_by_id ( +const struct iio_counter_value *const value,const int id) +{ +size_t i; +struct iio_counter_trigger *trigger; + +for (i =0 ; i < value->num_triggers; i++) { + trigger = value->triggers + i; +if (trigger->signal->id == id) +return trigger;
drivers/iio/industrialio-counter.c line 65
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ } + +return NULL ; +} + +static struct iio_counter_value *iio_counter_value_find_by_id ( +const struct iio_counter *const counter,const int id) +{ +size_t i; +struct iio_counter_value *value;
In most cases this mapping will be entirely obvious to the driver - perhaps provide an optional callback to let it provide it directly without any searching?
We could always add this later though if we start getting drivers with lots of instances of the various parts...
Quoted 24 lines
drivers/iio/industrialio-counter.c new file +24
-0+ +for (i =0 ; i < counter->num_values; i++) { + value = counter->values + i; +if (value->id == id) +return value; + } + +return NULL ; +} + +static ssize_t iio_counter_signal_name_read (struct iio_dev *indio_dev, +uintptr_t priv,const struct iio_chan_spec *chan,char *buf) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +const struct iio_counter_signal *signal; + + signal =iio_counter_signal_find_by_id (counter, chan->channel2); +if (!signal) +return -EINVAL ; + +return scnprintf (buf,PAGE_SIZE ,"%s\n" , signal->name); +} + +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
drivers/iio/industrialio-counter.c line 99
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + struct iio_counter *const counter = iio_priv_get_counter(indio_dev); +const struct iio_counter_value *value; + + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + +return scnprintf (buf,PAGE_SIZE ,"%s\n" , value->name);
I mentioned before but I wonder if we can just push name down into the chan spec itself in some fashion. Whether to associate it with existing datasheet_name or not is an open question however.
Quoted 153 lines
drivers/iio/industrialio-counter.c new file +153
-0+} + +static ssize_t iio_counter_value_triggers_read (struct iio_dev *indio_dev, +uintptr_t priv,const struct iio_chan_spec *chan,char *buf) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_value *value; +size_t i; +const struct iio_counter_trigger *trigger; +ssize_t len =0 ; + + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + +/* Print out a list of every Signal association to Value */ +for (i =0 ; i < value->num_triggers; i++) { + trigger = value->triggers + i; + len +=snprintf (buf + len,PAGE_SIZE - len,"%d\t%s\t%s\n" , + trigger->signal->id, trigger->signal->name, + trigger->trigger_modes[trigger->mode]); +if (len >=PAGE_SIZE ) +return -ENOMEM ; + } + +return len; +} + +static ssize_t iio_counter_trigger_mode_read (struct iio_dev *indio_dev, +uintptr_t priv,const struct iio_chan_spec *chan,char *buf) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_value *value; +struct iio_counter_trigger *trigger; +const int signal_id = *(int *)((void *)priv); +int mode; + +if (!counter->ops->trigger_mode_get) +return -EINVAL ; + + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + + trigger =iio_counter_trigger_find_by_id (value, signal_id); +if (!trigger) +return -EINVAL ; + + mode = counter->ops->trigger_mode_get (counter, value, trigger); + +if (mode <0 ) +return mode; +else if (mode >= trigger->num_trigger_modes) +return -EINVAL ; + + trigger->mode = mode; + +return scnprintf (buf,PAGE_SIZE ,"%s\n" , trigger->trigger_modes[mode]); +} + +static ssize_t iio_counter_trigger_mode_write (struct iio_dev *indio_dev, +uintptr_t priv,const struct iio_chan_spec *chan,const char *buf, +size_t len) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_value *value; +ssize_t err; +struct iio_counter_trigger *trigger; +const int signal_id = *(int *)((void *)priv); +unsigned int mode; + +if (!counter->ops->trigger_mode_set) +return -EINVAL ; + + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + + trigger =iio_counter_trigger_find_by_id (value, signal_id); +if (!trigger) +return -EINVAL ; + +for (mode =0 ; mode < trigger->num_trigger_modes; mode++) +if (sysfs_streq (buf, trigger->trigger_modes[mode])) +break ; + +if (mode >= trigger->num_trigger_modes) +return -EINVAL ; + + err = counter->ops->trigger_mode_set (counter, value, trigger, mode); +if (err) +return err; + + trigger->mode = mode; + +return len; +} + +static ssize_t iio_counter_trigger_mode_available_read ( +struct iio_dev *indio_dev,uintptr_t priv, +const struct iio_chan_spec *chan,char *buf) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_value *value; +ssize_t len =0 ; +struct iio_counter_trigger *trigger; +const int signal_id = *(int *)((void *)priv); +unsigned int i; + + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + + trigger =iio_counter_trigger_find_by_id (value, signal_id); +if (!trigger) +return -EINVAL ; + +for (i =0 ; i < trigger->num_trigger_modes; i++) + len +=scnprintf (buf + len,PAGE_SIZE - len,"%s " , + trigger->trigger_modes[i]); + + buf[len -1 ] ='\n' ; + +return len; +} + +static int iio_counter_value_function_set (struct iio_dev *indio_dev, +const struct iio_chan_spec *chan,unsigned int mode) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_value *value; +int err; + +if (!counter->ops->value_function_set) +return -EINVAL ; + +/* Find relevant Value */ + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + +/* Map IIO core function_set to Generic Counter value_function_set */ + err = counter->ops->value_function_set (counter, value, mode); +if (err) +return err; + + value->mode = mode; + +return 0 ; +} + +static int iio_counter_value_function_get (struct iio_dev *indio_dev, +const struct iio_chan_spec *chan)
drivers/iio/industrialio-counter.c line 262
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_value *value; +int retval; + +if (!counter->ops->value_function_get) +return -EINVAL ; + +/* Find relevant Value */ + value =iio_counter_value_find_by_id (counter, chan->channel2);
Same argument as below - would the driver not be able to do this better? Often it would know a simple transform to get the right one...
Quoted 142 lines
drivers/iio/industrialio-counter.c new file +142
-0+if (!value) +return -EINVAL ; + +/* Map IIO core function_get to Generic Counter value_function_get */ + retval = counter->ops->value_function_get (counter, value); +if (retval <0 ) +return retval; +else if (retval >= value->num_function_modes) +return -EINVAL ; + + value->mode = retval; + +return retval; +} + +static int iio_counter_value_ext_info_alloc (struct iio_chan_spec *const chan, +struct iio_counter_value *const value) +{ +const struct iio_chan_spec_ext_info ext_info_default[] = { + { + .name ="name" , + .shared =IIO_SEPARATE , + .read = iio_counter_value_name_read + }, +IIO_ENUM ("function" ,IIO_SEPARATE , &value->function_enum), + { + .name ="function_available" , + .shared =IIO_SEPARATE , + .read = iio_enum_available_read, + .private = (void *)&value->function_enum + }, + { + .name ="triggers" , + .shared =IIO_SEPARATE , + .read = iio_counter_value_triggers_read + } + }; +const size_t num_default =ARRAY_SIZE (ext_info_default); +const struct iio_chan_spec_ext_info ext_info_trigger[] = { + { + .shared =IIO_SEPARATE , + .read = iio_counter_trigger_mode_read, + .write = iio_counter_trigger_mode_write + }, + { + .shared =IIO_SEPARATE , + .read = iio_counter_trigger_mode_available_read + } + }; +const size_t num_ext_info_trigger =ARRAY_SIZE (ext_info_trigger); +const size_t num_triggers_ext_info = num_ext_info_trigger * + value->num_triggers; +const size_t num_ext_info = num_default + num_triggers_ext_info +1 ; +int err; +struct iio_chan_spec_ext_info *ext_info; +const struct iio_counter_trigger *trigger; +size_t i; +size_t j; + +/* Construct function_enum for Value */ + value->function_enum.items = value->function_modes; + value->function_enum.num_items = value->num_function_modes; + value->function_enum.set = iio_counter_value_function_set; + value->function_enum.get = iio_counter_value_function_get; + + ext_info =kmalloc_array (num_ext_info,sizeof (*ext_info),GFP_KERNEL ); +if (!ext_info) +return -ENOMEM ; + ext_info[num_ext_info -1 ].name =NULL ; + +/* Add ext_info for the name, function, function_available, and triggers +* attributes +*/ +memcpy (ext_info, ext_info_default,sizeof (ext_info_default)); +/* Add ext_info for the trigger_signalX-Z and +* trigger_signalX-Z_available attributes for each Trigger +*/ +for (i =0 ; i < num_triggers_ext_info; i += num_ext_info_trigger) +memcpy (ext_info + num_default + i, ext_info_trigger, +sizeof (ext_info_trigger)); + +/* Set name for each trigger_signalX-Z and trigger_signalX-Z_available +* attribute; store the respective Signal ID address in each ext_info +* private member +*/ +for (i = num_default, j =0 ; j < value->num_triggers; i++, j++) { + trigger = value->triggers + j; + + ext_info[i].name =kasprintf (GFP_KERNEL ,"trigger_signal%d-%d" , + chan->channel, trigger->signal->id); +if (!ext_info[i].name) { + err = -ENOMEM ; + goto err_name_alloc; + } + ext_info[i].private = (void *)&trigger->signal->id; + i++; + + ext_info[i].name =kasprintf (GFP_KERNEL , +"trigger_signal%d-%d_available" , + chan->channel, trigger->signal->id); +if (!ext_info[i].name) { + err = -ENOMEM ; + goto err_name_alloc; + } + ext_info[i].private = (void *)&trigger->signal->id; + } + + chan->ext_info = ext_info; + +return 0 ; + +err_name_alloc: +while (i-- > num_default) +kfree (ext_info[i].name); +kfree (ext_info); +return err; +} + +static void iio_counter_value_ext_info_free ( +const struct iio_chan_spec *const channel) +{ +size_t i; +const char *const prefix ="trigger_signal" ; +const size_t prefix_len =strlen (prefix); + +for (i =0 ; channel->ext_info[i].name; i++) +if (!strncmp (channel->ext_info[i].name, prefix, prefix_len)) +kfree (channel->ext_info[i].name); +kfree (channel->ext_info); +} + +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = { + { + .name ="name" , + .shared =IIO_SEPARATE , + .read = iio_counter_signal_name_read + }, + {} +}; + +static int iio_counter_channels_alloc (struct iio_counter *const counter) +{
drivers/iio/industrialio-counter.c line 414
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+const size_t num_channels = counter->num_signals + counter->num_values + + counter->num_channels; +int err; +struct iio_chan_spec *channels; +size_t j; +struct iio_counter_value *value; +size_t i = counter->num_channels; +const struct iio_counter_signal *signal; + + channels =kcalloc (num_channels,sizeof (*channels),GFP_KERNEL );
Bring the calculation of num_channels down here - it will make it more obvious that this allows space for both sources of channel.
Quoted 11 lines
drivers/iio/industrialio-counter.c new file +11
-0+if (!channels) +return -ENOMEM ; + +/* If any channels were supplied on Counter registration, +* we add them here to the front of the array +*/ +memcpy (channels, counter->channels, + counter->num_channels *sizeof (*counter->channels)); + +/* Add channel for each Value */ +for (j =0 ; j < counter->num_values; j++) {
drivers/iio/industrialio-counter.c line 435
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ value = counter->values + j; + + channels[i].type =IIO_COUNT ; + channels[i].channel = counter->id; + channels[i].channel2 = value->id; + channels[i].info_mask_separate =BIT (IIO_CHAN_INFO_RAW ); + channels[i].indexed =1 ; + channels[i].counter =1 ; + +/* Add channels for Triggers associated with Value */
Add channels? 'i' would need incrementing. Also that's not what this function
is doing that I can see...
Quoted 97 lines
drivers/iio/industrialio-counter.c new file +97
-0+ err =iio_counter_value_ext_info_alloc (channels + i, value); +if (err) + goto err_value_ext_info_alloc; + + i++; + } + +/* Add channel for each Signal */ +for (j =0 ; j < counter->num_signals; j++) { + signal = counter->signals + j; + + channels[i].type =IIO_SIGNAL ; + channels[i].channel = counter->id; + channels[i].channel2 = signal->id; + channels[i].info_mask_separate =BIT (IIO_CHAN_INFO_RAW ); + channels[i].indexed =1 ; + channels[i].counter =1 ; + channels[i].ext_info = iio_counter_signal_ext_info; + + i++; + } + + counter->indio_dev->num_channels = num_channels; + counter->indio_dev->channels = channels; + +return 0 ; + +err_value_ext_info_alloc: +while (i-- > counter->num_channels) +iio_counter_value_ext_info_free (channels + i); +kfree (channels); +return err; +} + +static void iio_counter_channels_free (const struct iio_counter *const counter) +{ +size_t i = counter->num_channels + counter->indio_dev->num_channels; +const struct iio_chan_spec *const chans = counter->indio_dev->channels; + +while (i-- > counter->num_channels) +/* Only IIO_COUNT channels need to be freed here */ +if (chans[i].type ==IIO_COUNT ) +iio_counter_value_ext_info_free (chans + i); + +kfree (chans); +} + +static int iio_counter_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan,int *val,int *val2,long mask) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_signal *signal; +struct iio_counter_value *value; + +if (mask !=IIO_CHAN_INFO_RAW ) +return -EINVAL ; + +switch (chan->type) { +/* Map read_raw to signal_read for Signal */ +case IIO_SIGNAL : +if (!counter->ops->signal_read) +return -EINVAL ; + + signal =iio_counter_signal_find_by_id (counter, chan->channel2); +if (!signal) +return -EINVAL ; + +return counter->ops->signal_read (counter, signal, val, val2); +/* Map read_raw to value_read for Value */ +case IIO_COUNT : +if (!counter->ops->value_read) +return -EINVAL ; + + value =iio_counter_value_find_by_id (counter, chan->channel2); +if (!value) +return -EINVAL ; + +return counter->ops->value_read (counter, value, val, val2); +/* Map read_raw to read_raw for non-counter channel */ +default : +if (counter->info && counter->info->read_raw) +return counter->info->read_raw (indio_dev, chan, val, + val2, mask); + } + +return -EINVAL ; +} + +static int iio_counter_write_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan,int val,int val2,long mask) +{ +struct iio_counter *const counter =iio_priv_get_counter (indio_dev); +struct iio_counter_signal *signal; +struct iio_counter_value *value; + +if (mask !=IIO_CHAN_INFO_RAW ) +return -EINVAL ;
drivers/iio/industrialio-counter.c line 542
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ +switch (chan->type) { +/* Map write_raw to signal_write for Signal */ +case IIO_SIGNAL : +if (!counter->ops->signal_write) +return -EINVAL ; + + signal =iio_counter_signal_find_by_id (counter, chan->channel2); +if (!signal) +return -EINVAL ;
Hmm. have to search is certainly a little ugly. Particularly as the driver would be able to maintain this as a lookup. I'd be tempted to pass the signal id down into the callback rather than finding the signal in the core. Might turn out messier though... :)
drivers/iio/industrialio-counter.c line 550
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +return counter->ops->signal_write (counter, signal, val, val2); +/* Map write_raw to value_write for Value */ +case IIO_COUNT : +if (!counter->ops->value_write) +return -EINVAL ; + + value =iio_counter_value_find_by_id (counter, chan->channel2);
Again, this mapping is a simple lookup in the driver I think, perhaps push it down there?
Quoted 36 lines
drivers/iio/industrialio-counter.c new file +36
-0+if (!value) +return -EINVAL ; + +return counter->ops->value_write (counter, value, val, val2); +/* Map write_raw to write_raw for non-counter channel */ +default : +if (counter->info && counter->info->write_raw) +return counter->info->write_raw (indio_dev, chan, val, + val2, mask); + } + +return -EINVAL ; +} + +static int iio_counter_signals_validate (const struct iio_counter *const counter) +{ +size_t i; +const struct iio_counter_signal *signal; +size_t j; +int curr_id; + +/* At least one Signal must be defined */ +if (!counter->num_signals || !counter->signals) { +pr_err ("Counter '%d' Signals undefined\n" , counter->id); +return -EINVAL ; + } + +for (i =0 ; i < counter->num_signals; i++) { + signal = counter->signals + i; +/* No two Signals may share the same ID */ +for (j =0 ; j < i; j++) { + curr_id = counter->signals[j].id; +if (curr_id == signal->id) { +pr_err ("Duplicate Counter '%d' Signal '%d'\n" , + counter->id, signal->id); +return -EEXIST ;
drivers/iio/industrialio-counter.c line 596
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ } + } +for (j = i +1 ; j < counter->num_signals; j++) { + curr_id = counter->signals[j].id; +if (curr_id == signal->id) { +pr_err ("Duplicate Counter '%d' Signal '%d'\n" , + counter->id, signal->id); +return -EEXIST ; + } + }
Same as below.
Quoted 12 lines
drivers/iio/industrialio-counter.c new file +12
-0+ } + +return 0 ; +} + +static int iio_counter_triggers_validate (const int counter_id, +const struct iio_counter_value *const value) +{ +size_t i; +const struct iio_counter_trigger *trigger; +size_t j; +int curr_id;
drivers/iio/industrialio-counter.c line 618
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ +/* At least one Trigger must be defined */ +if (!value->num_triggers || !value->triggers) { +pr_err ("Counter '%d' Value '%d' Triggers undefined\n" , + counter_id, value->id); +return -EINVAL ; + } + +/* Ensure all Triggers have a defined Signal; this prevents a possible +* NULL pointer dereference when later validating Trigger Signal IDs
Fix comment syntax throughout...
Quoted 20 lines
drivers/iio/industrialio-counter.c new file +20
-0+*/ +for (i =0 ; i < value->num_triggers; i++) +if (!value->triggers[i].signal) { +pr_err ("Counter '%d' Trigger '%zu' Signal undefined\n" , + counter_id, i); +return -EINVAL ; + } + +/* Verify validity of each Trigger */ +for (i =0 ; i < value->num_triggers; i++) { + trigger = value->triggers + i; +/* No two Trigger Signals may share the same ID */ +for (j =0 ; j < i; j++) { + curr_id = value->triggers[j].signal->id; +if (curr_id == trigger->signal->id) { +pr_err ("Signal '%d' is already linked to Counter '%d' Value '%d'\n" , + trigger->signal->id, counter_id, + value->id); +return -EEXIST ; + }
drivers/iio/industrialio-counter.c line 648
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ } +for (j = i +1 ; j < value->num_triggers; j++) { + curr_id = value->triggers[j].signal->id; +if (curr_id == trigger->signal->id) { +pr_err ("Signal '%d' is already linked to Counter '%d' Value '%d'\n" , + trigger->signal->id, counter_id, + value->id); +return -EEXIST ; + } + }
Again, one loop with the condition changed so it doesn't fault on the i = j case -- see below and note I'm reviewing backwards...
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ +/* At least one trigger mode must be defined for each Trigger */ +if (!trigger->num_trigger_modes || !trigger->trigger_modes) { + pr_err("Counter '%d' Signal '%d' trigger modes undefined\n",
drivers/iio/industrialio-counter.c line 662
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ counter_id, trigger->signal->id); +return -EINVAL ; + } + } + +return 0 ; +} + +static int iio_counter_values_validate (const struct iio_counter *const counter) +{
Hmm. Pretty heavy weight checking. Ah well, up to you.
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7
-0+size_t i; +const struct iio_counter_value *value; +size_t j; +int curr_id; +int err; + +/* At least one Value must be defined */
drivers/iio/industrialio-counter.c line 679
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+if (!counter->num_values || !counter->values) { +pr_err ("Counter '%d' Values undefined\n" , counter->id); +return -EINVAL ; + } + +/* Verify validity of each Value */ +for (i =0 ; i < counter->num_values; i++) { + value = counter->values + i; +/* No two Values may share the same ID */ +for (j =0 ; j < i; j++) {
Single loop with a slightly change to condition
if ((i != j) and (curr_id == value->id))
Quoted 52 lines
drivers/iio/industrialio-counter.c new file +52
-0+ curr_id = counter->values[j].id; +if (curr_id == value->id) { +pr_err ("Duplicate Counter '%d' Value '%d'\n" , + counter->id, value->id); +return -EEXIST ; + } + } +for (j = i +1 ; j < counter->num_values; j++) { + curr_id = counter->values[j].id; +if (curr_id == value->id) { +pr_err ("Duplicate Counter '%d' Value '%d'\n" , + counter->id, value->id); +return -EEXIST ; + } + } + +/* At least one function mode must be defined for each Value */ +if (!value->num_function_modes || !value->function_modes) { +pr_err ("Counter '%d' Value '%d' function modes undefined\n" , + counter->id, value->id); +return -EINVAL ; + } + +/* Verify the Triggers associated with each Value */ + err =iio_counter_triggers_validate (counter->id, value); +if (err) +return err; + } + +return 0 ; +} + +/** +* iio_counter_register - register Counter to the system +* @counter: pointer to IIO Counter to register +* +* This function piggybacks off of iio_device_register. First, the relevant +* Counter members are validated; the signals and values members must be defined +* and populated by valid Signal and Value structures respectively. Next, a +* struct iio_dev is allocated by a call to iio_device_alloc and initialized for +* the Counter, IIO channels are allocated, the Counter address is stored as the +* private data, and finally iio_device_register is called. +*/ +int iio_counter_register (struct iio_counter *const counter) +{ +const struct iio_info info_default = { + .read_raw = iio_counter_read_raw, + .write_raw = iio_counter_write_raw + }; +int err; +struct iio_info *info; +struct iio_counter **priv;
drivers/iio/industrialio-counter.c line 741
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ +if (!counter) +return -EINVAL ; + +/* Verify that Signals are valid and IDs do not conflict */ + err =iio_counter_signals_validate (counter); +if (err) +return err; + +/* Verify that Values are valid and IDs do not conflict;
Fix multiline comment syntax to standard kernel syntax. /*
* Verify
*/
Quoted 201 lines
drivers/iio/industrialio-counter.c new file +159
-0+* Triggers for each Value are also verified for validity +*/ + err =iio_counter_values_validate (counter); +if (err) +return err; + + counter->indio_dev =iio_device_alloc (sizeof (counter)); +if (!counter->indio_dev) +return -ENOMEM ; + + info =kmalloc (sizeof (*info),GFP_KERNEL ); +if (!info) { + err = -ENOMEM ; + goto err_info_alloc; + } +/* If an iio_info has been supplied than we use that, +* otherwise we set all callbacks to NULL; iio_counter_read_raw +* and iio_counter_write_raw is used for read_raw and write_raw +* for either case in order to support counter functionality +* (supplied read_raw/write_raw will be called from within +* iio_counter_read_raw/iio_counter_write_raw for non-counter +* channels) +*/ +if (counter->info) { +memcpy (info, counter->info,sizeof (*counter->info)); + info->read_raw = iio_counter_read_raw; + info->write_raw = iio_counter_write_raw; + }else { +memcpy (info, &info_default,sizeof (info_default)); + } + + counter->indio_dev->info = info; + counter->indio_dev->modes =INDIO_DIRECT_MODE ; + counter->indio_dev->name = counter->name; + counter->indio_dev->dev.parent = counter->dev; + +/* IIO channels are allocated and set for Signals, Values, and Triggers; +* any auxiliary IIO channels provided in iio_counter are also set +*/ + err =iio_counter_channels_alloc (counter); +if (err) + goto err_channels_alloc; + +/* Pointer to the counter is stored in indio_dev as a way to refer +* back to the counter from within various IIO callbacks +*/ + priv =iio_priv (counter->indio_dev); +memcpy (priv, &counter,sizeof (*priv)); + + err =iio_device_register (counter->indio_dev); +if (err) + goto err_iio_device_register; + +return 0 ; + +err_iio_device_register: +iio_counter_channels_free (counter); +err_channels_alloc: +kfree (info); +err_info_alloc: +iio_device_free (counter->indio_dev); +return err; +} +EXPORT_SYMBOL (iio_counter_register); + +/** +* iio_counter_unregister - unregister Counter from the system +* @counter: pointer to IIO Counter to unregister +* +* The Counter is unregistered from the system. The indio_dev is unregistered +* and all allocated memory is freed. +*/ +void iio_counter_unregister (struct iio_counter *const counter) +{ +const struct iio_info *const info = counter->indio_dev->info; + +if (!counter) +return ; + +iio_device_unregister (counter->indio_dev); + +iio_counter_channels_free (counter); + +kfree (info); +iio_device_free (counter->indio_dev); +} +EXPORT_SYMBOL (iio_counter_unregister); + +static void devm_iio_counter_unreg (struct device *dev,void *res) +{ +iio_counter_unregister (*(struct iio_counter **)res); +} + +/** +* devm_iio_counter_register - Resource-managed iio_counter_register +* @dev: Device to allocate iio_counter for +* @counter: pointer to IIO Counter to register +* +* Managed iio_counter_register. The IIO counter registered with this +* function is automatically unregistered on driver detach. This function +* calls iio_counter_register internally. Refer to that function for more +* information. +* +* If an iio counter registered with this function needs to be unregistered +* separately, devm_iio_counter_unregister must be used. +* +* RETURNS: +* 0 on success, negative error number on failure. +*/ +int devm_iio_counter_register (struct device *dev, +struct iio_counter *const counter) +{ +struct iio_counter **ptr; +int ret; + + ptr =devres_alloc (devm_iio_counter_unreg,sizeof (*ptr),GFP_KERNEL ); +if (!ptr) +return -ENOMEM ; + + *ptr = counter; + ret =iio_counter_register (counter); +if (!ret) +devres_add (dev, ptr); +else +devres_free (ptr); + +return ret; +} +EXPORT_SYMBOL (devm_iio_counter_register); + +static int devm_iio_counter_match (struct device *dev,void *res,void *data) +{ +struct iio_counter **r = res; + +if (!r || !*r) { +WARN_ON (!r || !*r); +return 0 ; + } + +return *r == data; +} + +/** +* devm_iio_counter_unregister - Resource-managed iio_counter_unregister +* @dev: Device this iio_counter belongs to +* @counter: the iio counter associated with the device +* +* Unregister iio counter registered with devm_iio_counter_register. +*/ +void devm_iio_counter_unregister (struct device *dev, +struct iio_counter *const counter) +{ +int rc; + + rc =devres_release (dev, devm_iio_counter_unreg, + devm_iio_counter_match, counter); +WARN_ON (rc); +} +EXPORT_SYMBOL (devm_iio_counter_unregister);include/linux/iio/counter.h new file +35
-0@@ -0,0 +1,166 @@ +/* +* Industrial I/O counter interface +* Copyright (C) 2017 William Breathitt Gray +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, version 2, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ +#ifndef _IIO_COUNTER_H_ +#define _IIO_COUNTER_H_ + +#ifdef CONFIG_IIO_COUNTER + +#include <linux/device.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +/** +* struct iio_counter_signal - IIO Counter Signal node +* @id: [DRIVER] unique ID used to identify signal +* @name: [DRIVER] device-specific signal name +*/ +struct iio_counter_signal { +int id; +const char *name; +}; + +/** +* struct iio_counter_trigger - IIO Counter Trigger node
include/linux/iio/counter.h line 45
Quoted 10 lines
include/linux/iio/counter.h new file +10
-0+* @mode: [DRIVER] current trigger mode state +* @trigger_modes: [DRIVER] available trigger modes +* @num_trigger_modes: [DRIVER] number of modes specified in @trigger_modes +* @signal: [DRIVER] pointer to associated signal +*/ +struct iio_counter_trigger { +unsigned int mode; +const char *const *trigger_modes; +unsigned int num_trigger_modes; +struct iio_counter_signal *signal;
Are signals actually changeable? - Or is that pointer constant?
Quoted 24 lines
include/linux/iio/counter.h new file +24
-0+}; + +/** +* struct iio_counter_value - IIO Counter Value node +* @id: [DRIVER] unique ID used to identify value +* @name: [DRIVER] device-specific value name +* @mode: [DRIVER] current function mode state +* @function_modes: [DRIVER] available function modes +* @num_function_modes: [DRIVER] number of modes specified in @function_modes +* @triggers: [DRIVER] array of triggers for initialization +* @num_triggers: [DRIVER] number of triggers specified in @triggers +* @function_enum: [INTERN] used internally to generate function attributes +*/ +struct iio_counter_value { +int id; +const char *name; +unsigned int mode; +const char *const *function_modes; +unsigned int num_function_modes; + +struct iio_counter_trigger *triggers; +size_t num_triggers; + +struct iio_enum function_enum;
include/linux/iio/counter.h line 79
Quoted 10 lines
include/linux/iio/counter.h new file +10
-0+}; + +struct iio_counter ; + +/** +* struct iio_counter_ops - IIO Counter related callbacks +* @signal_read: function to request a signal value from the device. +* Return value will specify the type of value returned by +* the device. val and val2 will contain the elements +* making up the returned value.
Add some detail on the importance of the return value.
include/linux/iio/counter.h line 81
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* @signal_write: function to write a signal value to the device. +* Parameters are interpreted the same as signal_read.
You need a means of establishing the correct format for write (we needed it in IIO fairly early on too)
Quoted 47 lines
include/linux/iio/counter.h new file +47
-0+* @trigger_mode_set: function to set the trigger mode. mode is the index of +* the requested mode from the value trigger_modes array. +* @trigger_mode_get: function to get the current trigger mode. Return value +* will specify the index of the current mode from the +* value trigger_modes array. +* @value_read: function to request a value value from the device. +* Return value will specify the type of value returned by +* the device. val and val2 will contain the elements +* making up the returned value. +* @value_write: function to write a value value to the device. +* Parameters are interpreted the same as value_read. +* @value_function_set: function to set the value function mode. mode is the +* index of the requested mode from the value +* function_modes array. +* @value_function_get: function to get the current value function mode. Return +* value will specify the index of the current mode from +* the value function_modes array. +*/ +struct iio_counter_ops { +int (*signal_read)(struct iio_counter *counter, +struct iio_counter_signal *signal,int *val,int *val2); +int (*signal_write)(struct iio_counter *counter, +struct iio_counter_signal *signal,int val,int val2); +int (*trigger_mode_set)(struct iio_counter *counter, +struct iio_counter_value *value, +struct iio_counter_trigger *trigger,unsigned int mode); +int (*trigger_mode_get)(struct iio_counter *counter, +struct iio_counter_value *value, +struct iio_counter_trigger *trigger); +int (*value_read)(struct iio_counter *counter, +struct iio_counter_value *value,int *val,int *val2); +int (*value_write)(struct iio_counter *counter, +struct iio_counter_value *value,int val,int val2); +int (*value_function_set)(struct iio_counter *counter, +struct iio_counter_value *value,unsigned int mode); +int (*value_function_get)(struct iio_counter *counter, +struct iio_counter_value *value); +}; + +/** +* struct iio_counter - IIO Counter data structure +* @id: [DRIVER] unique ID used to identify counter +* @name: [DRIVER] name of the device +* @dev: [DRIVER] device structure, should be assigned a parent +* and owner +* @ops: [DRIVER] callbacks from driver for counter components +* @signals: [DRIVER] array of signals for initialization
include/linux/iio/counter.h line 138
Quoted 10 lines
include/linux/iio/counter.h new file +10
-0+* @num_signals: [DRIVER] number of signals specified in @signals +* @values: [DRIVER] array of values for initialization +* @num_values: [DRIVER] number of values specified in @values +* @channels: [DRIVER] channel specification structure table +* @num_channels: [DRIVER] number of channels specified in @channels +* @info: [DRIVER] callbacks and constant info from driver +* @indio_dev: [INTERN] industrial I/O device structure +* @driver_data: [DRIVER] driver data +*/ +struct iio_counter {
Mentioned earlier - I think naming the device counter, is confusing. The counters should be named counter - call it iio_counter_device or iio_counter_group or something and keep counter for the things currently called value.
Quoted 28 lines
include/linux/iio/counter.h new file +28
-0+int id; +const char *name; +struct device *dev; +const struct iio_counter_ops *ops; + +struct iio_counter_signal *signals; +size_t num_signals; +struct iio_counter_value *values; +size_t num_values; + +const struct iio_chan_spec *channels; +size_t num_channels; +const struct iio_info *info; + +struct iio_dev *indio_dev; +void *driver_data; +}; + +int iio_counter_register (struct iio_counter *const counter); +void iio_counter_unregister (struct iio_counter *const counter); +int devm_iio_counter_register (struct device *dev, +struct iio_counter *const counter); +void devm_iio_counter_unregister (struct device *dev, +struct iio_counter *const counter); + +#endif /* CONFIG_IIO_COUNTER */ + +#endif /* _IIO_COUNTER_H_ */
2017-10-08 16:30 GMT+02:00 Jonathan Cameron <[email protected]>:
Quoted 62 lines
Quoted 6 lines · On Thu, 5 Oct 2017 14:13:44 -0400 William Breathitt Gray…
This patch introduces the IIO generic counter interface for supporting counter devices. The generic counter interface serves as a catch-all to enable rudimentary support for devices that qualify as counters. More specific and apt counter interfaces may be developed on top of the generic counter interface, and those interfaces should be used by drivers when possible rather than the generic counter interface.
I think you probably want to avoid any additional layering and try to push things down here (maybe with a few utility functions to simplify common cases), but that's a question for another day!
Quoted 34 lines
In the context of the IIO generic counter interface, a counter is defined as a device that reports one or more "counter values" based on the state changes of one or more "counter signals" as evaluated by a defined "counter function."
The IIO generic counter interface piggybacks off of the IIO core. This is primarily used to leverage the existing sysfs setup: the
IIO_COUNTchannel attributes represent the Counter Value, while theIIO_SIGNALchannel attributes represent the Counter Signal; auxilaryIIO_COUNTattributes represent the Counter Signal connections (Triggers) and their respective state change configurations which trigger an associated "counter function" evaluation.The
iio_counter_opsstructure serves as a container for driver callbacks to communicate with the device; function callbacks are provided to read and write various Signals and Values, and set and get the "trigger mode" and "function mode" for various Triggers and Values respectively.To support a counter device, a driver must first allocate the available Counter Signals via
iio_counter_signalstructures. These Signals should be stored as an array and set to the signals array member of an allocatediio_counterstructure before the Counter is registered to the system.Counter Values may be allocated via
iio_counter_valuestructures, and respective Counter Signal associations (Triggers) made viaiio_counter_triggerstructures. Associatediio_counter_triggerstructures are stored as an array and set to the the triggers array member of the respectiveiio_counter_valuestructure. Theseiio_counter_valuestructures are set to the values array member of an allocatediio_counterstructure before the Counter is registered to the system.Quoted 7 lines
A counter device is registered to the system by passing the respective initialized
iio_counterstructure to theiio_counter_registerfunction; similarly, theiio_counter_unregisterfunction unregisters the respective counter. Thedevm_iio_counter_registerandiio_devm_iio_counter_unregisterfunctions serve as device memory-managed versions of theiio_counter_registerandiio_counterunregister functions respectively.All the find by ids are the main complexity introduced by layering this on IIO that you probably wouldn't have if it wasn't so layered. Hmm. I suggest inline that you could allow drivers to provide fast versions of that search given they can probably infer it directly from the index. Might be something to ignore for now though in the interests of initial simplicity.
Quoted 8 lines
Otherwise a few comments inline, but mostly this is coming together pretty well. I'm actually surprised the layering on top of IIO didn't end up more painful than it did. It's not horrendous - which is not to say you wouldn't be better breaking free of that entirely... (I'm not sure either way).
I think the big remaining questions are around naming more than anything else. I don't like the name value when a user will think they are lookin=
g
Quoted 1 line
for a counter. If you use counter though the current use of counter need=
s
Quoted 3 lines
to change.
Trigger is also, as has been raised, a rather overloaded term. Not totall=
y
Quoted 13 lines
obvious what a better term is but we shouldn't reuse that one...
I'll try and think of something if you have no luck!
Jonathan
Quoted 8 lines
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| 900 ++++++++++++++++++++++++++++++=
+++++++
Quoted 10 lines
MAINTAINERS F: Documentation/ABI/testing/sysfs-bus-iio-ad=
Quoted 10 lines
include/linux/iio/counter.h | 166 +++++++ 6 files changed, 1083 insertions(+) create mode 100644 drivers/iio/industrialio-counter.c create mode 100644 include/linux/iio/counter.hMAINTAINERS +0
-0@@ -6693,6 +6693,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-ad=
c-envelope-detector
Quoted 54 lines
MAINTAINERS F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector · line 6701
Quoted 9 lines
MAINTAINERS +6
-0F: 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.hDon't forget the directory the drivers are going in ;) *laughs manically*
Quoted 42 lines
MAINTAINERS +1
-0+ IIO MULTIPLEXER M: Peter Rosin <[email protected]> L: [email protected]drivers/iio/Kconfig +8
-0@@ -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" helpdrivers/iio/Makefile +1
-0@@ -5,6 +5,7 @@ obj-$(CONFIG_IIO) +=3D industrialio.o industrialio-y :=3D industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) +=3D industrialio-buffer.o +industrialio-$(CONFIG_IIO_COUNTER) +=3D industrialio-counter.o industrialio-$(CONFIG_IIO_TRIGGER) +=3D industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) +=3D industrialio-configfs.odrivers/iio/counter/Kconfig +0
-0@@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order
Quoted 6 lines
drivers/iio/industrial=
Quoted 6 lines
drivers/iio/counter/Kconfig +1
-0menu "Counters" + depends on IIO_COUNTER config 104_QUAD_8 tristate "ACCES 104-QUAD-8 driver"drivers/iio/industrial= renamed from drivers/iio/industrialio-counter.c +0
-0
io-counter.c
Quoted 78 lines
Quoted 60 lines
new file mode 100644
index 000000000000..dfb982dae3a8drivers/iio/industrialio-counter.c new file +55
-0@@ -0,0 +1,900 @@ +/* +* Industrial I/O counter interface functions +* Copyright (C) 2017 William Breathitt Gray +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, version 2, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#include <linux/iio/counter.h> + +#define iio_priv_get_counter (_indio_dev) \ + (*(struct iio_counter **)iio_priv(_indio_dev)) + +static struct iio_counter_signal *iio_counter_signal_find_by_id ( +const struct iio_counter *const counter,const int id) +{ +size_t i; +struct iio_counter_signal *signal; + + for (i =3D 0; i < counter->num_signals; i++) { + signal =3D counter->signals + i; + if (signal->id =3D=3D id) +return signal; + } + +return NULL ; +} + +static struct iio_counter_trigger *iio_counter_trigger_find_by_id ( +const struct iio_counter_value *const value,const int id) +{ +size_t i; +struct iio_counter_trigger *trigger; + + for (i =3D 0; i < value->num_triggers; i++) { + trigger =3D value->triggers + i; + if (trigger->signal->id =3D=3D id) +return trigger;drivers/iio/industrialio-counter.c line 65
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ } + +return NULL ; +} + +static struct iio_counter_value *iio_counter_value_find_by_id ( +const struct iio_counter *const counter,const int id) +{ +size_t i; +struct iio_counter_value *value;In most cases this mapping will be entirely obvious to the driver - perhaps provide an optional callback to let it provide it directly without any searching?
We could always add this later though if we start getting drivers with lots of instances of the various parts...
Quoted 14 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ + for (i =3D 0; i < counter->num_values; i++) { + value =3D counter->values + i; + if (value->id =3D=3D id)drivers/iio/industrialio-counter.c
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+return value; + } + +return NULL ; +} + +static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+const struct iio_counter_signal *signal; + + signal =3D iio_counter_signal_find_by_id(counter, chan->channel2); +if (!signal) +return -EINVAL ;
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +return scnprintf (buf,PAGE_SIZE ,"%s\n" , signal->name); +} + +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 12 lines
drivers/iio/industrialio-counter.c line 99
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7
-0+const struct iio_counter_value *value; + + value =3D iio_counter_value_find_by_id(counter, chan->channel2); +if (!value) +return -EINVAL ; + +return scnprintf (buf,PAGE_SIZE ,"%s\n" , value->name);I mentioned before but I wonder if we can just push name down into the chan spec itself in some fashion. Whether to associate it with existing
datasheet_nameor not is an open question however.
Quoted 3 lines
drivers/iio/industrialio-counter.c
Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+} + +static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_de=
v,
Quoted 3 lines
drivers/iio/industrialio-counter.c
Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+struct iio_counter_value *value; +size_t i; +const struct iio_counter_trigger *trigger; + ssize_t len =3D 0; +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ value =3D iio_counter_value_find_by_id(counter, chan->channel2); +if (!value) +return -EINVAL ; + +/* Print out a list of every Signal association to Value */ + for (i =3D 0; i < value->num_triggers; i++) { + trigger =3D value->triggers + i; + len +=3D snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\=
n",
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+ trigger->signal->id, trigger->signal->name, + trigger->trigger_modes[trigger->mode]); + if (len >=3D PAGE_SIZE) +return -ENOMEM ; + }
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +return len; +} + +static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 18 lines
Quoted 18 lines
drivers/iio/industrialio-counter.c new file +18
-0+struct iio_counter_value *value; +struct iio_counter_trigger *trigger; +const int signal_id =3D *(int *)((void *)priv); +int mode; + +if (!counter->ops->trigger_mode_get) +return -EINVAL ; + + value =3D iio_counter_value_find_by_id(counter, chan->channel2); +if (!value) +return -EINVAL ; + + trigger =3D iio_counter_trigger_find_by_id(value, signal_id); +if (!trigger) +return -EINVAL ; + + mode =3D counter->ops->trigger_mode_get(counter, value, trigger); +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+if (mode <0 ) +return mode; + else if (mode >=3D trigger->num_trigger_modes) +return -EINVAL ; + + trigger->mode =3D mode; + + return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mo=
de]);
Quoted 3 lines
drivers/iio/industrialio-counter.c
Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+} + +static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev=
,
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ uintptr_t priv, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 17 lines
Quoted 17 lines
drivers/iio/industrialio-counter.c new file +17
-0+struct iio_counter_value *value; +ssize_t err; +struct iio_counter_trigger *trigger; +const int signal_id =3D *(int *)((void *)priv); +unsigned int mode; + +if (!counter->ops->trigger_mode_set) +return -EINVAL ; + + value =3D iio_counter_value_find_by_id(counter, chan->channel2); +if (!value) +return -EINVAL ; + + trigger =3D iio_counter_trigger_find_by_id(value, signal_id); +if (!trigger) +return -EINVAL ; +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ for (mode =3D 0; mode < trigger->num_trigger_modes; mode++) +if (sysfs_streq (buf, trigger->trigger_modes[mode])) +break ; + + if (mode >=3D trigger->num_trigger_modes) +return -EINVAL ; + + err =3D counter->ops->trigger_mode_set(counter, value, trigger, mo=
de);
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+if (err) +return err; + + trigger->mode =3D mode; +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+return len; +} + +static ssize_t iio_counter_trigger_mode_available_read( + struct iio_dev *indio_dev, uintptr_t priv, + const struct iio_chan_spec *chan, char *buf) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 19 lines
Quoted 19 lines
drivers/iio/industrialio-counter.c new file +19
-0+struct iio_counter_value *value; + ssize_t len =3D 0; +struct iio_counter_trigger *trigger; +const int signal_id =3D *(int *)((void *)priv); +unsigned int i; + + value =3D iio_counter_value_find_by_id(counter, chan->channel2); +if (!value) +return -EINVAL ; + + trigger =3D iio_counter_trigger_find_by_id(value, signal_id); +if (!trigger) +return -EINVAL ; + + for (i =3D 0; i < trigger->num_trigger_modes; i++) + len +=3D scnprintf(buf + len, PAGE_SIZE - len, "%s ", + trigger->trigger_modes[i]); + + buf[len - 1] =3D '\n';
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +return len; +} + +static int iio_counter_value_function_set(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 4 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+struct iio_counter_value *value; +int err; + +if (!counter->ops->value_function_set)
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+return -EINVAL ; + +/* Find relevant Value */ + value =3D iio_counter_value_find_by_id(counter, chan->channel2); +if (!value) +return -EINVAL ; + +/* Map IIO core function_set to Generic Counter value_function_set=
*/
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+ err =3D counter->ops->value_function_set(counter, value, mode); +if (err) +return err; + + value->mode =3D mode;
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +return 0 ; +} + +static int iio_counter_value_function_get(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 17 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+struct iio_counter_value *value; +int retval; + +if (!counter->ops->value_function_get) +return -EINVAL ; + +/* Find relevant Value */ + value =3D iio_counter_value_find_by_id(counter, chan->channel2);Same argument as below - would the driver not be able to do this better? Often it would know a simple transform to get the right one...
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+if (!value) +return -EINVAL ; + +/* Map IIO core function_get to Generic Counter value_function_get=
*/
Quoted 4 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ retval =3D counter->ops->value_function_get(counter, value); +if (retval <0 ) +return retval; + else if (retval >=3D value->num_function_modes)
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+return -EINVAL ; + + value->mode =3D retval; + +return retval; +} + +static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const=
chan,
Quoted 26 lines
Quoted 26 lines
drivers/iio/industrialio-counter.c new file +26
-0+ struct iio_counter_value *const value) +{ + const struct iio_chan_spec_ext_info ext_info_default[] =3D { + { + .name =3D "name", + .shared =3D IIO_SEPARATE, + .read =3D iio_counter_value_name_read + }, +IIO_ENUM ("function" ,IIO_SEPARATE , &value->function_enum), + { + .name =3D "function_available", + .shared =3D IIO_SEPARATE, + .read =3D iio_enum_available_read, + .private =3D (void *)&value->function_enum + }, + { + .name =3D "triggers", + .shared =3D IIO_SEPARATE, + .read =3D iio_counter_value_triggers_read + } + }; + const size_t num_default =3D ARRAY_SIZE(ext_info_default); + const struct iio_chan_spec_ext_info ext_info_trigger[] =3D { + { + .shared =3D IIO_SEPARATE, + .read =3D iio_counter_trigger_mode_read,
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ .write =3D iio_counter_trigger_mode_write + }, + { + .shared =3D IIO_SEPARATE, + .read =3D iio_counter_trigger_mode_available_read + } + }; + const size_t num_ext_info_trigger =3D ARRAY_SIZE(ext_info_trigger)=
;
Quoted 3 lines
drivers/iio/industrialio-counter.c line 315
Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+ const size_t num_triggers_ext_info =3D num_ext_info_trigger * + value->num_triggers; + const size_t num_ext_info =3D num_default + num_triggers_ext_info =
+ 1;
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+int err; +struct iio_chan_spec_ext_info *ext_info; +const struct iio_counter_trigger *trigger; +size_t i; +size_t j;
Quoted 8 lines
drivers/iio/industrialio-counter.c line 328
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +/* Construct function_enum for Value */ + value->function_enum.items =3D value->function_modes; + value->function_enum.num_items =3D value->num_function_modes; + value->function_enum.set =3D iio_counter_value_function_set; + value->function_enum.get =3D iio_counter_value_function_get; + + ext_info =3D kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KE=
RNEL);
Quoted 5 lines
drivers/iio/industrialio-counter.c
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+if (!ext_info) +return -ENOMEM ; + ext_info[num_ext_info - 1].name =3D NULL; + +/* Add ext_info for the name, function, function_available, and tr=
iggers
Quoted 7 lines
drivers/iio/industrialio-counter.c
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7
-0+* attributes +*/ +memcpy (ext_info, ext_info_default,sizeof (ext_info_default)); +/* Add ext_info for the trigger_signalX-Z and +* trigger_signalX-Z_available attributes for each Trigger +*/ + for (i =3D 0; i < num_triggers_ext_info; i +=3D num_ext_info_trigg=
er)
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+memcpy (ext_info + num_default + i, ext_info_trigger, +sizeof (ext_info_trigger)); + +/* Set name for each trigger_signalX-Z and trigger_signalX-Z_avail=
able
Quoted 1 line
drivers/iio/industrialio-counter.c line 345
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+* attribute; store the respective Signal ID address in each ext_i=
nfo
Quoted 3 lines
drivers/iio/industrialio-counter.c
Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+* private member +*/ + for (i =3D num_default, j =3D 0; j < value->num_triggers; i++, j++=
) {
Quoted 3 lines
drivers/iio/industrialio-counter.c line 351
Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+ trigger =3D value->triggers + j; + + ext_info[i].name =3D kasprintf(GFP_KERNEL, "trigger_signal=
%d-%d",
Quoted 30 lines
Quoted 30 lines
drivers/iio/industrialio-counter.c new file +30
-0+ chan->channel, trigger->signal->id); +if (!ext_info[i].name) { + err =3D -ENOMEM ; + goto err_name_alloc; + } + ext_info[i].private =3D (void *)&trigger->signal->id; + i++; + + ext_info[i].name =3D kasprintf(GFP_KERNEL, +"trigger_signal%d-%d_available" , + chan->channel, trigger->signal->id); +if (!ext_info[i].name) { + err =3D -ENOMEM ; + goto err_name_alloc; + } + ext_info[i].private =3D (void *)&trigger->signal->id; + } + + chan->ext_info =3D ext_info; + +return 0 ; + +err_name_alloc: +while (i-- > num_default) +kfree (ext_info[i].name); +kfree (ext_info); +return err; +} + +static void iio_counter_value_ext_info_free(
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ const struct iio_chan_spec *const channel) +{ + size_t i; + const char *const prefix =3D "trigger_signal"; + const size_t prefix_len =3D strlen(prefix); + + for (i =3D 0; channel->ext_info[i].name; i++) + if (!strncmp(channel->ext_info[i].name, prefix, prefix_len=
))
Quoted 5 lines
drivers/iio/industrialio-counter.c
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+kfree (channel->ext_info[i].name); +kfree (channel->ext_info); +} + +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[=
] =3D {
Quoted 9 lines
drivers/iio/industrialio-counter.c
Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9
-0+ { + .name =3D "name", + .shared =3D IIO_SEPARATE, + .read =3D iio_counter_signal_name_read + }, + {} +}; + +static int iio_counter_channels_alloc(struct iio_counter *const counter=
)
Quoted 2 lines
drivers/iio/industrialio-counter.c line 405
Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2
-0+{ + const size_t num_channels =3D counter->num_signals + counter->num_=
values +
Quoted 27 lines
drivers/iio/industrialio-counter.c
Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9
-0+ counter->num_channels; +int err; +struct iio_chan_spec *channels; +size_t j; +struct iio_counter_value *value; + size_t i =3D counter->num_channels; +const struct iio_counter_signal *signal; + + channels =3D kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);Bring the calculation of
num_channelsdown here - it will make it more obvious that this allows space for both sources of channel.Quoted 14 lines
drivers/iio/industrialio-counter.c new file +14
-0+if (!channels) +return -ENOMEM ; + +/* If any channels were supplied on Counter registration, +* we add them here to the front of the array +*/ +memcpy (channels, counter->channels, + counter->num_channels *sizeof (*counter->channels)); + +/* Add channel for each Value */ + for (j =3D 0; j < counter->num_values; j++) { + value =3D counter->values + j; + + channels[i].type =3D IIO_COUNT;
Quoted 8 lines
drivers/iio/industrialio-counter.c line 435
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7
-0+ channels[i].channel =3D counter->id; + channels[i].channel2 =3D value->id; + channels[i].info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW); + channels[i].indexed =3D 1; + channels[i].counter =3D 1; + +/* Add channels for Triggers associated with Value */ Add channels? 'i' would need incrementing. Also that's not what this f=
unction
Quoted 2 lines
is doing that I can see...
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+ err =3D iio_counter_value_ext_info_alloc(channels + i, val=
ue);
Quoted 26 lines
Quoted 26 lines
drivers/iio/industrialio-counter.c new file +26
-0+if (err) + goto err_value_ext_info_alloc; + + i++; + } + +/* Add channel for each Signal */ + for (j =3D 0; j < counter->num_signals; j++) { + signal =3D counter->signals + j; + + channels[i].type =3D IIO_SIGNAL; + channels[i].channel =3D counter->id; + channels[i].channel2 =3D signal->id; + channels[i].info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW); + channels[i].indexed =3D 1; + channels[i].counter =3D 1; + channels[i].ext_info =3D iio_counter_signal_ext_info; + + i++; + } + + counter->indio_dev->num_channels =3D num_channels; + counter->indio_dev->channels =3D channels; + +return 0 ; +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+err_value_ext_info_alloc: +while (i-- > counter->num_channels) +iio_counter_value_ext_info_free (channels + i); +kfree (channels); +return err; +} + +static void iio_counter_channels_free(const struct iio_counter *const c=
ounter)
Quoted 2 lines
drivers/iio/industrialio-counter.c line 472
Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2
-0+{ + size_t i =3D counter->num_channels + counter->indio_dev->num_chann=
els;
Quoted 1 line
drivers/iio/industrialio-counter.c line 473
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+ const struct iio_chan_spec *const chans =3D counter->indio_dev->ch=
annels;
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+ +while (i-- > counter->num_channels) +/* Only IIO_COUNT channels need to be freed here */ + if (chans[i].type =3D=3D IIO_COUNT) +iio_counter_value_ext_info_free (chans + i);
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +kfree (chans); +} + +static int iio_counter_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+struct iio_counter_signal *signal; +struct iio_counter_value *value; + + if (mask !=3D IIO_CHAN_INFO_RAW) +return -EINVAL ;
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ + switch (chan->type) { + /* Map read_raw to signal_read for Signal */ + case IIO_SIGNAL: + if (!counter->ops->signal_read) + return -EINVAL; + + signal =3D iio_counter_signal_find_by_id(counter, chan->ch=
annel2);
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+if (!signal) +return -EINVAL ; + + return counter->ops->signal_read(counter, signal, val, val=
2);
Quoted 6 lines
drivers/iio/industrialio-counter.c
Quoted 6 lines
drivers/iio/industrialio-counter.c new file +6
-0+/* Map read_raw to value_read for Value */ +case IIO_COUNT : +if (!counter->ops->value_read) +return -EINVAL ; + + value =3D iio_counter_value_find_by_id(counter, chan->chan=
nel2);
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+if (!value) +return -EINVAL ; + + return counter->ops->value_read(counter, value, val, val2)=
;
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+/* Map read_raw to read_raw for non-counter channel */ +default : +if (counter->info && counter->info->read_raw) + return counter->info->read_raw(indio_dev, chan, va=
l,
Quoted 10 lines
drivers/iio/industrialio-counter.c
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ val2, mask); + } + +return -EINVAL ; +} + +static int iio_counter_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+struct iio_counter_signal *signal; +struct iio_counter_value *value; + + if (mask !=3D IIO_CHAN_INFO_RAW) +return -EINVAL ;
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ + switch (chan->type) { + /* Map write_raw to signal_write for Signal */ + case IIO_SIGNAL: + if (!counter->ops->signal_write) + return -EINVAL; + + signal =3D iio_counter_signal_find_by_id(counter, chan->ch=
annel2);
Quoted 4 lines
drivers/iio/industrialio-counter.c line 542
Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2
-0+if (!signal) +return -EINVAL ;Hmm. have to search is certainly a little ugly. Particularly as =
the driver
Quoted 1 line
would be able to maintain this as a lookup. I'd be tempted to pa=
ss the
Quoted 1 line
signal id down into the callback rather than finding the signal i=
n the core.
Quoted 4 lines
Might turn out messier though... :)
Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2
-0+ + return counter->ops->signal_write(counter, signal, val, va=
l2);
Quoted 6 lines
drivers/iio/industrialio-counter.c
Quoted 6 lines
drivers/iio/industrialio-counter.c new file +6
-0+/* Map write_raw to value_write for Value */ +case IIO_COUNT : +if (!counter->ops->value_write) +return -EINVAL ; + + value =3D iio_counter_value_find_by_id(counter, chan->chan=
nel2);
Quoted 2 lines
Again, this mapping is a simple lookup in the driver I think, perhaps pus=
h
Quoted 6 lines
it down there?
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+if (!value) +return -EINVAL ; + + return counter->ops->value_write(counter, value, val, val2=
);
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+/* Map write_raw to write_raw for non-counter channel */ +default : +if (counter->info && counter->info->write_raw) + return counter->info->write_raw(indio_dev, chan, v=
al,
Quoted 7 lines
drivers/iio/industrialio-counter.c
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7
-0+ val2, mask); + } + +return -EINVAL ; +} + +static int iio_counter_signals_validate(const struct iio_counter *const=
counter)
Quoted 11 lines
Quoted 11 lines
drivers/iio/industrialio-counter.c new file +11
-0+{ +size_t i; +const struct iio_counter_signal *signal; +size_t j; +int curr_id; + +/* At least one Signal must be defined */ +if (!counter->num_signals || !counter->signals) { +pr_err ("Counter '%d' Signals undefined\n" , counter->id); +return -EINVAL ; + }
Quoted 8 lines
drivers/iio/industrialio-counter.c line 584
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ + for (i =3D 0; i < counter->num_signals; i++) { + signal =3D counter->signals + i; +/* No two Signals may share the same ID */ + for (j =3D 0; j < i; j++) { + curr_id =3D counter->signals[j].id; + if (curr_id =3D=3D signal->id) { + pr_err("Duplicate Counter '%d' Signal '%d'=
\n",
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ counter->id, signal->id); +return -EEXIST ; + } + } + for (j =3D i + 1; j < counter->num_signals; j++) { + curr_id =3D counter->signals[j].id; + if (curr_id =3D=3D signal->id) { + pr_err("Duplicate Counter '%d' Signal '%d'=
\n",
Quoted 20 lines
drivers/iio/industrialio-counter.c line 588
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ counter->id, signal->id); +return -EEXIST ; + } + }Same as below.
Quoted 13 lines
drivers/iio/industrialio-counter.c new file +13
-0+ } + +return 0 ; +} + +static int iio_counter_triggers_validate (const int counter_id, +const struct iio_counter_value *const value) +{ +size_t i; +const struct iio_counter_trigger *trigger; +size_t j; +int curr_id; +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+/* At least one Trigger must be defined */ +if (!value->num_triggers || !value->triggers) { +pr_err ("Counter '%d' Value '%d' Triggers undefined\n" , + counter_id, value->id); +return -EINVAL ; + } + +/* Ensure all Triggers have a defined Signal; this prevents a poss=
ible
Quoted 1 line
drivers/iio/industrialio-counter.c line 618
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+* NULL pointer dereference when later validating Trigger Signal I=
Ds
Quoted 7 lines
Fix comment syntax throughout...
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+*/ + for (i =3D 0; i < value->num_triggers; i++) +if (!value->triggers[i].signal) { + pr_err("Counter '%d' Trigger '%zu' Signal undefine=
d\n",
Quoted 4 lines
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ counter_id, i); +return -EINVAL ; + } +
Quoted 8 lines
drivers/iio/industrialio-counter.c line 634
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+/* Verify validity of each Trigger */ + for (i =3D 0; i < value->num_triggers; i++) { + trigger =3D value->triggers + i; +/* No two Trigger Signals may share the same ID */ + for (j =3D 0; j < i; j++) { + curr_id =3D value->triggers[j].signal->id; + if (curr_id =3D=3D trigger->signal->id) { + pr_err("Signal '%d' is already linked to C=
ounter '%d' Value '%d'\n",
Quoted 9 lines
drivers/iio/industrialio-counter.c
Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9
-0+ trigger->signal->id, counter_id, + value->id); +return -EEXIST ; + } + } + for (j =3D i + 1; j < value->num_triggers; j++) { + curr_id =3D value->triggers[j].signal->id; + if (curr_id =3D=3D trigger->signal->id) { + pr_err("Signal '%d' is already linked to C=
ounter '%d' Value '%d'\n",
Quoted 6 lines
drivers/iio/industrialio-counter.c line 639
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+ trigger->signal->id, counter_id, + value->id); +return -EEXIST ; + } + }Again, one loop with the condition changed so it doesn't fault on the i =
=3D j case -- see below and
Quoted 5 lines
note I'm reviewing backwards...
Quoted 2 lines
drivers/iio/industrialio-counter.c new file +2
-0+ +/* At least one trigger mode must be defined for each Trig=
ger */
Quoted 1 line
drivers/iio/industrialio-counter.c line 651
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+ if (!trigger->num_trigger_modes || !trigger->trigger_modes=
) {
Quoted 1 line
drivers/iio/industrialio-counter.c line 652
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+ pr_err("Counter '%d' Signal '%d' trigger modes und=
efined\n",
Quoted 9 lines
drivers/iio/industrialio-counter.c
Quoted 9 lines
drivers/iio/industrialio-counter.c new file +9
-0+ counter_id, trigger->signal->id); +return -EINVAL ; + } + } + +return 0 ; +} + +static int iio_counter_values_validate(const struct iio_counter *const =
counter)
Quoted 16 lines
drivers/iio/industrialio-counter.c line 662
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+{Hmm. Pretty heavy weight checking. Ah well, up to you.
Quoted 12 lines
drivers/iio/industrialio-counter.c new file +12
-0+size_t i; +const struct iio_counter_value *value; +size_t j; +int curr_id; +int err; + +/* At least one Value must be defined */ +if (!counter->num_values || !counter->values) { +pr_err ("Counter '%d' Values undefined\n" , counter->id); +return -EINVAL ; + } +
Quoted 12 lines
drivers/iio/industrialio-counter.c line 679
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+/* Verify validity of each Value */ + for (i =3D 0; i < counter->num_values; i++) { + value =3D counter->values + i; +/* No two Values may share the same ID */ + for (j =3D 0; j < i; j++) {Single loop with a slightly change to condition
if ((i !=3D j) and (curr_id=3D=3Dvalue->id))Quoted 3 lines
drivers/iio/industrialio-counter.c new file +3
-0+ curr_id =3D counter->values[j].id; + if (curr_id =3D=3D value->id) { + pr_err("Duplicate Counter '%d' Value '%d'\=
n",
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ counter->id, value->id); +return -EEXIST ; + } + } + for (j =3D i + 1; j < counter->num_values; j++) { + curr_id =3D counter->values[j].id; + if (curr_id =3D=3D value->id) { + pr_err("Duplicate Counter '%d' Value '%d'\=
n",
Quoted 6 lines
drivers/iio/industrialio-counter.c
Quoted 6 lines
drivers/iio/industrialio-counter.c new file +6
-0+ counter->id, value->id); +return -EEXIST ; + } + } + +/* At least one function mode must be defined for each Val=
ue */
Quoted 1 line
drivers/iio/industrialio-counter.c line 697
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+ if (!value->num_function_modes || !value->function_modes) =
{
Quoted 1 line
drivers/iio/industrialio-counter.c line 698
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+ pr_err("Counter '%d' Value '%d' function modes und=
efined\n",
Quoted 10 lines
Quoted 10 lines
drivers/iio/industrialio-counter.c new file +10
-0+ counter->id, value->id); +return -EINVAL ; + } + +/* Verify the Triggers associated with each Value */ + err =3D iio_counter_triggers_validate(counter->id, value); +if (err) +return err; + } +
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+return 0 ; +} + +/** +* iio_counter_register - register Counter to the system +* @counter: pointer to IIO Counter to register +* +* This function piggybacks off of iio_device_register. First, the rele=
vant
Quoted 1 line
drivers/iio/industrialio-counter.c line 717
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+* Counter members are validated; the signals and values members must b=
e defined
Quoted 1 line
drivers/iio/industrialio-counter.c line 718
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+* and populated by valid Signal and Value structures respectively. Nex=
t, a
Quoted 1 line
drivers/iio/industrialio-counter.c line 719
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+* struct iio_dev is allocated by a call to iio_device_alloc and initia=
lized for
Quoted 1 line
drivers/iio/industrialio-counter.c line 720
Quoted 1 line
drivers/iio/industrialio-counter.c new file +1
-0+* the Counter, IIO channels are allocated, the Counter address is stor=
ed as the
Quoted 17 lines
Quoted 17 lines
drivers/iio/industrialio-counter.c new file +17
-0+* private data, and finally iio_device_register is called. +*/ +int iio_counter_register (struct iio_counter *const counter) +{ + const struct iio_info info_default =3D { + .read_raw =3D iio_counter_read_raw, + .write_raw =3D iio_counter_write_raw + }; +int err; +struct iio_info *info; +struct iio_counter **priv; + +if (!counter) +return -EINVAL ; + +/* Verify that Signals are valid and IDs do not conflict */ + err =3D iio_counter_signals_validate(counter);
Quoted 42 lines
drivers/iio/industrialio-counter.c line 741
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+if (err) +return err; + +/* Verify that Values are valid and IDs do not conflict; Fix multiline comment syntax to standard kernel syntax. /*
* Verify
*/Quoted 34 lines
drivers/iio/industrialio-counter.c new file +33
-0+* Triggers for each Value are also verified for validity +*/ + err =3D iio_counter_values_validate(counter); +if (err) +return err; + + counter->indio_dev =3D iio_device_alloc(sizeof(counter)); +if (!counter->indio_dev) +return -ENOMEM ; + + info =3D kmalloc(sizeof(*info), GFP_KERNEL); +if (!info) { + err =3D -ENOMEM ; + goto err_info_alloc; + } +/* If an iio_info has been supplied than we use that, +* otherwise we set all callbacks to NULL; iio_counter_read_raw +* and iio_counter_write_raw is used for read_raw and write_raw +* for either case in order to support counter functionality +* (supplied read_raw/write_raw will be called from within +* iio_counter_read_raw/iio_counter_write_raw for non-counter +* channels) +*/ +if (counter->info) { +memcpy (info, counter->info,sizeof (*counter->info)); + info->read_raw =3D iio_counter_read_raw; + info->write_raw =3D iio_counter_write_raw; + }else { +memcpy (info, &info_default,sizeof (info_default)); + } + + counter->indio_dev->info =3D info; + counter->indio_dev->modes =3D INDIO_DIRECT_MODE;
As I have said in the previous version, stm32 driver will not work with this mode, so I need way to change it.
One solution could be to use iio_priv to store an iio_counter structure wit= h
ops, arrays etc... Maybe even iio_counter structure could be hiden from dri= vers.
It could require to split iio_counter allocation and registration to functi= ons.
Quoted 4 lines
drivers/iio/industrialio-counter.c
Quoted 4 lines
drivers/iio/industrialio-counter.c new file +4
-0+ counter->indio_dev->name =3D counter->name; + counter->indio_dev->dev.parent =3D counter->dev; + +/* IIO channels are allocated and set for Signals, Values, and Tri=
ggers;
Quoted 25 lines
Quoted 25 lines
drivers/iio/industrialio-counter.c new file +25
-0+* any auxiliary IIO channels provided in iio_counter are also set +*/ + err =3D iio_counter_channels_alloc(counter); +if (err) + goto err_channels_alloc; + +/* Pointer to the counter is stored in indio_dev as a way to refer +* back to the counter from within various IIO callbacks +*/ + priv =3D iio_priv(counter->indio_dev); +memcpy (priv, &counter,sizeof (*priv)); + + err =3D iio_device_register(counter->indio_dev); +if (err) + goto err_iio_device_register; + +return 0 ; + +err_iio_device_register: +iio_counter_channels_free (counter); +err_channels_alloc: +kfree (info); +err_info_alloc: +iio_device_free (counter->indio_dev); +return err;
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+} +EXPORT_SYMBOL (iio_counter_register); + +/** +* iio_counter_unregister - unregister Counter from the system +* @counter: pointer to IIO Counter to unregister +* +* The Counter is unregistered from the system. The indio_dev is unregi=
stered
Quoted 22 lines
Quoted 22 lines
+ * and all allocated memory is freed.
+ */drivers/iio/industrialio-counter.c new file +20
-0+void iio_counter_unregister (struct iio_counter *const counter) +{ + const struct iio_info *const info =3D counter->indio_dev->info; + +if (!counter) +return ; + +iio_device_unregister (counter->indio_dev); + +iio_counter_channels_free (counter); + +kfree (info); +iio_device_free (counter->indio_dev); +} +EXPORT_SYMBOL (iio_counter_unregister); + +static void devm_iio_counter_unreg (struct device *dev,void *res) +{ +iio_counter_unregister (*(struct iio_counter **)res); +}
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+ +/** +* devm_iio_counter_register - Resource-managed iio_counter_register +* @dev: Device to allocate iio_counter for +* @counter: pointer to IIO Counter to register +* +* Managed iio_counter_register. The IIO counter registered with this +* function is automatically unregistered on driver detach. This functi=
on
Quoted 1 line
Quoted 1 line
+ * calls
iio_counter_registerinternally. Refer to that function for mo=
re
Quoted 3 lines
Quoted 3 lines
+ * information.
+ *
+ * If an iio counter registered with this function needs to be unregist=
ered
Quoted 4 lines
Quoted 4 lines
+ * separately,
devm_iio_counter_unregistermust be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
+ */
drivers/iio/industrialio-counter.c new file +7
-0+int devm_iio_counter_register(struct device *dev, + struct iio_counter *const counter) +{ + struct iio_counter **ptr; + int ret; + + ptr =3D devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KER=
NEL);
Quoted 7 lines
Quoted 7 lines
drivers/iio/industrialio-counter.c new file +7
-0+if (!ptr) +return -ENOMEM ; + + *ptr =3D counter; + ret =3D iio_counter_register(counter); +if (!ret) +devres_add (dev, ptr);
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+else +devres_free (ptr); + +return ret; +} +EXPORT_SYMBOL (devm_iio_counter_register); + +static int devm_iio_counter_match(struct device *dev, void *res, void *=
data)
Quoted 5 lines
Quoted 5 lines
drivers/iio/industrialio-counter.c new file +5
-0+{ + struct iio_counter **r =3D res; + +if (!r || !*r) { +WARN_ON (!r || !*r);
Quoted 8 lines
drivers/iio/industrialio-counter.c
Quoted 8 lines
drivers/iio/industrialio-counter.c new file +8
-0+return 0 ; + } + + return *r =3D=3D data; +} + +/** +* devm_iio_counter_unregister - Resource-managed iio_counter_unregiste=
r
Quoted 51 lines
Quoted 51 lines
+ * @dev: Device this
iio_counterbelongs to
+ * @counter: the iio counter associated with the device
+ *
+ * Unregister iio counter registered withdevm_iio_counter_register.
+ */drivers/iio/industrialio-counter.c new file +10
-0+void devm_iio_counter_unregister (struct device *dev, +struct iio_counter *const counter) +{ +int rc; + + rc =3D devres_release(dev, devm_iio_counter_unreg, + devm_iio_counter_match, counter); +WARN_ON (rc); +} +EXPORT_SYMBOL (devm_iio_counter_unregister);include/linux/iio/counter.h new file +30
-0@@ -0,0 +1,166 @@ +/* +* Industrial I/O counter interface +* Copyright (C) 2017 William Breathitt Gray +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, version 2, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ +#ifndef _IIO_COUNTER_H_ +#define _IIO_COUNTER_H_ + +#ifdef CONFIG_IIO_COUNTER + +#include <linux/device.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +/** + * struct iio_counter_signal - IIO Counter Signal node + * @id: [DRIVER] unique ID used to identify signal + * @name: [DRIVER] device-specific signal name + */ +struct iio_counter_signal { + int id;
Quoted 8 lines
include/linux/iio/counter.h
Quoted 8 lines
include/linux/iio/counter.h new file +8
-0+const char *name; +}; + +/** +* struct iio_counter_trigger - IIO Counter Trigger node +* @mode: [DRIVER] current trigger mode state +* @trigger_modes: [DRIVER] available trigger modes +* @num_trigger_modes: [DRIVER] number of modes specified in @tri=
gger_modes
Quoted 10 lines
include/linux/iio/counter.h line 45
Quoted 7 lines
include/linux/iio/counter.h new file +7
-0+* @signal: [DRIVER] pointer to associated signal +*/ +struct iio_counter_trigger { +unsigned int mode; +const char *const *trigger_modes; +unsigned int num_trigger_modes; +struct iio_counter_signal *signal;Are signals actually changeable? - Or is that pointer constant?
Quoted 9 lines
include/linux/iio/counter.h
Quoted 9 lines
include/linux/iio/counter.h new file +9
-0+}; + +/** +* struct iio_counter_value - IIO Counter Value node +* @id: [DRIVER] unique ID used to identify value +* @name: [DRIVER] device-specific value name +* @mode: [DRIVER] current function mode state +* @function_modes: [DRIVER] available function modes +* @num_function_modes: [DRIVER] number of modes specified in @fun=
ction_modes
Quoted 1 line
include/linux/iio/counter.h line 55
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* @triggers: [DRIVER] array of triggers for initializat=
ion
Quoted 2 lines
include/linux/iio/counter.h line 57
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* @num_triggers: [DRIVER] number of triggers specified in @triggers +* @function_enum: [INTERN] used internally to generate function attr=
ibutes
Quoted 11 lines
Quoted 11 lines
include/linux/iio/counter.h new file +11
-0+*/ +struct iio_counter_value { +int id; +const char *name; +unsigned int mode; +const char *const *function_modes; +unsigned int num_function_modes; + +struct iio_counter_trigger *triggers; +size_t num_triggers; +
Quoted 8 lines
include/linux/iio/counter.h
Quoted 8 lines
include/linux/iio/counter.h new file +8
-0+struct iio_enum function_enum; +}; + +struct iio_counter ; + +/** +* struct iio_counter_ops - IIO Counter related callbacks +* @signal_read: function to request a signal value from the device=
.
Quoted 1 line
include/linux/iio/counter.h line 77
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* Return value will specify the type of value return=
ed by
Quoted 7 lines
include/linux/iio/counter.h line 79
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* the device. val and val2 will contain the elements +* making up the returned value. Add some detail on the importance of the return value.
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* @signal_write: function to write a signal value to the device. +* Parameters are interpreted the same as signal_r=
ead.
Quoted 2 lines
You need a means of establishing the correct format for write (we needed =
it
Quoted 3 lines
in IIO fairly early on too)
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* @trigger_mode_set: function to set the trigger mode. mode is =
the index of
Quoted 1 line
include/linux/iio/counter.h line 83
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* the requested mode from the value trigger_modes ar=
ray.
Quoted 1 line
include/linux/iio/counter.h line 84
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* @trigger_mode_get: function to get the current trigger mode. =
Return value
Quoted 1 line
include/linux/iio/counter.h line 85
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* will specify the index of the current mode from th=
e
Quoted 2 lines
include/linux/iio/counter.h line 87
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* value trigger_modes array. +* @value_read: function to request a value value from the=
device.
Quoted 1 line
include/linux/iio/counter.h line 88
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* Return value will specify the type of value return=
ed by
Quoted 4 lines
include/linux/iio/counter.h
Quoted 4 lines
include/linux/iio/counter.h new file +4
-0+* the device. val and val2 will contain the elements +* making up the returned value. +* @value_write: function to write a value value to the device. +* Parameters are interpreted the same as value_re=
ad.
Quoted 1 line
include/linux/iio/counter.h line 93
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* @value_function_set: function to set the value function mode. mode i=
s the
Quoted 3 lines
include/linux/iio/counter.h
Quoted 3 lines
include/linux/iio/counter.h new file +3
-0+* index of the requested mode from the value +* function_modes array. +* @value_function_get: function to get the current value function mode=
. Return
Quoted 1 line
include/linux/iio/counter.h line 97
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* value will specify the index of the current mode f=
rom
Quoted 18 lines
Quoted 18 lines
include/linux/iio/counter.h new file +18
-0+* the value function_modes array. +*/ +struct iio_counter_ops { +int (*signal_read)(struct iio_counter *counter, +struct iio_counter_signal *signal,int *val,int *val2); +int (*signal_write)(struct iio_counter *counter, +struct iio_counter_signal *signal,int val,int val2); +int (*trigger_mode_set)(struct iio_counter *counter, +struct iio_counter_value *value, +struct iio_counter_trigger *trigger,unsigned int mode); +int (*trigger_mode_get)(struct iio_counter *counter, +struct iio_counter_value *value, +struct iio_counter_trigger *trigger); +int (*value_read)(struct iio_counter *counter, +struct iio_counter_value *value,int *val,int *val2); +int (*value_write)(struct iio_counter *counter, +struct iio_counter_value *value,int val,int val2); + int (*value_function_set)(struct iio_counter *counter,
Quoted 8 lines
include/linux/iio/counter.h
Quoted 8 lines
include/linux/iio/counter.h new file +8
-0+ struct iio_counter_value *value, unsigned int mode); +int (*value_function_get)(struct iio_counter *counter, +struct iio_counter_value *value); +}; + +/** +* struct iio_counter - IIO Counter data structure +* @id: [DRIVER] unique ID used to identify counte=
r
Quoted 2 lines
include/linux/iio/counter.h line 125
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* @name: [DRIVER] name of the device +* @dev: [DRIVER] device structure, should be assigned a pa=
rent
Quoted 2 lines
include/linux/iio/counter.h line 127
Quoted 2 lines
include/linux/iio/counter.h new file +2
-0+* and owner +* @ops: [DRIVER] callbacks from driver for counter compone=
nts
Quoted 4 lines
include/linux/iio/counter.h
Quoted 4 lines
include/linux/iio/counter.h new file +4
-0+* @signals: [DRIVER] array of signals for initialization +* @num_signals: [DRIVER] number of signals specified in @signals +* @values: [DRIVER] array of values for initialization +* @num_values: [DRIVER] number of values specified in @va=
lues
Quoted 1 line
include/linux/iio/counter.h line 132
Quoted 1 line
include/linux/iio/counter.h new file +1
-0+* @channels: [DRIVER] channel specification structure t=
able
Quoted 32 lines
include/linux/iio/counter.h line 138
Quoted 6 lines
include/linux/iio/counter.h new file +6
-0+* @num_channels: [DRIVER] number of channels specified in @channels +* @info: [DRIVER] callbacks and constant info from driver +* @indio_dev: [INTERN] industrial I/O device structure +* @driver_data: [DRIVER] driver data +*/ +struct iio_counter {Mentioned earlier - I think naming the device counter, is confusing. The counters should be named counter - call it
iio_counter_deviceoriio_counter_groupor something and keep counter for the things currently called value.Quoted 20 lines
include/linux/iio/counter.h new file +20
-0+int id; +const char *name; +struct device *dev; +const struct iio_counter_ops *ops; + +struct iio_counter_signal *signals; +size_t num_signals; +struct iio_counter_value *values; +size_t num_values; + +const struct iio_chan_spec *channels; +size_t num_channels; +const struct iio_info *info; + +struct iio_dev *indio_dev; +void *driver_data; +}; + +int iio_counter_register (struct iio_counter *const counter); +void iio_counter_unregister (struct iio_counter *const counter);
Quoted 9 lines
include/linux/iio/counter.h line 166
Quoted 8 lines
include/linux/iio/counter.h new file +8
-0+int devm_iio_counter_register (struct device *dev, +struct iio_counter *const counter); +void devm_iio_counter_unregister (struct device *dev, +struct iio_counter *const counter); + +#endif /* CONFIG_IIO_COUNTER */ + +#endif /* _IIO_COUNTER_H_ */
--=20
Benjamin Gaignard
Graphic Study Group
Linaro.org =E2=94=82 Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
PATCH v3 3/6 iio: Documentation: Add IIO Generic Counter sysfs documentation
In reply to William Breathitt GrayThis patch adds standard documentation for the userspace sysfs atrributes of the IIO Generic Counter Interface.
--- .../testing/sysfs-bus-iio-generic-counter-sysfs | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +63 -0
@@ -0,0 +1,63 @@ +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_function +KernelVersion: 4.14 +Contact: [email protected] +Description: + Counter function mode for Value Y of Counter X; counter function + evaluation triggered by conditions specified by the + countX-Y_trigger_signalX-Z attributes. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_function_available +KernelVersion: 4.14 +Contact: [email protected] +Description: + Discrete set of available function modes for the configuration + of the respective Value Y of Counter X are listed in this file. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_name +KernelVersion: 4.14 +Contact: [email protected] +Description: + Read-only attribute that indicates the device-specific name of + Value Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_raw +KernelVersion: 4.14 +Contact: [email protected] +Description: + Raw count data of Value Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_triggers +KernelVersion: 4.14 +Contact: [email protected] +Description: + List of Signals associated to Value Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z +KernelVersion: 4.14 +Contact: [email protected] +Description: + Trigger mode of Value Y for Signal Z. This attribute indicates + the condition of Signal Z that triggers the counter function + evaluation for Value Y. Both Value Y and Signal Z are part of + Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z_available +KernelVersion: 4.14 +Contact: [email protected] +Description: + Discrete set of available trigger modes are listed in this file + for the configuration of the respective Trigger associating + Signal Z of Counter X with Value Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/signalX-Y_name +KernelVersion: 4.14 +Contact: [email protected] +Description: + Read-only attribute that indicates the device-specific name of + Signal Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/signalX-Y_raw +KernelVersion: 4.14 +Contact: [email protected] +Description: + Raw signal data of Signal Y of Counter X.
Signature · 1 line
2.14.1
Jonathan Cameron
maintainerRe: [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation
Quoted 4 lines · On Thu, 5 Oct 2017 14:13:59 -0400 William Breathitt Gray…
This patch adds standard documentation for the userspace sysfs atrributes of the IIO Generic Counter Interface.
Signed-off-by William Breathitt Gray <[email protected]>
It's a good start - but I think we need to expand on some elements. I'm also worried that repurposing the - in here form being actual subtraction in the differential channels is confusing.
Quoted 15 lines
--- .../testing/sysfs-bus-iio-generic-counter-sysfs | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfsDocumentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +4
-0@@ -0,0 +1,63 @@ +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_function +KernelVersion: 4.14 +Contact: [email protected] +Description:
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 14
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +10
-0+ Counter function mode for Value Y of Counter X; counter function + evaluation triggered by conditions specified by the + countX-Y_trigger_signalX-Z attributes. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_function_available +KernelVersion: 4.14 +Contact: [email protected] +Description: + Discrete set of available function modes for the configuration + of the respective Value Y of Counter X are listed in this file.
Docs need to list what they can be.
Quoted 9 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +9
-0+ +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_name +KernelVersion: 4.14 +Contact: [email protected] +Description: + Read-only attribute that indicates the device-specific name of + Value Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_raw
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 33
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +10
-0+KernelVersion: 4.14 +Contact: [email protected] +Description: + Raw count data of Value Y of Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_triggers +KernelVersion: 4.14 +Contact: [email protected] +Description: + List of Signals associated to Value Y of Counter X.
That description doesn't seem to match. This is triggers rather than signals.
Quoted 8 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +8
-0+ +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z +KernelVersion: 4.14 +Contact: [email protected] +Description: + Trigger mode of Value Y for Signal Z. This attribute indicates + the condition of Signal Z that triggers the counter function + evaluation for Value Y. Both Value Y and Signal Z are part of
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 50
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +9
-0+ Counter X. + +What: /sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z_available +KernelVersion: 4.14 +Contact: [email protected] +Description: + Discrete set of available trigger modes are listed in this file + for the configuration of the respective Trigger associating + Signal Z of Counter X with Value Y of Counter X.
it's another case where I think overloading the - is getting confusing. Not sure what the right answer is though!
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 57
Quoted 7 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +7
-0+ +What: /sys/bus/iio/devices/iio:deviceX/signalX-Y_name +KernelVersion: 4.14 +Contact: [email protected] +Description: + Read-only attribute that indicates the device-specific name of + Signal Y of Counter X.
What is this name for? This doesn't feel like a counter specific thing, but rather a generic IIO addition.
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs line 63
Quoted 6 lines
Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs new file +6
-0+ +What: /sys/bus/iio/devices/iio:deviceX/signalX-Y_raw +KernelVersion: 4.14 +Contact: [email protected] +Description: + Raw signal data of Signal Y of Counter X.
While it might seem clunky - I'd prefer us to keep the channel direction prefixes throughout. So that means most of these would end up as in_signalX-Y_raw etc.
As I mentioned before I think overloading the meaning of - is probably a bad idea. Not sure what the right option would be though. Err. Something like in_signalX<Y>_raw? Or do we need to make it really obvious and end up with something like. in_signalX<CounterY>_raw - though that would require another level of info beyond and index to describe that we are dealing with a counter rather than a light channel for example.
I wonder if we would be better off not using the naming to reflect the grouping at all, but rather add something like in_signalX_group
in_counterY_groupmembers which lists the members. Are we trying to stretch the interface too far in sticking to doing it in naming? Not sure...
Jonathan
PATCH v3 4/6 docs: Add IIO Generic Counter Interface documentation
In reply to William Breathitt GrayThis patch adds high-level documentation about the IIO Generic Counter Interface.
--- Documentation/driver-api/iio/generic-counter.txt | 526 +++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 Documentation/driver-api/iio/generic-counter.txt
Documentation/driver-api/iio/generic-counter.txt new file +526 -0
@@ -0,0 +1,526 @@ +========================= +Generic Counter Interface +========================= + +Introduction +============ + +Counter devices are prevalent within a diverse spectrum of industries. +The ubiquitous presence of these devices necessitates a common interface +and standard of interaction and exposure. This driver API attempts to +resolve the issue of duplicate code found among existing counter device +drivers by introducing a generic counter interface for consumption. The +generic counter interface enables drivers to support and expose a common +set of components and functionality present in counter devices. + +Theory +====== + +Counter devices can vary greatly in design, but regardless of whether +some devices are quadrature encoder counters or pedometers, all counter +devices consist of a core set of components. This core set of +components, shared by all counter devices, is what forms the essence of +the generic counter interface. + +There are three core components to a counter: + + VALUE + ----- + A Value represents the count data for a set of Signals. A Value + has a count function mode (e.g. "increment" or "quadrature x4") + which respresents the update behavior for the count data. A + Value also has a set of one or more associated Signals. + + SIGNAL + ------ + A Signal represents a count input line. A Signal may be + associated to one or more Values. + + TRIGGER + ------- + A Trigger represents a Value's count function trigger condition + mode (e.g. "rising edge" or "double pulse") for an associated + Signal. If a Signal is associated with a Value, a respective + Trigger instance for that association exists -- albeit perhaps + with a trigger condition mode of "none." + +A counter is defined as a set of input signals associated to count data +that are generated by the evaluation of the state of the associated +input signals as defined by the respective count functions. Within the +context of the generic counter interface, a counter consists of Values +each associated to a set of Signals, whose respective Trigger instances +represent the count function update conditions for the associated +Values. + +Paradigm +======== + +The most basic counter device may be expressed as a single Value +associated with a single Signal via a single Trigger. Take for example +a hypothetical counter device which simply accumulates a count of rising +edges on a source input line. + + Value Trigger Signal + ----- ------- ------ ++---------------------+ +| Data: Count | Rising Edge ________ +| Function: Increment | <------------- / Source \ +| | ____________ ++---------------------+ + +In this example, the Signal is a source input line with a pulsing +voltage, while the Value is a persistent count which increments. The +Signal is associated with the Value via a respective Trigger. The +increment function is triggered by the condition specified by the +Triggered -- in this case a rising edge condition. In summary, the +counter device existence and behavior is aptly represented by respective +Value, Signal, and Trigger components: a rising edge condition triggers +an incrementation function on an accumulating count datum. + +A counter device is not limited to a single Signal; in fact, in theory +an unlimited number of Signals may be associated with a Value. For +example, a quadrature encoder counter device can keep track of position +based on the states of two input lines. + + Value Trigger Signal + ----- ------- ------ ++-------------------------+ +| Data: Position | Both Edges ___ +| Function: Quadrature x4 | <------------- / A \ +| | _______ +| | +| | Both Edges ___ +| | <------------- / B \ +| | _______ ++-------------------------+ + +In this example, two Signals (quadrature encoder lines A and B) are +associated to a single Value: a rising or falling edge on either A or B +triggers the "Quadrature x4" function which determines the direction of +movement and updates the respective position data. The "Quadrature x4" +function is likely implemented in the hardware of the quadrature encoder +counter device; the Value, Triggers, and Signals simply represent this +hardware behavior and functionality. + +Signal associated to the same Value can have differing trigger +conditions. For example, a quadrature encoder counter device operating +in a non-quadrature mode could have one input line dedicated for +movement and a second input line dedicated for direction. + + Value Trigger Signal + ----- ------- ------ ++------------------------- + +| Data: Position | Rising Edge ___ +| Function: Non-quadrature | <------------- / A \ (Movement) +| | _______ +| | +| | None ___ +| | <------------- / B \ (Direction) +| | _______ ++--------------------------+ + +Only Signal A triggers the "Non-quadrature" update function, but the +state of Signal B is still required in order to know the direction in +order to properly update the position data. So in the end, both Signals +are associated to the same Value via two respective Triggers, but only +one Trigger has an active trigger condition while the other is left in a +"None" condition mode to indicate its respective Signal's availability +for state evaluation despite its non-triggering mode. + +Although the examples thus far have been representations of physical +devices, this is not a necessity. A counter simply represent the +evaluation (Value) of input data (Signals) triggered by specific +conditions (Triggers). A counter can be the representation of more +abstract components. + +For example, suppose a counter representation is desired for a DNA +sequence analysizer which detects possible genetic diseases. + + Value Trigger Signal + ----- ------- ------ ++---------------------+ +| Data: Diseases | Gene Transcript (EST) _____ +| Function: Cancers | <----------------------- / DNA \ (GAAGTGC...) +| | _________ ++---------------------+ + +In this scenario, the Signal is a stream of DNA nucleotide bases (As, +Ts, Cs, and Gs), the Trigger is expressed sequence tags (ESTs), and the +Value is a list of diseases discovered (in this case the function is +evaluating for possible cancers). Note how the Signal in this example +does not represent a physical voltage line, nor does the Trigger +represent a physical voltage line state change, nor does the Value +represent a strictly decimal data value. + +The DNA sequence analysizer example is contrived to illustrate the +flexibility of the generic counter paradigm by demonstrating its +capability of representing abstract concepts; however, physical devices +are likely to be more fitting for such a representation. + +The key concept is that the Signal, Trigger, and Value are abstract +representations which do not need to be closely married to their +respective physical sources. This allows the user of a counter to +divorce themselves from the nuances of physical components (such as +whether an input line is differential or single-ended) and focus on the +core idea of what the data and process represent (an accumulated count +of rising edges). + +Userspace Interface +=================== + +Several sysfs attributes are generated by the generic counter interface, +and reside under the /sys/bus/iio/devices/iio:deviceX directory. + +Each counter has a respective set of countX-Y and signalX-Y prefixed +attributes, where X is the id set in the counter structure, and Y is the +id of the respective Value or Signal. + +The generic counter interface sysfs attributes are as follows: + + countX-Y_function: count function mode + countX-Y_function_available: available count function modes + countX-Y_name: Value name + countX-Y_raw: Value data + countX-Y_triggers: Value's associated Signals + countX-Y_trigger_signalX-Z: Value Y trigger mode for Signal Z + countX-Y_trigger_signalX-Z_available: available Value Y trigger + modes for Signal Z + signalX-Y_name: Signal name + signalX-Y_raw: Signal data + +Through these sysfs attributes, programs and scripts may interact with +the generic counter paradigm Values, Triggers, and Signals of respective +counter devices. + +Driver API +========== + +Driver authors may utilize the generic counter interface in their code +by including the include/linux/iio/counter.h header file. This header +file provides several core data structures and function prototypes for +defining a generic counter. + +struct iio_counter_signal +------------------------- + +This structure defines a generic counter paradigm Signal component; +typically this will correlate with an input channel on a physical +counter device. This structure is the simplest to define with only two +structure members which require explicit configuration: + + id: Unique ID used to identify the Signal + + name: Device-specific Signal name (typically the device input + channel name) + +struct iio_counter_trigger +-------------------------- + +This structure defines a generic counter paradigm Trigger component. To +properly utilize this structure, trigger modes and an associated Signal +must be defined: + + mode: Index of the current trigger mode state + + trigger_modes: Array of trigger modes each represented + by a character string + + num_trigger_modes: Number of trigger modes provided in + trigger_modes array + + signal: Pointer to associated Signal + +struct iio_counter_value +------------------------ + +This structure defines a generic counter paradigm Value component; +typically this will correlate with the read data (the "count" value) +provided by a physical counter device. This structure requires the +explicit configuration of an ID, name, function modes (the function +triggered on a Trigger condition), and optionally a set of initial +associated Triggers: + + id: Unique ID used to identify the Signal + + name: Device-specific Value name (typically + the device read channel name) + + mode: Index of the current function mode state + + function_modes: Array of function modes each represented + by a character string + + num_function_modes: Number of function modes provided in + function_modes array + + triggers: Array of associated Triggers + + num_triggers: Number of Triggers provided in triggers + array + +struct iio_counter_ops +---------------------- + +This structure defines callbacks to interact with the Value, Trigger, +and Signal components: + + signal_read: Function to request a signal value from + the device. Return value will specify + the type of value returned by the + device. val and val2 will contain the + elements making up the returned value. + + signal_write: Function to write a signal value to the + device. Parameters are interpreted the + same as signal_read. + + trigger_mode_set: Function to set the trigger mode. mode + is the index of the requested mode from + the value trigger_modes array. + + trigger_mode_get: Function to get the current trigger + mode. Return value will specify the + index of the current mode from the value + trigger_modes array. + + value_read: Function to request a value value from + the device. Return value will specify + the type of value returned by the + device. val and val2 will contain the + elements making up the returned value. + + value_write: Function to write a value value to the + device. Parameters are interpreted the + same as value_read. + + value_function_set: Function to set the value function mode. + mode is the index of the requested mode + from the value function_modes array. + + value_function_get: Function to get the current value + function mode. Return value will specify + the index of the current mode from the + value function_modes array. + +struct iio_counter +------------------ + +This is the main data structure for a counter device; access to all +respective Values, Triggers, and Signals is possible from this +structure. This structure allows the configuration of an ID, name, +function callbacks, initial Signals and initial Values, auxiliary IIO +core channels and callbacks, and driver-specific data: + + id: Unique ID used to identify the counter + + name: Name of the counter device + + dev: Device structure, which should be + assigned a parent and owner + + ops: Function callbacks for counter + components (Signal, Trigger, Value) + + signals: Array of Signals + + num_signals: Number of Signals specified in signals + array + + values: Array of Values + + num_values: Number of Values specified in values + array + + channels: Optional IIO core channels specification + structure table + + num_channels: Number of channels specified in channels + + info: IIO core function callbacks and constant + info from driver + + driver_data: Driver-specific data + +Registration functions +---------------------- + +Counters may be registered to the system via the iio_counter_register +function and subsequently unregistered via the iio_counter_unregister +function. The devm_iio_counter_register and devm_iio_counter_unregister +functions serve as device memory-managed versions of the +iio_counter_register and iio_counter_unregister functions. + +An initialized iio_counter structure, which defines the Counter, is +required to be passed in for registration; the Signals, Values, and +Triggers for the Counter passed in via the signals and values arrays as +part of the iio_counter structure. If auxiliary IIO core channels and +functionality are required, IIO core channels and callbacks may be +passed in via the channels and info members of the passed-in iio_counter +structure. + +Implementation +============== + +The IIO generic counter interface piggybacks off of the IIO core. This +is primarily used to leverage the existing sysfs setup: the IIO_COUNT +channel attributes represent the Counter Value, while the IIO_SIGNAL +channel attributes represent the Counter Signal; auxilary IIO_COUNT +attributes represent the Counter Signal connections (Triggers) and their +respective state change configurations which trigger an associated +"counter function" evaluation. + +The iio_counter_ops structure serves as a container for driver callbacks +to communicate with the device; function callbacks are provided to read +and write various Signals and Values, and set and get the "trigger mode" +and "function mode" for various Triggers and Values respectively. + +To support a counter device, a driver must first allocate the available +Counter Signals via iio_counter_signal structures. These Signals should +be stored as an array and set to the signals array member of an +allocated iio_counter structure before the Counter is registered to the +system. + +Counter Values may be allocated via iio_counter_value structures, and +respective Counter Signal associations (Triggers) made via +iio_counter_trigger structures. Associated iio_counter_trigger +structures are stored as an array and set to the the triggers array +member of the respective iio_counter_value structure. These +iio_counter_value structures are set to the values array member of an +allocated iio_counter structure before the Counter is registered to the +system. + +A counter device is registered to the system by passing the respective +initialized iio_counter structure to the iio_counter_register function; +similarly, the iio_counter_unregister function unregisters the +respective counter. + +Architecture +============ + +Although the IIO Generic Counter Interface utilizes IIO core under the +hood, driver authors are not necessarily required to interact with IIO +core data structures and functions directly -- in theory, such details +of the system are abstracted away. Driver authors only need to concern +themselves with the Generic Counter specific data structures and +functions found in the include/linux/iio/counter.h header file. + +In other words, the driver API is intended to expose itself sufficiently +upon the principles and concepts of the generic counter paradigm (i.e. +Values, Triggers, Signals, etc.) such that it may be indepedent from its +underlying implementation; theoretically, the IIO core code in the +implementation could be replaced away in its entirely by an alternative +implementation all without the need to update existing drivers utilizing +the Generic Counter Interface driver API. + +This paradigm separation however does result in some mapping concerns +between Generic Counter functions to IIO core functions; in particular, +parameters for the IIO core functions expect IIO core data structures +(e.g. iio_dev and iio_chan_spec) which are not provided directly by the +parameters for the respective Generic Counter functions. This results in +a somewhat opaque pathway from a iio_counter structure to its associated +iio_dev in order to support the required IIO core calls. + +The following call graphs should help illustrate some of the main IIO +core dependencies: + ++----------------------+ +| iio_counter_register | ++----------------------+ + | | | + | | +-----------------------------+ + | +------------------+ | + | | | + V V V ++------------------+ +----------+ +---------------------+ +| iio_device_alloc | | iio_priv | | iio_device_register | ++------------------+ +----------+ +---------------------+ + +The iio_counter_register function allocates and initializes a new +iio_dev structure which will serve as the respective Counter's gateway +to IIO core support. The address of the parent iio_counter structure is +stored with the iio_dev structure via iio_priv in order to allow access +back to the Counter from within the IIO core functions. Finally, the +iio_dev structure is registered via iio_device_register. + ++-----------------------+ +----------------------+ +| iio_read_channel_info |-->| iio_counter_read_raw | ++-----------------------+ +----------------------+ + | | | + +---------------------------+ | | + | +-------------+ | + | | ++ + | | | + V V V + IIO_SIGNAL IIO_COUNT IIO_* ++-------------+ +------------+ +----------+ +| signal_read | | value_read | | read_raw | ++-------------+ +------------+ +----------+ + ++------------------------+ +-----------------------+ +| iio_write_channel_info |-->| iio_counter_write_raw | ++------------------------+ +-----------------------+ + | | | + +----------------------------+ | | + | +-------------+ | + | | | + | | | + V V V + IIO_SIGNAL IIO_COUNT IIO_* ++--------------+ +-------------+ +-----------+ +| signal_write | | value_write | | write_raw | ++--------------+ +-------------+ +-----------+ + +Normally, the IIO core iio_read_channel_info and iio_write_channel_info +functions respectiveluy call the driver-supplied read_raw and write_raw +callbacks directly. Since the generic counter interface serves as an +abstraction above IIO core, drive authors do not generally directly +configure a read_raw/write_raw callback. + +Instead, the IIO Generic Counter Interface hooks on to the +iio_read_channel_info and iio_write_channel_info expected read_raw and +write_raw callbacks respectively via iio_counter_read_raw and +iio_counter_write_raw. The iio_counter_read_raw and +iio_counter_write_raw functions then call the respective driver-supplied +signal_read/value_read and signal_write/value_write callbacks +respectively for the appropriate IIO_SIGNAL OR IIO_COUNT. If an IIO core +channel that was not part of the generic counter paradigm was supplied +via the channels member of the iio_counter structure, then the +respective driver-supplied (via the iio_counter structure info member) +read_raw and write_raw are called. + ++---------------------------+ +----------------------------+ +| iio_read_channel_ext_info | | iio_write_channel_ext_info | ++---------------------------+ +----------------------------+ + | | + V V ++-------------------------------+ +--------------------------------+ +| iio_counter_trigger_mode_read | | iio_counter_trigger_mode_write | ++-------------------------------+ +--------------------------------+ + | | + V V ++------------------+ +------------------+ +| trigger_mode_get | | trigger_mode_set | ++------------------+ +------------------+ + ++---------------+ +----------------+ +| iio_enum_read | | iio_enum_write | ++---------------+ +----------------+ + | | + V V ++--------------------------------+ +--------------------------------+ +| iio_counter_value_function_get | | iio_counter_value_function_set | ++--------------------------------+ +--------------------------------+ + | | + V V ++--------------------+ +--------------------+ +| value_function_get | | value_function_set | ++--------------------+ +--------------------+ + +The driver-supplied trigger_mode_get and trigger_mode_set callbacks hook +on to the iio_read_channel_ext_info and iio_write_channel_ext_info +functions respectively via the iio_counter_trigger_mode_read and +iio_counter_trigger_mode_write functions. Similarly, the driver-supplied +value_function_get and value_function set callbacks hook on to the +iio_enum_read and iio_enum_write functions respectively via the +iio_counter_value_function_get and iio_counter_value_function set +functions.
Signature · 1 line
2.14.1
Jonathan Cameron
maintainerRe: [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation
Quoted 4 lines · On Thu, 5 Oct 2017 14:14:24 -0400 William Breathitt Gray…
This patch adds high-level documentation about the IIO Generic Counter Interface.
Signed-off-by William Breathitt Gray <[email protected]>
Put it into the relevant index.rst to get and make it an rst file so it gets built as part of the sphinx docs.
Quoted 24 lines
--- Documentation/driver-api/iio/generic-counter.txt | 526 +++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 Documentation/driver-api/iio/generic-counter.txtDocumentation/driver-api/iio/generic-counter.txt new file +13
-0@@ -0,0 +1,526 @@ +========================= +Generic Counter Interface +========================= + +Introduction +============ + +Counter devices are prevalent within a diverse spectrum of industries. +The ubiquitous presence of these devices necessitates a common interface +and standard of interaction and exposure. This driver API attempts to +resolve the issue of duplicate code found among existing counter device +drivers by introducing a generic counter interface for consumption. The +generic counter interface enables drivers to support and expose a common
Documentation/driver-api/iio/generic-counter.txt line 23
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+set of components and functionality present in counter devices. + +Theory +====== + +Counter devices can vary greatly in design, but regardless of whether +some devices are quadrature encoder counters or pedometers, all counter +devices consist of a core set of components. This core set of +components, shared by all counter devices, is what forms the essence of +the generic counter interface.
Hmm. Whether it makes sense to move over pedometers is a bit of an open question... They tend to be fairly opaque wrt to where their data is coming from. We'll see, but I'd kind of prefer we keep it out of the docs until that case has been fully analysed!
Documentation/driver-api/iio/generic-counter.txt line 32
Quoted 9 lines
Documentation/driver-api/iio/generic-counter.txt new file +9
-0+ +There are three core components to a counter: + + VALUE + ----- + A Value represents the count data for a set of Signals. A Value + has a count function mode (e.g. "increment" or "quadrature x4") + which respresents the update behavior for the count data. A + Value also has a set of one or more associated Signals.
The whole set of valid function modes needs to be documented somewhere. I don't think it currently is (but I may have missed it and haven't read all the patches yet!)
Interesting terminology - this Value is what most people would think of as a counter. Can we just call it that? The issue is that we are thinking from the hardware point of view that says a 'counter block' can have multiple counters so value is just one of them. I'm not sure we want to present that terminology to userspace.
Documentation/driver-api/iio/generic-counter.txt line 45
Quoted 13 lines
Documentation/driver-api/iio/generic-counter.txt new file +13
-0+ + SIGNAL + ------ + A Signal represents a count input line. A Signal may be + associated to one or more Values. + + TRIGGER + ------- + A Trigger represents a Value's count function trigger condition + mode (e.g. "rising edge" or "double pulse") for an associated + Signal. If a Signal is associated with a Value, a respective + Trigger instance for that association exists -- albeit perhaps + with a trigger condition mode of "none."
Again, we need to enumerate the full acceptable list. Enforcing it can be by review, but we need a list that people can refer to or we will get multiple drivers doing subtly different variants.
Documentation/driver-api/iio/generic-counter.txt line 53
Quoted 8 lines
Documentation/driver-api/iio/generic-counter.txt new file +8
-0+ +A counter is defined as a set of input signals associated to count data +that are generated by the evaluation of the state of the associated +input signals as defined by the respective count functions. Within the +context of the generic counter interface, a counter consists of Values +each associated to a set of Signals, whose respective Trigger instances +represent the count function update conditions for the associated +Values.
Could we call this a 'counter group' or 'counter device' or something to free the term counter up for a single instance of what is currently called value.
Documentation/driver-api/iio/generic-counter.txt line 61
Quoted 8 lines
Documentation/driver-api/iio/generic-counter.txt new file +8
-0+ +Paradigm +======== + +The most basic counter device may be expressed as a single Value +associated with a single Signal via a single Trigger. Take for example +a hypothetical counter device which simply accumulates a count of rising +edges on a source input line.
Drop hypothetical - this is a perfectly real example. Plenty of hardware out there does this!
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+ + Value Trigger Signal + ----- ------- ------ ++---------------------+ +| Data: Count | Rising Edge ________ +| Function: Increment | <------------- / Source \ +| | ____________ ++---------------------+ + +In this example, the Signal is a source input line with a pulsing
Documentation/driver-api/iio/generic-counter.txt
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+voltage, while the Value is a persistent count which increments. The +Signal is associated with the Value via a respective Trigger. The +increment function is triggered by the condition specified by the +Triggered -- in this case a rising edge condition. In summary, the +counter device existence and behavior is aptly represented by respective +Value, Signal, and Trigger components: a rising edge condition triggers +an incrementation function on an accumulating count datum. + +A counter device is not limited to a single Signal; in fact, in theory +an unlimited number of Signals may be associated with a Value.
Change that that to many signals. Otherwise someone pedantic will say it's limited by the implementation to 2^32 or similar..
Quoted 13 lines
For
Documentation/driver-api/iio/generic-counter.txt new file +12
-0+example, a quadrature encoder counter device can keep track of position +based on the states of two input lines. + + Value Trigger Signal + ----- ------- ------ ++-------------------------+ +| Data: Position | Both Edges ___ +| Function: Quadrature x4 | <------------- / A \ +| | _______ +| | +| | Both Edges ___ +| | <------------- / B \
Documentation/driver-api/iio/generic-counter.txt line 103
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+| | _______ ++-------------------------+ + +In this example, two Signals (quadrature encoder lines A and B) are +associated to a single Value: a rising or falling edge on either A or B +triggers the "Quadrature x4" function which determines the direction of +movement and updates the respective position data. The "Quadrature x4" +function is likely implemented in the hardware of the quadrature encoder +counter device; the Value, Triggers, and Signals simply represent this +hardware behavior and functionality.
Interesting that there is a relationship between valid trigger settings and valid Value:Function settings.
Quoted 7 lines
Documentation/driver-api/iio/generic-counter.txt new file +7
-0+ +Signal associated to the same Value can have differing trigger +conditions. For example, a quadrature encoder counter device operating +in a non-quadrature mode could have one input line dedicated for +movement and a second input line dedicated for direction. + + Value Trigger Signal
Documentation/driver-api/iio/generic-counter.txt line 120
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+ ----- ------- ------ ++------------------------- + +| Data: Position | Rising Edge ___ +| Function: Non-quadrature | <------------- / A \ (Movement) +| | _______ +| | +| | None ___ +| | <------------- / B \ (Direction) +| | _______ ++--------------------------+
Non-quadrature is a pretty odd way of defining it! I'd suggest something more positive - pulse-direction would be the standard terminology.
Documentation/driver-api/iio/generic-counter.txt line 128
Quoted 8 lines
Documentation/driver-api/iio/generic-counter.txt new file +8
-0+ +Only Signal A triggers the "Non-quadrature" update function, but the +state of Signal B is still required in order to know the direction in +order to properly update the position data. So in the end, both Signals +are associated to the same Value via two respective Triggers, but only +one Trigger has an active trigger condition while the other is left in a +"None" condition mode to indicate its respective Signal's availability +for state evaluation despite its non-triggering mode.
Interesting - there sill be some confusion around the quadrature cases though as the 'value' will only change on say rising edge A, but the internal state machine is tracking the previous changes of edge B. I'd suggest we add a note about that somewhere here.
Quoted 50 lines
Documentation/driver-api/iio/generic-counter.txt new file +50
-0+ +Although the examples thus far have been representations of physical +devices, this is not a necessity. A counter simply represent the +evaluation (Value) of input data (Signals) triggered by specific +conditions (Triggers). A counter can be the representation of more +abstract components. + +For example, suppose a counter representation is desired for a DNA +sequence analysizer which detects possible genetic diseases. + + Value Trigger Signal + ----- ------- ------ ++---------------------+ +| Data: Diseases | Gene Transcript (EST) _____ +| Function: Cancers | <----------------------- / DNA \ (GAAGTGC...) +| | _________ ++---------------------+ + +In this scenario, the Signal is a stream of DNA nucleotide bases (As, +Ts, Cs, and Gs), the Trigger is expressed sequence tags (ESTs), and the +Value is a list of diseases discovered (in this case the function is +evaluating for possible cancers). Note how the Signal in this example +does not represent a physical voltage line, nor does the Trigger +represent a physical voltage line state change, nor does the Value +represent a strictly decimal data value. + +The DNA sequence analysizer example is contrived to illustrate the +flexibility of the generic counter paradigm by demonstrating its +capability of representing abstract concepts; however, physical devices +are likely to be more fitting for such a representation. + +The key concept is that the Signal, Trigger, and Value are abstract +representations which do not need to be closely married to their +respective physical sources. This allows the user of a counter to +divorce themselves from the nuances of physical components (such as +whether an input line is differential or single-ended) and focus on the +core idea of what the data and process represent (an accumulated count +of rising edges). + +Userspace Interface +=================== + +Several sysfs attributes are generated by the generic counter interface, +and reside under the /sys/bus/iio/devices/iio:deviceX directory. + +Each counter has a respective set of countX-Y and signalX-Y prefixed +attributes, where X is the id set in the counter structure, and Y is the +id of the respective Value or Signal. + +The generic counter interface sysfs attributes are as follows:
Documentation/driver-api/iio/generic-counter.txt line 188
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+ + countX-Y_function: count function mode + countX-Y_function_available: available count function modes + countX-Y_name: Value name + countX-Y_raw: Value data + countX-Y_triggers: Value's associated Signals + countX-Y_trigger_signalX-Z: Value Y trigger mode for Signal Z + countX-Y_trigger_signalX-Z_available: available Value Y trigger + modes for Signal Z + signalX-Y_name: Signal name
I mentioned this in one of the other patches - This naming stuff feels like, if we want to do it we should do it throughout IIO.
Possibly it maps to the data_sheet_name.. Higher level information beyond this device would to my mind require explicit representation of devices beyond this - i.e. what is attached to the wires that is generating things to count.
Quoted 28 lines
Documentation/driver-api/iio/generic-counter.txt new file +28
-0+ signalX-Y_raw: Signal data + +Through these sysfs attributes, programs and scripts may interact with +the generic counter paradigm Values, Triggers, and Signals of respective +counter devices. + +Driver API +========== + +Driver authors may utilize the generic counter interface in their code +by including the include/linux/iio/counter.h header file. This header +file provides several core data structures and function prototypes for +defining a generic counter. + +struct iio_counter_signal +------------------------- + +This structure defines a generic counter paradigm Signal component; +typically this will correlate with an input channel on a physical +counter device. This structure is the simplest to define with only two +structure members which require explicit configuration: + + id: Unique ID used to identify the Signal + + name: Device-specific Signal name (typically the device input + channel name) + +struct iio_counter_trigger
Documentation/driver-api/iio/generic-counter.txt line 226
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+-------------------------- + +This structure defines a generic counter paradigm Trigger component. To +properly utilize this structure, trigger modes and an associated Signal +must be defined: + + mode: Index of the current trigger mode state + + trigger_modes: Array of trigger modes each represented + by a character string
As mentioned before these have to be explicitly listed somewhere. They form userspace ABI. We must not leave this empty. Defining these cleanly and generically may be one of the hardest aspects to agree on.
Quoted 23 lines
Documentation/driver-api/iio/generic-counter.txt new file +23
-0+ + num_trigger_modes: Number of trigger modes provided in + trigger_modes array + + signal: Pointer to associated Signal + +struct iio_counter_value +------------------------ + +This structure defines a generic counter paradigm Value component; +typically this will correlate with the read data (the "count" value) +provided by a physical counter device. This structure requires the +explicit configuration of an ID, name, function modes (the function +triggered on a Trigger condition), and optionally a set of initial +associated Triggers: + + id: Unique ID used to identify the Signal + + name: Device-specific Value name (typically + the device read channel name) + + mode: Index of the current function mode state +
Documentation/driver-api/iio/generic-counter.txt line 259
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+ function_modes: Array of function modes each represented + by a character string + + num_function_modes: Number of function modes provided in + function_modes array + + triggers: Array of associated Triggers + + num_triggers: Number of Triggers provided in triggers + array
It might also be convenient to have a flag in here to say if the triggers are fixed or not.
Documentation/driver-api/iio/generic-counter.txt line 262
Quoted 3 lines
Documentation/driver-api/iio/generic-counter.txt new file +3
-0+ +struct iio_counter_ops +----------------------
I'd suggest you push these details down in to kernel-doc in the code and pull it back in here using the relevant bits of markup. That stuff is a bit clunky but any docs in here of actual elements etc will probably rot unedited if stuff changes.
Documentation/driver-api/iio/generic-counter.txt line 271
Quoted 9 lines
Documentation/driver-api/iio/generic-counter.txt new file +9
-0+ +This structure defines callbacks to interact with the Value, Trigger, +and Signal components: + + signal_read: Function to request a signal value from + the device. Return value will specify + the type of value returned by the + device. val and val2 will contain the + elements making up the returned value.
Mention IIO_VAL_INT type return values as well.
Quoted 9 lines
Documentation/driver-api/iio/generic-counter.txt new file +9
-0+ + signal_write: Function to write a signal value to the + device. Parameters are interpreted the + same as signal_read. + + trigger_mode_set: Function to set the trigger mode. mode + is the index of the requested mode from + the value trigger_modes array. +
Documentation/driver-api/iio/generic-counter.txt line 290
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+ trigger_mode_get: Function to get the current trigger + mode. Return value will specify the + index of the current mode from the value + trigger_modes array. + + value_read: Function to request a value value from + the device. Return value will specify + the type of value returned by the + device. val and val2 will contain the + elements making up the returned value.
This is the counter I think? Value is rather vague, counter_read will be less so.
Quoted 70 lines
Documentation/driver-api/iio/generic-counter.txt new file +70
-0+ + value_write: Function to write a value value to the + device. Parameters are interpreted the + same as value_read. + + value_function_set: Function to set the value function mode. + mode is the index of the requested mode + from the value function_modes array. + + value_function_get: Function to get the current value + function mode. Return value will specify + the index of the current mode from the + value function_modes array. + +struct iio_counter +------------------ + +This is the main data structure for a counter device; access to all +respective Values, Triggers, and Signals is possible from this +structure. This structure allows the configuration of an ID, name, +function callbacks, initial Signals and initial Values, auxiliary IIO +core channels and callbacks, and driver-specific data: + + id: Unique ID used to identify the counter + + name: Name of the counter device + + dev: Device structure, which should be + assigned a parent and owner + + ops: Function callbacks for counter + components (Signal, Trigger, Value) + + signals: Array of Signals + + num_signals: Number of Signals specified in signals + array + + values: Array of Values + + num_values: Number of Values specified in values + array + + channels: Optional IIO core channels specification + structure table + + num_channels: Number of channels specified in channels + + info: IIO core function callbacks and constant + info from driver + + driver_data: Driver-specific data + +Registration functions +---------------------- + +Counters may be registered to the system via the iio_counter_register +function and subsequently unregistered via the iio_counter_unregister +function. The devm_iio_counter_register and devm_iio_counter_unregister +functions serve as device memory-managed versions of the +iio_counter_register and iio_counter_unregister functions. + +An initialized iio_counter structure, which defines the Counter, is +required to be passed in for registration; the Signals, Values, and +Triggers for the Counter passed in via the signals and values arrays as +part of the iio_counter structure. If auxiliary IIO core channels and +functionality are required, IIO core channels and callbacks may be +passed in via the channels and info members of the passed-in iio_counter +structure. +
Documentation/driver-api/iio/generic-counter.txt line 370
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+Implementation +============== + +The IIO generic counter interface piggybacks off of the IIO core. This +is primarily used to leverage the existing sysfs setup: the IIO_COUNT +channel attributes represent the Counter Value, while the IIO_SIGNAL +channel attributes represent the Counter Signal; auxilary IIO_COUNT +attributes represent the Counter Signal connections (Triggers) and their +respective state change configurations which trigger an associated +"counter function" evaluation.
If it makes more sense I wouldn't mind changing the form of that setup code to be more generic so you can use it without jumping through so many hoops.
Quoted 33 lines
Documentation/driver-api/iio/generic-counter.txt new file +33
-0+ +The iio_counter_ops structure serves as a container for driver callbacks +to communicate with the device; function callbacks are provided to read +and write various Signals and Values, and set and get the "trigger mode" +and "function mode" for various Triggers and Values respectively. + +To support a counter device, a driver must first allocate the available +Counter Signals via iio_counter_signal structures. These Signals should +be stored as an array and set to the signals array member of an +allocated iio_counter structure before the Counter is registered to the +system. + +Counter Values may be allocated via iio_counter_value structures, and +respective Counter Signal associations (Triggers) made via +iio_counter_trigger structures. Associated iio_counter_trigger +structures are stored as an array and set to the the triggers array +member of the respective iio_counter_value structure. These +iio_counter_value structures are set to the values array member of an +allocated iio_counter structure before the Counter is registered to the +system. + +A counter device is registered to the system by passing the respective +initialized iio_counter structure to the iio_counter_register function; +similarly, the iio_counter_unregister function unregisters the +respective counter. + +Architecture +============ + +Although the IIO Generic Counter Interface utilizes IIO core under the +hood, driver authors are not necessarily required to interact with IIO +core data structures and functions directly -- in theory, such details +of the system are abstracted away. Driver authors only need to concern
Documentation/driver-api/iio/generic-counter.txt line 413
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0+themselves with the Generic Counter specific data structures and +functions found in the include/linux/iio/counter.h header file. + +In other words, the driver API is intended to expose itself sufficiently +upon the principles and concepts of the generic counter paradigm (i.e. +Values, Triggers, Signals, etc.) such that it may be indepedent from its +underlying implementation; theoretically, the IIO core code in the +implementation could be replaced away in its entirely by an alternative +implementation all without the need to update existing drivers utilizing +the Generic Counter Interface driver API.
Good. I hope it's not just in theory ;) May well make sense in the future as in particular IIO may get based on some new core infrastructure itself.
Quoted 103 lines
Documentation/driver-api/iio/generic-counter.txt new file +103
-0+ +This paradigm separation however does result in some mapping concerns +between Generic Counter functions to IIO core functions; in particular, +parameters for the IIO core functions expect IIO core data structures +(e.g. iio_dev and iio_chan_spec) which are not provided directly by the +parameters for the respective Generic Counter functions. This results in +a somewhat opaque pathway from a iio_counter structure to its associated +iio_dev in order to support the required IIO core calls. + +The following call graphs should help illustrate some of the main IIO +core dependencies: + ++----------------------+ +| iio_counter_register | ++----------------------+ + | | | + | | +-----------------------------+ + | +------------------+ | + | | | + V V V ++------------------+ +----------+ +---------------------+ +| iio_device_alloc | | iio_priv | | iio_device_register | ++------------------+ +----------+ +---------------------+ + +The iio_counter_register function allocates and initializes a new +iio_dev structure which will serve as the respective Counter's gateway +to IIO core support. The address of the parent iio_counter structure is +stored with the iio_dev structure via iio_priv in order to allow access +back to the Counter from within the IIO core functions. Finally, the +iio_dev structure is registered via iio_device_register. + ++-----------------------+ +----------------------+ +| iio_read_channel_info |-->| iio_counter_read_raw | ++-----------------------+ +----------------------+ + | | | + +---------------------------+ | | + | +-------------+ | + | | ++ + | | | + V V V + IIO_SIGNAL IIO_COUNT IIO_* ++-------------+ +------------+ +----------+ +| signal_read | | value_read | | read_raw | ++-------------+ +------------+ +----------+ + ++------------------------+ +-----------------------+ +| iio_write_channel_info |-->| iio_counter_write_raw | ++------------------------+ +-----------------------+ + | | | + +----------------------------+ | | + | +-------------+ | + | | | + | | | + V V V + IIO_SIGNAL IIO_COUNT IIO_* ++--------------+ +-------------+ +-----------+ +| signal_write | | value_write | | write_raw | ++--------------+ +-------------+ +-----------+ + +Normally, the IIO core iio_read_channel_info and iio_write_channel_info +functions respectiveluy call the driver-supplied read_raw and write_raw +callbacks directly. Since the generic counter interface serves as an +abstraction above IIO core, drive authors do not generally directly +configure a read_raw/write_raw callback. + +Instead, the IIO Generic Counter Interface hooks on to the +iio_read_channel_info and iio_write_channel_info expected read_raw and +write_raw callbacks respectively via iio_counter_read_raw and +iio_counter_write_raw. The iio_counter_read_raw and +iio_counter_write_raw functions then call the respective driver-supplied +signal_read/value_read and signal_write/value_write callbacks +respectively for the appropriate IIO_SIGNAL OR IIO_COUNT. If an IIO core +channel that was not part of the generic counter paradigm was supplied +via the channels member of the iio_counter structure, then the +respective driver-supplied (via the iio_counter structure info member) +read_raw and write_raw are called. + ++---------------------------+ +----------------------------+ +| iio_read_channel_ext_info | | iio_write_channel_ext_info | ++---------------------------+ +----------------------------+ + | | + V V ++-------------------------------+ +--------------------------------+ +| iio_counter_trigger_mode_read | | iio_counter_trigger_mode_write | ++-------------------------------+ +--------------------------------+ + | | + V V ++------------------+ +------------------+ +| trigger_mode_get | | trigger_mode_set | ++------------------+ +------------------+ + ++---------------+ +----------------+ +| iio_enum_read | | iio_enum_write | ++---------------+ +----------------+ + | | + V V ++--------------------------------+ +--------------------------------+ +| iio_counter_value_function_get | | iio_counter_value_function_set | ++--------------------------------+ +--------------------------------+ + | | + V V ++--------------------+ +--------------------+ +| value_function_get | | value_function_set |
Documentation/driver-api/iio/generic-counter.txt line 526
Quoted 10 lines
Documentation/driver-api/iio/generic-counter.txt new file +10
-0++--------------------+ +--------------------+ + +The driver-supplied trigger_mode_get and trigger_mode_set callbacks hook +on to the iio_read_channel_ext_info and iio_write_channel_ext_info +functions respectively via the iio_counter_trigger_mode_read and +iio_counter_trigger_mode_write functions. Similarly, the driver-supplied +value_function_get and value_function set callbacks hook on to the +iio_enum_read and iio_enum_write functions respectively via the +iio_counter_value_function_get and iio_counter_value_function set +functions.
I'm inclined to agree with you when you suggest that separating things out more may well make sense.
This all seems way to involved just to share a relatively small amount of code.
The question is whether we loose anything particularly useful if we drop most of the complexity. If it makes sense for a counter to sit on the IIO bus in sysfs that's easily done (triggers in IIO for example do that already).
The main advantage then becomes one of grouping 'somewhat' related subsystems together... Can also share some utility code given the overall structures are somewhat similar of course.
Not sure...
Jonathan
PATCH v3 5/6 iio: Add dummy counter driver
In reply to William Breathitt GrayThis patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface.
Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write.
--- drivers/iio/counter/Kconfig | 15 ++ drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/iio/counter/dummy-counter.c
drivers/iio/counter/Kconfig +15 -0
@@ -22,6 +22,21 @@ config 104_QUAD_8 The base port addresses for the devices may be configured via the base array module parameter. +config DUMMY_COUNTER + tristate "Dummy counter driver" + help + Select this option to enable the dummy counter driver. The dummy + counter driver serves as a reference implementation of a driver that + utilizes the Generic Counter interface. + + Writing individual '1' and '0' characters to the Signal attributes + allows a user to simulate a typical Counter Signal input stream for + evaluation; the Counter will evaluate the Signal data based on the + respective trigger mode for the associated Signal, and trigger the + associated counter function specified by the respective function mode. + The current Value value may be read, and the Value value preset by a + write. + config STM32_LPTIMER_CNT tristate "STM32 LP Timer encoder counter driver" depends on MFD_STM32_LPTIMER || COMPILE_TEST
drivers/iio/counter/Makefile +1 -0
@@ -5,4 +5,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o +obj-$(CONFIG_DUMMY_COUNTER) += dummy-counter.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
drivers/iio/counter/dummy-counter.c new file +293 -0
293 lines not on this page · raw message
Signature · 1 line
2.14.1
Jonathan Cameron
maintainerRe: [PATCH v3 5/6] iio: Add dummy counter driver
Quoted 3 lines · On Thu, 5 Oct 2017 14:14:38 -0400 William Breathitt Gray…
This patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface.
This is great - I was planning to write one of these to try out the interface and you've already done it :)
Quoted 10 lines
Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write.
Signed-off-by William Breathitt Gray <[email protected]>
Comments are more generic suggestions for improving the example than general comments on the ABI - that all seems to make reasonable sense (other than when the documents contain the wonderful
Value value - not confusing at all ;)
Quoted 85 lines
--- drivers/iio/counter/Kconfig | 15 ++ drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/iio/counter/dummy-counter.cdrivers/iio/counter/Kconfig +15
-0@@ -22,6 +22,21 @@ config 104_QUAD_8 The base port addresses for the devices may be configured via the base array module parameter. +config DUMMY_COUNTER + tristate "Dummy counter driver" + help + Select this option to enable the dummy counter driver. The dummy + counter driver serves as a reference implementation of a driver that + utilizes the Generic Counter interface. + + Writing individual '1' and '0' characters to the Signal attributes + allows a user to simulate a typical Counter Signal input stream for + evaluation; the Counter will evaluate the Signal data based on the + respective trigger mode for the associated Signal, and trigger the + associated counter function specified by the respective function mode. + The current Value value may be read, and the Value value preset by a + write. + config STM32_LPTIMER_CNT tristate "STM32 LP Timer encoder counter driver" depends on MFD_STM32_LPTIMER || COMPILE_TESTdrivers/iio/counter/Makefile +1
-0@@ -5,4 +5,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o +obj-$(CONFIG_DUMMY_COUNTER) += dummy-counter.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.odrivers/iio/counter/dummy-counter.c new file +36
-0@@ -0,0 +1,293 @@ +/* +* Dummy counter driver +* Copyright (C) 2017 William Breathitt Gray +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, version 2, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/counter.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#define DUMCNT_NUM_COUNTERS 2 +/** +* struct dumcnt - private data structure +* @counter: instance of the iio_counter +* @counts: array of accumulation values +* @states: array of input line states +*/ +struct dumcnt { +struct iio_counter counter; +unsigned int counts[DUMCNT_NUM_COUNTERS ]; +unsigned int states[DUMCNT_NUM_COUNTERS ]; +}; + +static int dumcnt_signal_read (struct iio_counter *counter, +struct iio_counter_signal *signal,int *val,int *val2)
drivers/iio/counter/dummy-counter.c line 46
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+{ +struct dumcnt *const priv = counter->driver_data; + *val = priv->states[signal->id]; + +return IIO_VAL_INT ; +} + +static int dumcnt_signal_write (struct iio_counter *counter, +struct iio_counter_signal *signal,int val,int val2) +{
This is an odd one to have in the generic interface. What real hardware does writing the state make sense for? If it is only fake drivers - figure out a way to do it without having to add to the generic interfaces.
Quoted 92 lines
drivers/iio/counter/dummy-counter.c new file +92
-0+struct dumcnt *const priv = counter->driver_data; +const unsigned int id = signal->id; +const unsigned int prev_state = priv->states[id]; +struct iio_counter_value *const value = counter->values + id; +const unsigned int function_mode = value->mode; +const unsigned int trigger_mode = value->triggers[0 ].mode; +unsigned int triggered =0 ; + +if (val && val !=1 ) +return -EINVAL ; + +/* If no state change then just exit */ +if (prev_state == val) +return 0 ; + + priv->states[id] = val; + +switch (trigger_mode) { +/* "none" case */ +case 0 : +return 0 ; +/* "rising edge" case */ +case 1 : +if (!prev_state) + triggered =1 ; +break ; +/* "falling edge" case */ +case 2 : +if (prev_state) + triggered =1 ; +break ; +/* "both edges" case */ +case 3 : + triggered =1 ; +break ; + } + +/* If counter function triggered */ +if (triggered) +/* "increase" case */ +if (function_mode) + priv->counts[id]++; +/* "decrease" case */ +else + priv->counts[id]--; + +return 0 ; +} + +static int dumcnt_trigger_mode_set (struct iio_counter *counter, +struct iio_counter_value *value,struct iio_counter_trigger *trigger, +unsigned int mode) +{ +if (mode >= trigger->num_trigger_modes) +return -EINVAL ; + + trigger->mode = mode; + +return 0 ; +} + +static int dumcnt_trigger_mode_get (struct iio_counter *counter, +struct iio_counter_value *value,struct iio_counter_trigger *trigger) +{ +return trigger->mode; +} + +static int dumcnt_value_read (struct iio_counter *counter, +struct iio_counter_value *value,int *val,int *val2) +{ +struct dumcnt *const priv = counter->driver_data; + + *val = priv->counts[value->id]; + +return IIO_VAL_INT ; +} + +static int dumcnt_value_write (struct iio_counter *counter, +struct iio_counter_value *value,int val,int val2) +{ +struct dumcnt *const priv = counter->driver_data; + + priv->counts[value->id] = val; + +return 0 ; +} + +static int dumcnt_value_function_set (struct iio_counter *counter, +struct iio_counter_value *value,unsigned int mode) +{ +if (mode >= value->num_function_modes) +return -EINVAL ;
drivers/iio/counter/dummy-counter.c line 148
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ + value->mode = mode; + +return 0 ; +} + +static int dumcnt_value_function_get (struct iio_counter *counter, +struct iio_counter_value *value) +{ +return value->mode;
If it's called function in the function name, call it function in the structure as well rather than mode.
Quoted 5 lines
drivers/iio/counter/dummy-counter.c new file +5
-0+} + +static const struct iio_counter_ops dumcnt_ops = { + .signal_read = dumcnt_signal_read, + .signal_write = dumcnt_signal_write,
drivers/iio/counter/dummy-counter.c line 163
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ .trigger_mode_get = dumcnt_trigger_mode_get, + .trigger_mode_set = dumcnt_trigger_mode_set, + .value_read = dumcnt_value_read, + .value_write = dumcnt_value_write, + .value_function_set = dumcnt_value_function_set, + .value_function_get = dumcnt_value_function_get +}; + +static const char *const dumcnt_function_modes[] = { +"decrease" ,
I think increment was used somewhere in the docs... It's clearer, but you need to document this ABI to stop having subtle variations of it like this (even if I imagined it ;)
Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15
-0+"increase" +}; + +#define DUMCNT_SIGNAL (_id, _name) { \ + .id = _id, \ + .name = _name \ +} + +static const struct iio_counter_signal dumcnt_signals[] = { +DUMCNT_SIGNAL (0 ,"Signal A" ),DUMCNT_SIGNAL (1 ,"Signal B" ) +}; + +#define DUMCNT_VALUE (_id, _name) { \ + .id = _id, \ + .name = _name, \
drivers/iio/counter/dummy-counter.c line 188
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ .mode = 0, \ + .function_modes = dumcnt_function_modes, \ + .num_function_modes = ARRAY_SIZE(dumcnt_function_modes) \ +} + +static const struct iio_counter_value dumcnt_values[] = { +DUMCNT_VALUE (0 ,"Count A" ),DUMCNT_VALUE (1 ,"Count B" ) +}; + +static const char *const dumcnt_trigger_modes[] = {
As mentioned below, use an enum for the index as then you can make it obvious what 0 means when you set the mode to it later.
drivers/iio/counter/dummy-counter.c line 199
Quoted 11 lines
drivers/iio/counter/dummy-counter.c new file +11
-0+"none" , +"rising edge" , +"falling edge" , + "both edges" +}; + +static int dumcnt_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct iio_counter_signal *signals; +const size_t num_signals =ARRAY_SIZE (dumcnt_signals);
Don't bother with local variable - makes it less obvious what is going on.
drivers/iio/counter/dummy-counter.c line 201
Quoted 2 lines
drivers/iio/counter/dummy-counter.c new file +2
-0+struct iio_counter_value *values; +const size_t num_values =ARRAY_SIZE (dumcnt_values);
Local variable doesn't add anything and if anything makes it slightly harder to check what is going on.
drivers/iio/counter/dummy-counter.c line 210
Quoted 9 lines
drivers/iio/counter/dummy-counter.c new file +9
-0+struct iio_counter_trigger *triggers; +int i; +struct dumcnt *dumcnt; + + signals =devm_kmalloc (dev,sizeof (dumcnt_signals),GFP_KERNEL ); +if (!signals) +return -ENOMEM ; + +memcpy (signals, dumcnt_signals,sizeof (dumcnt_signals));
devm_kmemdup?
drivers/iio/counter/dummy-counter.c line 216
Quoted 6 lines
drivers/iio/counter/dummy-counter.c new file +6
-0+ + values =devm_kmalloc (dev,sizeof (dumcnt_values),GFP_KERNEL ); +if (!values) +return -ENOMEM ; + +memcpy (values, dumcnt_values,sizeof (dumcnt_values));
devm_kmemdup?
drivers/iio/counter/dummy-counter.c line 224
Quoted 8 lines
drivers/iio/counter/dummy-counter.c new file +8
-0+ +/* Associate values with their respective signals */ +for (i =0 ; i < num_values; i++) { + triggers =devm_kmalloc (dev,sizeof (*triggers),GFP_KERNEL ); +if (!triggers) +return -ENOMEM ; + + triggers->mode =0 ;
Use an enum for the dumcn_trigger_modes array index then specify by enum value here. Will make it more readable.
Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15
-0+ triggers->trigger_modes = dumcnt_trigger_modes; + triggers->num_trigger_modes =ARRAY_SIZE (dumcnt_trigger_modes); + triggers->signal = &signals[i]; + + values[i].triggers = triggers; + values[i].num_triggers =1 ; + } + + dumcnt =devm_kzalloc (dev,sizeof (*dumcnt),GFP_KERNEL ); +if (!dumcnt) +return -ENOMEM ; + + dumcnt->counter.name =dev_name (dev); + dumcnt->counter.dev = dev; + dumcnt->counter.ops = &dumcnt_ops;
drivers/iio/counter/dummy-counter.c line 249
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ dumcnt->counter.signals = signals; + dumcnt->counter.num_signals = num_signals; + dumcnt->counter.values = values; + dumcnt->counter.num_values = num_values; + dumcnt->counter.driver_data = dumcnt; + +return devm_iio_counter_register (dev, &dumcnt->counter); +} + +static struct platform_device *dumcnt_device;
Support multiple instances - nick this stuff from the IIO dummy driver or more specifically the industrialio-sw-device.c
drivers/iio/counter/dummy-counter.c line 253
Quoted 4 lines
drivers/iio/counter/dummy-counter.c new file +4
-0+ +static struct platform_driver dumcnt_driver = { + .driver = { + .name ="104-quad-8"
Don't do that! Give it it's own name.
drivers/iio/counter/dummy-counter.c line 266
Quoted 13 lines
drivers/iio/counter/dummy-counter.c new file +13
-0+ } +}; + +static void __exit dumcnt_exit(void) +{ +platform_device_unregister (dumcnt_device); +platform_driver_unregister (&dumcnt_driver); +} + +static int __init dumcnt_init (void ) +{ +int err; +
General thing, but if we are going to upstream this with the subsystem, make device instantiation happen via configfs.
Quoted 27 lines
drivers/iio/counter/dummy-counter.c new file +27
-0+ dumcnt_device =platform_device_alloc (dumcnt_driver.driver.name,-1 ); +if (!dumcnt_device) +return -ENOMEM ; + + err =platform_device_add (dumcnt_device); +if (err) + goto err_platform_device; + + err =platform_driver_probe (&dumcnt_driver, dumcnt_probe); +if (err) + goto err_platform_driver; + +return 0 ; + +err_platform_driver: +platform_device_del (dumcnt_device); +err_platform_device: +platform_device_put (dumcnt_device); +return err; +} + +module_init (dumcnt_init); +module_exit (dumcnt_exit); + +MODULE_AUTHOR ("William Breathitt Gray <[email protected]>" ); +MODULE_DESCRIPTION ("Dummy counter driver" ); +MODULE_LICENSE ("GPL v2" );
Re: [PATCH v3 5/6] iio: Add dummy counter driver
2017-10-08 15:41 GMT+02:00 Jonathan Cameron <[email protected]>:
Quoted 9 lines
Quoted 3 lines · On Thu, 5 Oct 2017 14:14:38 -0400 William Breathitt Gray…
This patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface.
This is great - I was planning to write one of these to try out the interface and you've already done it :)
May I suggest you to write a gpio-counter driver instead ? (that was in my todo list)
It is more or less the same than this driver but instead of simulate the signal it could use gpio to them provide a software quadratic counter.
Benjamin
Quoted 406 lines
Quoted 10 lines
Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write.
Signed-off-by William Breathitt Gray <[email protected]>Comments are more generic suggestions for improving the example than general comments on the ABI - that all seems to make reasonable sense (other than when the documents contain the wonderful
Value value - not confusing at all ;)Quoted 85 lines
--- drivers/iio/counter/Kconfig | 15 ++ drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/iio/counter/dummy-counter.cdrivers/iio/counter/Kconfig +15
-0@@ -22,6 +22,21 @@ config 104_QUAD_8 The base port addresses for the devices may be configured via the base array module parameter. +config DUMMY_COUNTER + tristate "Dummy counter driver" + help + Select this option to enable the dummy counter driver. The dummy + counter driver serves as a reference implementation of a driver that + utilizes the Generic Counter interface. + + Writing individual '1' and '0' characters to the Signal attributes + allows a user to simulate a typical Counter Signal input stream for + evaluation; the Counter will evaluate the Signal data based on the + respective trigger mode for the associated Signal, and trigger the + associated counter function specified by the respective function mode. + The current Value value may be read, and the Value value preset by a + write. + config STM32_LPTIMER_CNT tristate "STM32 LP Timer encoder counter driver" depends on MFD_STM32_LPTIMER || COMPILE_TESTdrivers/iio/counter/Makefile +1
-0@@ -5,4 +5,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o +obj-$(CONFIG_DUMMY_COUNTER) += dummy-counter.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.odrivers/iio/counter/dummy-counter.c new file +36
-0@@ -0,0 +1,293 @@ +/* +* Dummy counter driver +* Copyright (C) 2017 William Breathitt Gray +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, version 2, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/counter.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#define DUMCNT_NUM_COUNTERS 2 +/** +* struct dumcnt - private data structure +* @counter: instance of the iio_counter +* @counts: array of accumulation values +* @states: array of input line states +*/ +struct dumcnt { +struct iio_counter counter; +unsigned int counts[DUMCNT_NUM_COUNTERS ]; +unsigned int states[DUMCNT_NUM_COUNTERS ]; +}; + +static int dumcnt_signal_read (struct iio_counter *counter, +struct iio_counter_signal *signal,int *val,int *val2)drivers/iio/counter/dummy-counter.c line 46
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+{ +struct dumcnt *const priv = counter->driver_data; + *val = priv->states[signal->id]; + +return IIO_VAL_INT ; +} + +static int dumcnt_signal_write (struct iio_counter *counter, +struct iio_counter_signal *signal,int val,int val2) +{This is an odd one to have in the generic interface. What real hardware does writing the state make sense for? If it is only fake drivers - figure out a way to do it without having to add to the generic interfaces.
Quoted 92 lines
drivers/iio/counter/dummy-counter.c new file +92
-0+struct dumcnt *const priv = counter->driver_data; +const unsigned int id = signal->id; +const unsigned int prev_state = priv->states[id]; +struct iio_counter_value *const value = counter->values + id; +const unsigned int function_mode = value->mode; +const unsigned int trigger_mode = value->triggers[0 ].mode; +unsigned int triggered =0 ; + +if (val && val !=1 ) +return -EINVAL ; + +/* If no state change then just exit */ +if (prev_state == val) +return 0 ; + + priv->states[id] = val; + +switch (trigger_mode) { +/* "none" case */ +case 0 : +return 0 ; +/* "rising edge" case */ +case 1 : +if (!prev_state) + triggered =1 ; +break ; +/* "falling edge" case */ +case 2 : +if (prev_state) + triggered =1 ; +break ; +/* "both edges" case */ +case 3 : + triggered =1 ; +break ; + } + +/* If counter function triggered */ +if (triggered) +/* "increase" case */ +if (function_mode) + priv->counts[id]++; +/* "decrease" case */ +else + priv->counts[id]--; + +return 0 ; +} + +static int dumcnt_trigger_mode_set (struct iio_counter *counter, +struct iio_counter_value *value,struct iio_counter_trigger *trigger, +unsigned int mode) +{ +if (mode >= trigger->num_trigger_modes) +return -EINVAL ; + + trigger->mode = mode; + +return 0 ; +} + +static int dumcnt_trigger_mode_get (struct iio_counter *counter, +struct iio_counter_value *value,struct iio_counter_trigger *trigger) +{ +return trigger->mode; +} + +static int dumcnt_value_read (struct iio_counter *counter, +struct iio_counter_value *value,int *val,int *val2) +{ +struct dumcnt *const priv = counter->driver_data; + + *val = priv->counts[value->id]; + +return IIO_VAL_INT ; +} + +static int dumcnt_value_write (struct iio_counter *counter, +struct iio_counter_value *value,int val,int val2) +{ +struct dumcnt *const priv = counter->driver_data; + + priv->counts[value->id] = val; + +return 0 ; +} + +static int dumcnt_value_function_set (struct iio_counter *counter, +struct iio_counter_value *value,unsigned int mode) +{ +if (mode >= value->num_function_modes) +return -EINVAL ;drivers/iio/counter/dummy-counter.c line 148
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ + value->mode = mode; + +return 0 ; +} + +static int dumcnt_value_function_get (struct iio_counter *counter, +struct iio_counter_value *value) +{ +return value->mode;If it's called function in the function name, call it function in the structure as well rather than mode.
Quoted 5 lines
drivers/iio/counter/dummy-counter.c new file +5
-0+} + +static const struct iio_counter_ops dumcnt_ops = { + .signal_read = dumcnt_signal_read, + .signal_write = dumcnt_signal_write,drivers/iio/counter/dummy-counter.c line 163
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ .trigger_mode_get = dumcnt_trigger_mode_get, + .trigger_mode_set = dumcnt_trigger_mode_set, + .value_read = dumcnt_value_read, + .value_write = dumcnt_value_write, + .value_function_set = dumcnt_value_function_set, + .value_function_get = dumcnt_value_function_get +}; + +static const char *const dumcnt_function_modes[] = { +"decrease" ,I think increment was used somewhere in the docs... It's clearer, but you need to document this ABI to stop having subtle variations of it like this (even if I imagined it ;)
Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15
-0+"increase" +}; + +#define DUMCNT_SIGNAL (_id, _name) { \ + .id = _id, \ + .name = _name \ +} + +static const struct iio_counter_signal dumcnt_signals[] = { +DUMCNT_SIGNAL (0 ,"Signal A" ),DUMCNT_SIGNAL (1 ,"Signal B" ) +}; + +#define DUMCNT_VALUE (_id, _name) { \ + .id = _id, \ + .name = _name, \drivers/iio/counter/dummy-counter.c line 188
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ .mode = 0, \ + .function_modes = dumcnt_function_modes, \ + .num_function_modes = ARRAY_SIZE(dumcnt_function_modes) \ +} + +static const struct iio_counter_value dumcnt_values[] = { +DUMCNT_VALUE (0 ,"Count A" ),DUMCNT_VALUE (1 ,"Count B" ) +}; + +static const char *const dumcnt_trigger_modes[] = {As mentioned below, use an enum for the index as then you can make it obvious what 0 means when you set the mode to it later.
drivers/iio/counter/dummy-counter.c line 199
Quoted 11 lines
drivers/iio/counter/dummy-counter.c new file +11
-0+"none" , +"rising edge" , +"falling edge" , + "both edges" +}; + +static int dumcnt_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct iio_counter_signal *signals; +const size_t num_signals =ARRAY_SIZE (dumcnt_signals);Don't bother with local variable - makes it less obvious what is going on.
drivers/iio/counter/dummy-counter.c line 201
Quoted 2 lines
drivers/iio/counter/dummy-counter.c new file +2
-0+struct iio_counter_value *values; +const size_t num_values =ARRAY_SIZE (dumcnt_values);Local variable doesn't add anything and if anything makes it slightly harder to check what is going on.
drivers/iio/counter/dummy-counter.c line 210
Quoted 9 lines
drivers/iio/counter/dummy-counter.c new file +9
-0+struct iio_counter_trigger *triggers; +int i; +struct dumcnt *dumcnt; + + signals =devm_kmalloc (dev,sizeof (dumcnt_signals),GFP_KERNEL ); +if (!signals) +return -ENOMEM ; + +memcpy (signals, dumcnt_signals,sizeof (dumcnt_signals));
devm_kmemdup?drivers/iio/counter/dummy-counter.c line 216
Quoted 6 lines
drivers/iio/counter/dummy-counter.c new file +6
-0+ + values =devm_kmalloc (dev,sizeof (dumcnt_values),GFP_KERNEL ); +if (!values) +return -ENOMEM ; + +memcpy (values, dumcnt_values,sizeof (dumcnt_values));
devm_kmemdup?drivers/iio/counter/dummy-counter.c line 224
Quoted 8 lines
drivers/iio/counter/dummy-counter.c new file +8
-0+ +/* Associate values with their respective signals */ +for (i =0 ; i < num_values; i++) { + triggers =devm_kmalloc (dev,sizeof (*triggers),GFP_KERNEL ); +if (!triggers) +return -ENOMEM ; + + triggers->mode =0 ;Use an enum for the
dumcn_trigger_modesarray index then specify by enum value here. Will make it more readable.Quoted 15 lines
drivers/iio/counter/dummy-counter.c new file +15
-0+ triggers->trigger_modes = dumcnt_trigger_modes; + triggers->num_trigger_modes =ARRAY_SIZE (dumcnt_trigger_modes); + triggers->signal = &signals[i]; + + values[i].triggers = triggers; + values[i].num_triggers =1 ; + } + + dumcnt =devm_kzalloc (dev,sizeof (*dumcnt),GFP_KERNEL ); +if (!dumcnt) +return -ENOMEM ; + + dumcnt->counter.name =dev_name (dev); + dumcnt->counter.dev = dev; + dumcnt->counter.ops = &dumcnt_ops;drivers/iio/counter/dummy-counter.c line 249
Quoted 10 lines
drivers/iio/counter/dummy-counter.c new file +10
-0+ dumcnt->counter.signals = signals; + dumcnt->counter.num_signals = num_signals; + dumcnt->counter.values = values; + dumcnt->counter.num_values = num_values; + dumcnt->counter.driver_data = dumcnt; + +return devm_iio_counter_register (dev, &dumcnt->counter); +} + +static struct platform_device *dumcnt_device;Support multiple instances - nick this stuff from the IIO dummy driver or more specifically the
industrialio-sw-device.cdrivers/iio/counter/dummy-counter.c line 253
Quoted 4 lines
drivers/iio/counter/dummy-counter.c new file +4
-0+ +static struct platform_driver dumcnt_driver = { + .driver = { + .name ="104-quad-8" Don't do that! Give it it's own name.
drivers/iio/counter/dummy-counter.c line 266
Quoted 13 lines
drivers/iio/counter/dummy-counter.c new file +13
-0+ } +}; + +static void __exit dumcnt_exit(void) +{ +platform_device_unregister (dumcnt_device); +platform_driver_unregister (&dumcnt_driver); +} + +static int __init dumcnt_init (void ) +{ +int err; +General thing, but if we are going to upstream this with the subsystem, make device instantiation happen via configfs.
Quoted 27 lines
drivers/iio/counter/dummy-counter.c new file +27
-0+ dumcnt_device =platform_device_alloc (dumcnt_driver.driver.name,-1 ); +if (!dumcnt_device) +return -ENOMEM ; + + err =platform_device_add (dumcnt_device); +if (err) + goto err_platform_device; + + err =platform_driver_probe (&dumcnt_driver, dumcnt_probe); +if (err) + goto err_platform_driver; + +return 0 ; + +err_platform_driver: +platform_device_del (dumcnt_device); +err_platform_device: +platform_device_put (dumcnt_device); +return err; +} + +module_init (dumcnt_init); +module_exit (dumcnt_exit); + +MODULE_AUTHOR ("William Breathitt Gray <[email protected]>" ); +MODULE_DESCRIPTION ("Dummy counter driver" ); +MODULE_LICENSE ("GPL v2" );
PATCH v3 6/6 iio: 104-quad-8: Add IIO generic counter interface support
In reply to William Breathitt GrayThis patch adds support for the IIO generic counter interface to the 104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not be affected by this patch; all changes are intended as supplemental additions as perceived by the user.
IIO Counter Signals are defined for all quadrature input pairs (A and B), as well as index input lines. However, IIO Counter Triggers are not created for the index input Signals. IIO Counter Values are created for the eight quadrature channel counts, and their respective Signals are associated via IIO Counter Triggers.
The new generic counter interface sysfs attributes expose the same functionality and data available via the existing 104-QUAD-8 device interface. Four IIO Counter Value function modes are available, correlating to the four possible quadrature mode configurations:
"non-quadrature," "quadrature x1," "quadrature x2," and "quadrature x4."
--- drivers/iio/counter/104-quad-8.c | 294 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 277 insertions(+), 17 deletions(-)
drivers/iio/counter/104-quad-8.c +277 -17
@@ -16,6 +16,7 @@#include <linux/bitops.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/iio/counter.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> #include <linux/io.h>
@@ -24,6 +25,7 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/string.h> #include <linux/types.h> #define QUAD8_EXTENT 32
@@ -37,6 +39,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");/** * struct quad8_iio - IIO device private data structure +* @counter: instance of the iio_counter * @preset: array of preset values * @count_mode: array of count mode configurations * @quadrature_mode: array of quadrature mode configurations
@@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");* @base: base port address of the IIO device */ struct quad8_iio { +struct iio_counter counter;unsigned int preset[QUAD8_NUM_COUNTERS ];unsigned int count_mode[QUAD8_NUM_COUNTERS ];unsigned int quadrature_mode[QUAD8_NUM_COUNTERS ];
@@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {QUAD8_COUNT_CHAN (7 ),QUAD8_INDEX_CHAN (7 ) }; +static int quad8_signal_read (struct iio_counter *counter, +struct iio_counter_signal *signal,int *val,int *val2) +{ +struct quad8_iio *const priv = counter->driver_data; + +if (signal->id <16 ) +return -EINVAL ; + + *val = !!(inb (priv->base +0x16 ) &BIT (signal->id -16 )); + +return IIO_VAL_INT ; +} + +static int quad8_trigger_mode_get (struct iio_counter *counter, +struct iio_counter_value *value,struct iio_counter_trigger *trigger) +{ +struct quad8_iio *const priv = counter->driver_data; +const unsigned int mode = priv->quadrature_mode[value->id]; +const unsigned int scale = priv->quadrature_scale[value->id]; +unsigned int direction; +const unsigned int flag_addr = priv->base +2 * value->id +1 ; +const int signal_id = trigger->signal->id %2 ; + +if (mode) +switch (scale) { +case 0 : +/* U/D flag: 1 = up, 0 = down */ +/* direction: 0 = up, 1 = down */ + direction = !(inb (flag_addr) &BIT (5 )); +if (!signal_id) +return direction +1 ; +break ; +case 1 : +if (!signal_id) +return 3 ; +break ; +case 2 : +return 3 ; + } +else +if (!signal_id) +return 1 ; + +return 0 ; +} + +static int quad8_value_read (struct iio_counter *counter, +struct iio_counter_value *value,int *val,int *val2) +{ +struct quad8_iio *const priv = counter->driver_data; +const int base_offset = priv->base +2 * value->id; +unsigned int flags; +unsigned int borrow; +unsigned int carry; +int i; + + flags =inb (base_offset +1 ); + borrow = flags &BIT (0 ); + carry = !!(flags &BIT (1 )); + +/* Borrow XOR Carry effectively doubles count range */ + *val = (borrow ^ carry) <<24 ; + +/* Reset Byte Pointer; transfer Counter to Output Latch */ +outb (0x11 , base_offset +1 ); + +for (i =0 ; i <3 ; i++) + *val |= (unsigned int )inb (base_offset) << (8 * i); + +return IIO_VAL_INT ; +} + +static int quad8_value_write (struct iio_counter *counter, +struct iio_counter_value *value,int val,int val2) +{ +struct quad8_iio *const priv = counter->driver_data; +const int base_offset = priv->base +2 * value->id; +int i; + +/* Only 24-bit values are supported */ +if ((unsigned int )val >0xFFFFFF ) +return -EINVAL ; + +/* Reset Byte Pointer */ +outb (0x01 , base_offset +1 ); + +/* Counter can only be set via Preset Register */ +for (i =0 ; i <3 ; i++) +outb (val >> (8 * i), base_offset); + +/* Transfer Preset Register to Counter */ +outb (0x08 , base_offset +1 ); + +/* Reset Byte Pointer */ +outb (0x01 , base_offset +1 ); + +/* Set Preset Register back to original value */ + val = priv->preset[value->id]; +for (i =0 ; i <3 ; i++) +outb (val >> (8 * i), base_offset); + +/* Reset Borrow, Carry, Compare, and Sign flags */ +outb (0x02 , base_offset +1 ); +/* Reset Error flag */ +outb (0x06 , base_offset +1 ); + +return 0 ; +} + +static int quad8_value_function_set (struct iio_counter *counter, +struct iio_counter_value *value,unsigned int mode) +{ +struct quad8_iio *const priv = counter->driver_data; +const unsigned int mode_cfg = mode <<3 | + priv->count_mode[value->id] <<1 ; +const unsigned int idr_cfg = priv->index_polarity[value->id] <<1 ; +const int base_offset = priv->base +2 * value->id +1 ; + +if (mode) + priv->quadrature_scale[value->id] = mode -1 ; +else { +/* Quadrature scaling only available in quadrature mode */ + priv->quadrature_scale[value->id] =0 ; + +/* Synchronous function not supported in non-quadrature mode */ +if (priv->synchronous_mode[value->id]) { + priv->synchronous_mode[value->id] =0 ; +outb (0x60 | idr_cfg, base_offset); + } + } + + priv->quadrature_mode[value->id] = !!mode; + +/* Load mode configuration to Counter Mode Register */ +outb (0x20 | mode_cfg, base_offset); + +return 0 ; +} + +static int quad8_value_function_get (struct iio_counter *counter, +struct iio_counter_value *value) +{ +struct quad8_iio *const priv = counter->driver_data; +unsigned int quadrature_mode = priv->quadrature_mode[value->id]; + +return (quadrature_mode) ? priv->quadrature_scale[value->id] +1 :0 ; +} + +static const struct iio_counter_ops quad8_ops = { + .signal_read = quad8_signal_read, + .trigger_mode_get = quad8_trigger_mode_get, + .value_read = quad8_value_read, + .value_write = quad8_value_write, + .value_function_set = quad8_value_function_set, + .value_function_get = quad8_value_function_get +}; + +static const char *const quad8_function_modes[] = { +"non-quadrature" , +"quadrature x1" , +"quadrature x2" , +"quadrature x4" +}; + +#define QUAD8_SIGNAL (_id, _name) { \ + .id = _id, \ + .name = _name \ +} + +static const struct iio_counter_signal quad8_signals[] = { +QUAD8_SIGNAL (0 ,"Channel 1 Quadrature A" ), +QUAD8_SIGNAL (1 ,"Channel 1 Quadrature B" ), +QUAD8_SIGNAL (2 ,"Channel 2 Quadrature A" ), +QUAD8_SIGNAL (3 ,"Channel 2 Quadrature B" ), +QUAD8_SIGNAL (4 ,"Channel 3 Quadrature A" ), +QUAD8_SIGNAL (5 ,"Channel 3 Quadrature B" ), +QUAD8_SIGNAL (6 ,"Channel 4 Quadrature A" ), +QUAD8_SIGNAL (7 ,"Channel 4 Quadrature B" ), +QUAD8_SIGNAL (8 ,"Channel 5 Quadrature A" ), +QUAD8_SIGNAL (9 ,"Channel 5 Quadrature B" ), +QUAD8_SIGNAL (10 ,"Channel 6 Quadrature A" ), +QUAD8_SIGNAL (11 ,"Channel 6 Quadrature B" ), +QUAD8_SIGNAL (12 ,"Channel 7 Quadrature A" ), +QUAD8_SIGNAL (13 ,"Channel 7 Quadrature B" ), +QUAD8_SIGNAL (14 ,"Channel 8 Quadrature A" ), +QUAD8_SIGNAL (15 ,"Channel 8 Quadrature B" ), +QUAD8_SIGNAL (16 ,"Channel 1 Index" ), +QUAD8_SIGNAL (17 ,"Channel 2 Index" ), +QUAD8_SIGNAL (18 ,"Channel 3 Index" ), +QUAD8_SIGNAL (19 ,"Channel 4 Index" ), +QUAD8_SIGNAL (20 ,"Channel 5 Index" ), +QUAD8_SIGNAL (21 ,"Channel 6 Index" ), +QUAD8_SIGNAL (22 ,"Channel 7 Index" ), +QUAD8_SIGNAL (23 ,"Channel 8 Index" ) +}; + +#define QUAD8_VALUE (_id, _name) { \ + .id = _id, \ + .name = _name, \ + .mode = 0, \ + .function_modes = quad8_function_modes, \ + .num_function_modes = ARRAY_SIZE(quad8_function_modes) \ +} + +static const struct iio_counter_value quad8_values[] = { +QUAD8_VALUE (0 ,"Channel 1 Count" ),QUAD8_VALUE (1 ,"Channel 2 Count" ), +QUAD8_VALUE (2 ,"Channel 3 Count" ),QUAD8_VALUE (3 ,"Channel 4 Count" ), +QUAD8_VALUE (4 ,"Channel 5 Count" ),QUAD8_VALUE (5 ,"Channel 6 Count" ), +QUAD8_VALUE (6 ,"Channel 7 Count" ),QUAD8_VALUE (7 ,"Channel 8 Count" ) +}; + +static const char *const quad8_trigger_modes[] = { +"none" , +"rising edge" , +"falling edge" , +"both edges" +}; +static int quad8_probe (struct device *dev,unsigned int id) { -struct iio_dev *indio_dev; -struct quad8_iio *priv; +struct iio_counter_signal *signals; +const size_t num_signals =ARRAY_SIZE (quad8_signals); +struct iio_counter_value *values; +const size_t num_values =ARRAY_SIZE (quad8_values); +struct iio_counter_trigger *triggers; +struct quad8_iio *quad8iio;int i, j;unsigned int base_offset; - indio_dev =devm_iio_device_alloc (dev,sizeof (*priv)); -if (!indio_dev) -return -ENOMEM ; - -if (!devm_request_region (dev, base[id],QUAD8_EXTENT , -dev_name (dev))) { +if (!devm_request_region (dev, base[id],QUAD8_EXTENT ,dev_name (dev))) {dev_err (dev,"Unable to lock port addresses (0x%X-0x%X)\n" , base[id], base[id] +QUAD8_EXTENT );return -EBUSY ; } - indio_dev->info = &quad8_info; - indio_dev->modes =INDIO_DIRECT_MODE ; - indio_dev->num_channels =ARRAY_SIZE (quad8_channels); - indio_dev->channels = quad8_channels; - indio_dev->name =dev_name (dev); - indio_dev->dev.parent = dev; + signals =devm_kmalloc (dev,sizeof (quad8_signals),GFP_KERNEL ); +if (!signals) +return -ENOMEM ; + +memcpy (signals, quad8_signals,sizeof (quad8_signals)); + + values =devm_kmalloc (dev,sizeof (quad8_values),GFP_KERNEL ); +if (!values) +return -ENOMEM ; + +memcpy (values, quad8_values,sizeof (quad8_values)); + +/* Associate values with their respective signals */ +for (i =0 ; i < num_values; i++) { + triggers =devm_kmalloc (dev,2 *sizeof (*triggers),GFP_KERNEL ); +if (!triggers) +return -ENOMEM ; + +/* Starts up in non-quadrature mode */ + triggers[0 ].mode =1 ; + triggers[0 ].trigger_modes = quad8_trigger_modes; + triggers[0 ].num_trigger_modes =ARRAY_SIZE (quad8_trigger_modes); + triggers[0 ].signal = &signals[2 * i]; + triggers[1 ].mode =0 ; + triggers[1 ].trigger_modes = quad8_trigger_modes; + triggers[1 ].num_trigger_modes =ARRAY_SIZE (quad8_trigger_modes); + triggers[1 ].signal = &signals[2 * i +1 ]; + + values[i].triggers = triggers; + values[i].num_triggers =2 ; + } + + quad8iio =devm_kzalloc (dev,sizeof (*quad8iio),GFP_KERNEL ); +if (!quad8iio) +return -ENOMEM ; - priv =iio_priv (indio_dev); - priv->base = base[id]; + quad8iio->counter.name =dev_name (dev); + quad8iio->counter.dev = dev; + quad8iio->counter.ops = &quad8_ops; + quad8iio->counter.signals = signals; + quad8iio->counter.num_signals = num_signals; + quad8iio->counter.values = values; + quad8iio->counter.num_values = num_values; + quad8iio->counter.channels = quad8_channels; + quad8iio->counter.num_channels =ARRAY_SIZE (quad8_channels); + quad8iio->counter.info = &quad8_info; + quad8iio->counter.driver_data = quad8iio; + quad8iio->base = base[id];/* Reset all counters and disable interrupt function */ outb (0x01 , base[id] +0x11 );
@@ -579,7 +839,7 @@ static int quad8_probe(struct device *dev, unsigned int id)/* Enable all counters */ outb (0x00 , base[id] +0x11 ); -return devm_iio_device_register (dev, indio_dev); +return devm_iio_counter_register (dev, &quad8iio->counter); }static struct isa_driver quad8_driver = {
Signature · 1 line
2.14.1
Jonathan Cameron
maintainerRe: [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support
Quoted 11 lines · On Thu, 5 Oct 2017 14:14:53 -0400 William Breathitt Gray…
This patch adds support for the IIO generic counter interface to the 104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not be affected by this patch; all changes are intended as supplemental additions as perceived by the user.
IIO Counter Signals are defined for all quadrature input pairs (A and B), as well as index input lines. However, IIO Counter Triggers are not created for the index input Signals. IIO Counter Values are created for the eight quadrature channel counts, and their respective Signals are associated via IIO Counter Triggers.
Quoted 7 lines
The new generic counter interface sysfs attributes expose the same functionality and data available via the existing 104-QUAD-8 device interface. Four IIO Counter Value function modes are available, correlating to the four possible quadrature mode configurations:
"non-quadrature," "quadrature x1," "quadrature x2," and "quadrature x4."Signed-off-by William Breathitt Gray <[email protected]>
A few generic comments inline - nothing significant.
Jonathan
Quoted 294 lines
--- drivers/iio/counter/104-quad-8.c | 294 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 277 insertions(+), 17 deletions(-)drivers/iio/counter/104-quad-8.c +230
-14@@ -16,6 +16,7 @@#include <linux/bitops.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/iio/counter.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> #include <linux/io.h> @@ -24,6 +25,7 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/string.h> #include <linux/types.h> #define QUAD8_EXTENT 32@@ -37,6 +39,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");/** * struct quad8_iio - IIO device private data structure +* @counter: instance of the iio_counter * @preset: array of preset values * @count_mode: array of count mode configurations * @quadrature_mode: array of quadrature mode configurations @@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");* @base: base port address of the IIO device */ struct quad8_iio { +struct iio_counter counter;unsigned int preset[QUAD8_NUM_COUNTERS ];unsigned int count_mode[QUAD8_NUM_COUNTERS ];unsigned int quadrature_mode[QUAD8_NUM_COUNTERS ];@@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {QUAD8_COUNT_CHAN (7 ),QUAD8_INDEX_CHAN (7 ) }; +static int quad8_signal_read (struct iio_counter *counter, +struct iio_counter_signal *signal,int *val,int *val2) +{ +struct quad8_iio *const priv = counter->driver_data; + +if (signal->id <16 ) +return -EINVAL ; + + *val = !!(inb (priv->base +0x16 ) &BIT (signal->id -16 )); + +return IIO_VAL_INT ; +} + +static int quad8_trigger_mode_get (struct iio_counter *counter, +struct iio_counter_value *value,struct iio_counter_trigger *trigger) +{ +struct quad8_iio *const priv = counter->driver_data; +const unsigned int mode = priv->quadrature_mode[value->id]; +const unsigned int scale = priv->quadrature_scale[value->id]; +unsigned int direction; +const unsigned int flag_addr = priv->base +2 * value->id +1 ; +const int signal_id = trigger->signal->id %2 ; + +if (mode) +switch (scale) { +case 0 : +/* U/D flag: 1 = up, 0 = down */ +/* direction: 0 = up, 1 = down */ + direction = !(inb (flag_addr) &BIT (5 )); +if (!signal_id) +return direction +1 ; +break ; +case 1 : +if (!signal_id) +return 3 ; +break ; +case 2 : +return 3 ; + } +else +if (!signal_id) +return 1 ; + +return 0 ; +} + +static int quad8_value_read (struct iio_counter *counter, +struct iio_counter_value *value,int *val,int *val2) +{ +struct quad8_iio *const priv = counter->driver_data; +const int base_offset = priv->base +2 * value->id; +unsigned int flags; +unsigned int borrow; +unsigned int carry; +int i; + + flags =inb (base_offset +1 ); + borrow = flags &BIT (0 ); + carry = !!(flags &BIT (1 )); + +/* Borrow XOR Carry effectively doubles count range */ + *val = (borrow ^ carry) <<24 ; + +/* Reset Byte Pointer; transfer Counter to Output Latch */ +outb (0x11 , base_offset +1 ); + +for (i =0 ; i <3 ; i++) + *val |= (unsigned int )inb (base_offset) << (8 * i); + +return IIO_VAL_INT ; +} + +static int quad8_value_write (struct iio_counter *counter, +struct iio_counter_value *value,int val,int val2) +{ +struct quad8_iio *const priv = counter->driver_data; +const int base_offset = priv->base +2 * value->id; +int i; + +/* Only 24-bit values are supported */ +if ((unsigned int )val >0xFFFFFF ) +return -EINVAL ; + +/* Reset Byte Pointer */ +outb (0x01 , base_offset +1 ); + +/* Counter can only be set via Preset Register */ +for (i =0 ; i <3 ; i++) +outb (val >> (8 * i), base_offset); + +/* Transfer Preset Register to Counter */ +outb (0x08 , base_offset +1 ); + +/* Reset Byte Pointer */ +outb (0x01 , base_offset +1 ); + +/* Set Preset Register back to original value */ + val = priv->preset[value->id]; +for (i =0 ; i <3 ; i++) +outb (val >> (8 * i), base_offset); + +/* Reset Borrow, Carry, Compare, and Sign flags */ +outb (0x02 , base_offset +1 ); +/* Reset Error flag */ +outb (0x06 , base_offset +1 ); + +return 0 ; +} + +static int quad8_value_function_set (struct iio_counter *counter, +struct iio_counter_value *value,unsigned int mode) +{ +struct quad8_iio *const priv = counter->driver_data; +const unsigned int mode_cfg = mode <<3 | + priv->count_mode[value->id] <<1 ; +const unsigned int idr_cfg = priv->index_polarity[value->id] <<1 ; +const int base_offset = priv->base +2 * value->id +1 ; + +if (mode) + priv->quadrature_scale[value->id] = mode -1 ; +else { +/* Quadrature scaling only available in quadrature mode */ + priv->quadrature_scale[value->id] =0 ; + +/* Synchronous function not supported in non-quadrature mode */ +if (priv->synchronous_mode[value->id]) { + priv->synchronous_mode[value->id] =0 ; +outb (0x60 | idr_cfg, base_offset); + } + } + + priv->quadrature_mode[value->id] = !!mode; + +/* Load mode configuration to Counter Mode Register */ +outb (0x20 | mode_cfg, base_offset); + +return 0 ; +} + +static int quad8_value_function_get (struct iio_counter *counter, +struct iio_counter_value *value) +{ +struct quad8_iio *const priv = counter->driver_data; +unsigned int quadrature_mode = priv->quadrature_mode[value->id]; + +return (quadrature_mode) ? priv->quadrature_scale[value->id] +1 :0 ; +} + +static const struct iio_counter_ops quad8_ops = { + .signal_read = quad8_signal_read, + .trigger_mode_get = quad8_trigger_mode_get, + .value_read = quad8_value_read, + .value_write = quad8_value_write, + .value_function_set = quad8_value_function_set, + .value_function_get = quad8_value_function_get +}; + +static const char *const quad8_function_modes[] = { +"non-quadrature" , +"quadrature x1" , +"quadrature x2" , +"quadrature x4" +}; + +#define QUAD8_SIGNAL (_id, _name) { \ + .id = _id, \ + .name = _name \ +} + +static const struct iio_counter_signal quad8_signals[] = { +QUAD8_SIGNAL (0 ,"Channel 1 Quadrature A" ), +QUAD8_SIGNAL (1 ,"Channel 1 Quadrature B" ), +QUAD8_SIGNAL (2 ,"Channel 2 Quadrature A" ), +QUAD8_SIGNAL (3 ,"Channel 2 Quadrature B" ), +QUAD8_SIGNAL (4 ,"Channel 3 Quadrature A" ), +QUAD8_SIGNAL (5 ,"Channel 3 Quadrature B" ), +QUAD8_SIGNAL (6 ,"Channel 4 Quadrature A" ), +QUAD8_SIGNAL (7 ,"Channel 4 Quadrature B" ), +QUAD8_SIGNAL (8 ,"Channel 5 Quadrature A" ), +QUAD8_SIGNAL (9 ,"Channel 5 Quadrature B" ), +QUAD8_SIGNAL (10 ,"Channel 6 Quadrature A" ), +QUAD8_SIGNAL (11 ,"Channel 6 Quadrature B" ), +QUAD8_SIGNAL (12 ,"Channel 7 Quadrature A" ), +QUAD8_SIGNAL (13 ,"Channel 7 Quadrature B" ), +QUAD8_SIGNAL (14 ,"Channel 8 Quadrature A" ), +QUAD8_SIGNAL (15 ,"Channel 8 Quadrature B" ), +QUAD8_SIGNAL (16 ,"Channel 1 Index" ), +QUAD8_SIGNAL (17 ,"Channel 2 Index" ), +QUAD8_SIGNAL (18 ,"Channel 3 Index" ), +QUAD8_SIGNAL (19 ,"Channel 4 Index" ), +QUAD8_SIGNAL (20 ,"Channel 5 Index" ), +QUAD8_SIGNAL (21 ,"Channel 6 Index" ), +QUAD8_SIGNAL (22 ,"Channel 7 Index" ), +QUAD8_SIGNAL (23 ,"Channel 8 Index" ) +}; + +#define QUAD8_VALUE (_id, _name) { \ + .id = _id, \ + .name = _name, \ + .mode = 0, \ + .function_modes = quad8_function_modes, \ + .num_function_modes = ARRAY_SIZE(quad8_function_modes) \ +} + +static const struct iio_counter_value quad8_values[] = { +QUAD8_VALUE (0 ,"Channel 1 Count" ),QUAD8_VALUE (1 ,"Channel 2 Count" ), +QUAD8_VALUE (2 ,"Channel 3 Count" ),QUAD8_VALUE (3 ,"Channel 4 Count" ), +QUAD8_VALUE (4 ,"Channel 5 Count" ),QUAD8_VALUE (5 ,"Channel 6 Count" ), +QUAD8_VALUE (6 ,"Channel 7 Count" ),QUAD8_VALUE (7 ,"Channel 8 Count" ) +}; + +static const char *const quad8_trigger_modes[] = { +"none" , +"rising edge" , +"falling edge" , +"both edges" +}; +static int quad8_probe (struct device *dev,unsigned int id) { -struct iio_dev *indio_dev; -struct quad8_iio *priv; +struct iio_counter_signal *signals; +const size_t num_signals =ARRAY_SIZE (quad8_signals); +struct iio_counter_value *values; +const size_t num_values =ARRAY_SIZE (quad8_values); +struct iio_counter_trigger *triggers; +struct quad8_iio *quad8iio;int i, j;unsigned int base_offset; - indio_dev =devm_iio_device_alloc (dev,sizeof (*priv)); -if (!indio_dev) -return -ENOMEM ; - -if (!devm_request_region (dev, base[id],QUAD8_EXTENT , -dev_name (dev))) { +if (!devm_request_region (dev, base[id],QUAD8_EXTENT ,dev_name (dev))) {dev_err (dev,"Unable to lock port addresses (0x%X-0x%X)\n" , base[id], base[id] +QUAD8_EXTENT );return -EBUSY ; } - indio_dev->info = &quad8_info; - indio_dev->modes =INDIO_DIRECT_MODE ; - indio_dev->num_channels =ARRAY_SIZE (quad8_channels); - indio_dev->channels = quad8_channels; - indio_dev->name =dev_name (dev); - indio_dev->dev.parent = dev; + signals =devm_kmalloc (dev,sizeof (quad8_signals),GFP_KERNEL );
drivers/iio/counter/104-quad-8.c static const struct iio_chan_spec quad8_channels[] = { · line 779
Quoted 10 lines
drivers/iio/counter/104-quad-8.c +10
-0+if (!signals) +return -ENOMEM ; + +memcpy (signals, quad8_signals,sizeof (quad8_signals)); + + values =devm_kmalloc (dev,sizeof (quad8_values),GFP_KERNEL ); +if (!values) +return -ENOMEM ; + +memcpy (values, quad8_values,sizeof (quad8_values));
devm_kmemdup
drivers/iio/counter/104-quad-8.c static const struct iio_chan_spec quad8_channels[] = { · line 788
Quoted 9 lines
drivers/iio/counter/104-quad-8.c +9
-0+ +/* Associate values with their respective signals */ +for (i =0 ; i < num_values; i++) { + triggers =devm_kmalloc (dev,2 *sizeof (*triggers),GFP_KERNEL ); +if (!triggers) +return -ENOMEM ; + +/* Starts up in non-quadrature mode */ + triggers[0 ].mode =1 ;
As I suggested for the dummy driver - use an enum, then the enum naming will make it obvious what the value means here without a comment.
Quoted 42 lines
drivers/iio/counter/104-quad-8.c +28
-3+ triggers[0 ].trigger_modes = quad8_trigger_modes; + triggers[0 ].num_trigger_modes =ARRAY_SIZE (quad8_trigger_modes); + triggers[0 ].signal = &signals[2 * i]; + triggers[1 ].mode =0 ; + triggers[1 ].trigger_modes = quad8_trigger_modes; + triggers[1 ].num_trigger_modes =ARRAY_SIZE (quad8_trigger_modes); + triggers[1 ].signal = &signals[2 * i +1 ]; + + values[i].triggers = triggers; + values[i].num_triggers =2 ; + } + + quad8iio =devm_kzalloc (dev,sizeof (*quad8iio),GFP_KERNEL ); +if (!quad8iio) +return -ENOMEM ; - priv =iio_priv (indio_dev); - priv->base = base[id]; + quad8iio->counter.name =dev_name (dev); + quad8iio->counter.dev = dev; + quad8iio->counter.ops = &quad8_ops; + quad8iio->counter.signals = signals; + quad8iio->counter.num_signals = num_signals; + quad8iio->counter.values = values; + quad8iio->counter.num_values = num_values; + quad8iio->counter.channels = quad8_channels; + quad8iio->counter.num_channels =ARRAY_SIZE (quad8_channels); + quad8iio->counter.info = &quad8_info; + quad8iio->counter.driver_data = quad8iio; + quad8iio->base = base[id];/* Reset all counters and disable interrupt function */ outb (0x01 , base[id] +0x11 );@@ -579,7 +839,7 @@ static int quad8_probe(struct device *dev, unsigned int id)/* Enable all counters */ outb (0x00 , base[id] +0x11 ); -return devm_iio_device_register (dev, indio_dev); +return devm_iio_counter_register (dev, &quad8iio->counter); }static struct isa_driver quad8_driver = {
Jonathan Cameron
maintainerQuoted 58 lines · On Thu, 5 Oct 2017 14:13:14 -0400 William Breathitt Gray…
There have been some significant implementation changes for this version of the patchset. Here's a brief summary of the updates:
- Inline comments throughout the
industrial-counter.cfile; this should help clarify some of the code and aid in the review of the system architecture- Simplification of the driver API; primarily, dynamic updates to the Counter Values, Triggers, and Signals lists are no longer allowed -- all relationships must be defined before the Counter is registered to the system
- Dynamic component registration functions (
iio_counter_value_register, et al.) have been removed, and now the signals, values, and triggers array members serve to register the components; signals, values, and triggers were previously namedinit_signals,init_values, andinit_triggersrespectively -- the "init_" prefix was removed now that dynamic registration is no longer allowed- Requiring static component relationships means linked lists are no longer necessary so all the list related code has been removed; Signals, Triggers, and Values are now accessed directly from the arrays supplied in the
iio_counterstructure- No dynamic additions/substraction of components eliminates the need for mutex locks, so those have been removed as well
- Previously, the entire
iio_counterstructure was copied and stored as theiio_devprivate data; now, only a pointer to the suppliediio_counterstructure is stored-
devm_iio_counter_registeranddevm_iio_counter_unregisterfunctions were incorporated from Benjamin Gaignard's suggestions- Dummy counter driver provided as a reference for a simple counter implementation utilizing the Generic Counter interface
In a previous discussion, Benjamin noted some conflicts between the Generic Counter and the underlying IIO core code: mapping does not always work out seemlessly and sometimes the IIO core functionality exposure provided via
iio_counteris not quite flexible enough. I intend to address these shortcomings in the next version of this patchset hopefully.In particular, I'm leaning towards separating the Generic Counter interface from the IIO core dependency entirely. Since the Generic Counter inteface focuses on a more abstract representation of a counter device, I don't think it provides a suitable interface to map onto IIO core, which appears to focus more on a representation of the physical hardware itself.
Separating Generic Counter from IIO core should allow a driver to use the IIO core functions (
iio_device_register, et al.) to represent the physical nature of their hardware (voltages, currents, etc.), while also utilize the Generic Counter interface to represent the abstract relationships between those Signals and their ultimate counter Values.
Quoted 7 lines
In particular, I'm hoping for the Generic Counter system implementation itself to not require all this hoop-jumping (mapping to IIO core functions, jumping back to the parent
iio_counter, handling non-matching parameter lists betweeniio_counterandiio_dev, etc.); that should make the code simpler to debug, more efficient, and more stable in the long-run. I will consider these advantages and disadvantages before committing to a separation however.
Key things here:
1) userspace ABI - this bit we need to get right
2) driver ABI - right now we'll irritate a few rather nice and hopefully calm people - so it doesn't matter much :)
3) Maintainability. The key thing is to end up with something that is easy to look after. Particularly when the inevitable happens and you can no longer remember how some corner of your own code works.
Quoted 7 lines
Regarding this version of the patchset in particular, I decided to remove the dynamic component registration functionality since I doubt that many devices would require such; almost all, if not all, hardware counters I encountered have static relationships between input lines and the count value (i.e. specific input lines correlate to specific count registers).
Where this is not true of the bit we are writing a driver for the wider system should impose this relationship in most cases. The only exception would be where the counter resources are more constrained than the hardware it is interfaced to. So say we have 2 hardware counters on a system that has 4 encoders but there is some underlying reason why we don't need to use all the encoders at the same time... We can probably figure out how to represent that though even with the initialization time mappings etc.
Something to fix when we care - not from the outset!
Quoted 11 lines
The requirement that Counter component relationships are static has opened the possibility of some optimizations in the Generic Counter interface code:
- Signals, Triggers, and Values are provided as arrays; these arrays could benefit from some amount of sorting based on component IDs
- Searching for a component can be more efficient in a sorted array; currently, the code just walks down the array elements and compare the IDs
I raise the point that mostly (I think) the drivers know exactly where to look in these arrays from the index. My inclination is to give them the ability to provide that knowledge to the core. I wouldn't worry too much for now. There are lots of ways of dealing with the small performance enhancements once the rest is sorted - they don't effect the ABI so we can try what we like ;)
Quoted 6 lines
- Similar to general searching, the arrays are initially verified to gurantee unique IDs; currently, the code walks down the array elements and compares the current element ID with all the elements before and after -- this could be made more efficient with some sorting
It's not a fast path - don't bother. You'll add code complexity to little practical gain - 0.1 seconds on boot up or something less than that...
Quoted 3 lines
There are likely other areas of improvement, so let me know and I'll see what I can do.
It comes up in the individual patch reviews but my main takeaways would be:
1) Naming - we need to deal with trigger (and I think also value -> counter) to limit confusion.
2) Fuller definitions in documentation. Good start on the docs, but you need to be specifying allowable values for parameters as that's what a userspace interface will need to know.
Anyhow, sorry it took me so long to get to it and looking forward to the next revision!
Jonathan
Quoted 30 lines
William Breathitt Gray (6):
iio: Implement counter channel specification andIIO_SIGNALconstant
iio: Introduce the generic counter interface
iio: Documentation: Add IIO Generic Counter sysfs documentation
docs: Add IIO Generic Counter Interface documentation
iio: Add dummy counter driver
iio: 104-quad-8: Add IIO generic counter interface support.../testing/sysfs-bus-iio-generic-counter-sysfs | 63 ++ Documentation/driver-api/iio/generic-counter.txt | 526 ++++++++++++ MAINTAINERS | 7 + drivers/iio/Kconfig | 8 + drivers/iio/Makefile | 1 + drivers/iio/counter/104-quad-8.c | 294 ++++++- drivers/iio/counter/Kconfig | 16 + drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 293 +++++++ drivers/iio/industrialio-core.c | 14 +- drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++ include/linux/iio/counter.h | 166 ++++ include/linux/iio/iio.h | 2 + include/uapi/linux/iio/types.h | 1 + 14 files changed, 2274 insertions(+), 18 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs create mode 100644 Documentation/driver-api/iio/generic-counter.txt create mode 100644 drivers/iio/counter/dummy-counter.c create mode 100644 drivers/iio/industrialio-counter.c create mode 100644 include/linux/iio/counter.h