Introduce the Counter character device interface
Subsystems (11)
v12 cover Introduce the Counter character device interface
Changes in v12:
- Move unlock to after register set in quad8_count_ceiling_write()
- Add locking protection to counter_set_event_node()
- Fix sparse warning by using {} instead of {0}
- Clean up and organize comments for clarity
- Reduce boilerplate by utilizing devm_add_action_or_reset()
- Use switch statements in ti_eqep_action_read() to make possible cases more obvious
I pulled out a lot of bits and pieces to their own patches; hopefully that makes reviewing this patchset much simpler than before. This patchset is also available on my personal git repo for convenience:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v12
The patches preceding "counter: Internalize sysfs interface code" are primarily cleanup and fixes that can be picked up and applied now to the IIO tree if so desired. The "counter: Internalize sysfs interface code" patch as well may be considered for pickup because it is relatively safe and makes no changes to the userspace interface.
To summarize the main points of this patchset: there are no changes to the existing Counter sysfs userspace interface; a Counter character device interface is introduced that allows Counter events and associated data to be read() by userspace; the events_configure() and watch_validate() driver callbacks are introduced to support Counter events; and IRQ support is added to the 104-QUAD-8 driver, serving as an example of how to support the new Counter events functionality.
Something that should still be discussed: should the struct counter_event "status" member be 8 bits or 32 bits wide? This member will provide the return status (system error number) of an event operation.
William Breathitt Gray (17):
counter: 104-quad-8: Return error when invalid mode during
ceiling_write
counter: Return error code on invalid modes
counter: Standardize to ERANGE for limit exceeded errors
counter: Rename counter_signal_value to counter_signal_level
counter: Rename counter_count_function to counter_function
counter: Internalize sysfs interface code
counter: Update counter.h comments to reflect sysfs internalization
docs: counter: Update to reflect sysfs internalization
counter: Move counter enums to uapi header
counter: Add character device interface
docs: counter: Document character device interface tools/counter: Create Counter tools
counter: Implement signalZ_action_component_id sysfs attribute
counter: Implement *_component_id sysfs attributes
counter: Implement events_queue_size sysfs attribute
counter: 104-quad-8: Replace mutex with spinlock
counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
Documentation/ABI/testing/sysfs-bus-counter | 38 +- Documentation/driver-api/generic-counter.rst | 366 +++- .../userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 3 +- drivers/counter/104-quad-8.c | 728 ++++---- drivers/counter/Kconfig | 6 +- drivers/counter/Makefile | 1 + drivers/counter/counter-chrdev.c | 498 ++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 182 ++ drivers/counter/counter-sysfs.c | 953 +++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------- drivers/counter/ftm-quaddec.c | 59 +- drivers/counter/intel-qep.c | 150 +- drivers/counter/interrupt-cnt.c | 73 +- drivers/counter/microchip-tcb-capture.c | 103 +- drivers/counter/stm32-lptimer-cnt.c | 176 +- drivers/counter/stm32-timer-cnt.c | 147 +- drivers/counter/ti-eqep.c | 205 ++- include/linux/counter.h | 716 ++++---- include/linux/counter_enum.h | 45 - include/uapi/linux/counter.h | 133 ++ tools/Makefile | 13 +- tools/counter/Build | 1 + tools/counter/Makefile | 53 + tools/counter/counter_example.c | 95 ++ 27 files changed, 3501 insertions(+), 2767 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h create mode 100644 include/uapi/linux/counter.h create mode 100644 tools/counter/Build create mode 100644 tools/counter/Makefile create mode 100644 tools/counter/counter_example.c
base-commit: 6cbb3aa0f9d5d23221df787cf36f74d3866fdb78
Signature · 1 line
2.32.0
PATCH v12 1/17 counter: 104-quad-8: Return error when invalid mode during ceiling_write
The 104-QUAD-8 only has two count modes where a ceiling value makes
sense: Range Limit and Modulo-N. Outside of these two modes, setting a ceiling value is an invalid operation -- so let's report it as such by returning -EINVAL.
--- drivers/counter/104-quad-8.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/counter/104-quad-8.c +3 -2
@@ -715,12 +715,13 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,case 1 :case 3 :quad8_preset_register_set (priv, count->id, ceiling); -break ; +mutex_unlock (&priv->lock); +return len; }mutex_unlock (&priv->lock); -return len; +return -EINVAL ; } static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
Signature · 1 line
2.32.0
PATCH v12 2/17 counter: Return error code on invalid modes
In reply to William Breathitt GrayOnly a select set of modes (function, action, etc.) are valid for a given device configuration. This patch ensures that invalid modes result in a return -EINVAL. Such a situation should never occur in reality, but it's good to define a default switch cases for the sake of making the intent of the code clear.
--- drivers/counter/104-quad-8.c | 20 ++++++++++++------ drivers/counter/microchip-tcb-capture.c | 6 ++++++ drivers/counter/stm32-lptimer-cnt.c | 10 +++++---- drivers/counter/ti-eqep.c | 27 +++++++++++++++---------- 4 files changed, 42 insertions(+), 21 deletions(-)
drivers/counter/104-quad-8.c +14 -6
@@ -273,6 +273,10 @@ static int quad8_function_set(struct counter_device *counter, *scale =2 ; mode_cfg |=QUAD8_CMR_QUADRATURE_X4 ;break ; +default : +/* should never reach this path */ +mutex_unlock (&priv->lock); +return -EINVAL ; } }
@@ -349,7 +353,7 @@ static int quad8_action_get(struct counter_device *counter,case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION :if (synapse->signal->id == signal_a_id) *action =QUAD8_SYNAPSE_ACTION_RISING_EDGE ; -break ; +return 0 ;case QUAD8_COUNT_FUNCTION_QUADRATURE_X1 :if (synapse->signal->id == signal_a_id) {quad8_direction_get (counter, count, &direction);
@@ -359,17 +363,18 @@ static int quad8_action_get(struct counter_device *counter,else *action =QUAD8_SYNAPSE_ACTION_FALLING_EDGE ; } -break ; +return 0 ;case QUAD8_COUNT_FUNCTION_QUADRATURE_X2 :if (synapse->signal->id == signal_a_id) *action =QUAD8_SYNAPSE_ACTION_BOTH_EDGES ; -break ; +return 0 ;case QUAD8_COUNT_FUNCTION_QUADRATURE_X4 : *action =QUAD8_SYNAPSE_ACTION_BOTH_EDGES ; -break ; +return 0 ; +default : +/* should never reach this path */ +return -EINVAL ; } - -return 0 ; }static const struct counter_ops quad8_ops = {
@@ -529,6 +534,9 @@ static int quad8_count_mode_set(struct counter_device *counter,case COUNTER_COUNT_MODE_MODULO_N : cnt_mode =3 ;break ; +default : +/* should never reach this path */ +return -EINVAL ; }mutex_lock (&priv->lock);
drivers/counter/microchip-tcb-capture.c +6 -0
@@ -133,6 +133,9 @@ static int mchp_tc_count_function_set(struct counter_device *counter, bmr |=ATMEL_TC_QDEN |ATMEL_TC_POSEN ; cmr |=ATMEL_TC_ETRGEDG_RISING |ATMEL_TC_ABETRG |ATMEL_TC_XC0 ;break ; +default : +/* should never reach this path */ +return -EINVAL ; }regmap_write (priv->regmap,ATMEL_TC_BMR , bmr);
@@ -226,6 +229,9 @@ static int mchp_tc_count_action_set(struct counter_device *counter,case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE : edge =ATMEL_TC_ETRGEDG_BOTH ;break ; +default : +/* should never reach this path */ +return -EINVAL ; } return regmap_write_bits(priv->regmap,
drivers/counter/stm32-lptimer-cnt.c +6 -4
@@ -206,9 +206,10 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter, priv->quadrature_mode =1 ; priv->polarity =STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES ;return 0 ; +default : +/* should never reach this path */ +return -EINVAL ; } - -return -EINVAL ; } static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
@@ -326,9 +327,10 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,case STM32_LPTIM_ENCODER_BOTH_EDGE : *action = priv->polarity;return 0 ; +default : +/* should never reach this path */ +return -EINVAL ; } - -return -EINVAL ; } static int stm32_lptim_cnt_action_set(struct counter_device *counter,
drivers/counter/ti-eqep.c +16 -11
@@ -157,7 +157,7 @@ static int ti_eqep_action_get(struct counter_device *counter,* QEPA and QEPB trigger QCLK. */ *action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES ; -break ; +return 0 ;case TI_EQEP_COUNT_FUNC_DIR_COUNT :/* In direction-count mode only rising edge of QEPA is counted * and QEPB gives direction.
@@ -165,12 +165,14 @@ static int ti_eqep_action_get(struct counter_device *counter,switch (synapse->signal->id) {case TI_EQEP_SIGNAL_QEPA : *action =TI_EQEP_SYNAPSE_ACTION_RISING_EDGE ; -break ; -default : +return 0 ; +case TI_EQEP_SIGNAL_QEPB : *action =TI_EQEP_SYNAPSE_ACTION_NONE ; -break ; +return 0 ; +default : +/* should never reach this path */ +return -EINVAL ; } -break ;case TI_EQEP_COUNT_FUNC_UP_COUNT :case TI_EQEP_COUNT_FUNC_DOWN_COUNT :/* In up/down-count modes only QEPA is counted and QEPB is not
@@ -186,15 +188,18 @@ static int ti_eqep_action_get(struct counter_device *counter, *action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES ; else *action =TI_EQEP_SYNAPSE_ACTION_RISING_EDGE ; -break ; -default : +return 0 ; +case TI_EQEP_SIGNAL_QEPB : *action =TI_EQEP_SYNAPSE_ACTION_NONE ; -break ; +return 0 ; +default : +/* should never reach this path */ +return -EINVAL ; } -break ; +default : +/* should never reach this path */ +return -EINVAL ; } - -return 0 ; }static const struct counter_ops ti_eqep_counter_ops = {
Signature · 1 line
2.32.0
Re: [PATCH v12 02/17] counter: Return error code on invalid modes
Quoted 6 lines · William Breathitt Gray
Only a select set of modes (function, action, etc.) are valid for a given device configuration. This patch ensures that invalid modes result in a return
-EINVAL. Such a situation should never occur in reality, but it's good to define a default switch cases for the sake of making the intent of the code clear.
Quoted 8 lines
Cc Kamel Bouhara <[email protected]>Cc Maxime Coquelin <[email protected]>Cc Alexandre Torgue <[email protected]>Cc David Lechner <[email protected]>Reviewed-by Fabrice Gasnier <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>---
PATCH v12 3/17 counter: Standardize to ERANGE for limit exceeded errors
In reply to William Breathitt GrayERANGE is a semantically better error code to return when an argument value falls outside the supported limit range of a device.
--- drivers/counter/104-quad-8.c | 6 +++--- drivers/counter/intel-qep.c | 2 +- drivers/counter/interrupt-cnt.c | 3 +++ drivers/counter/stm32-lptimer-cnt.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-)
drivers/counter/104-quad-8.c +3 -3
@@ -154,7 +154,7 @@ static int quad8_count_write(struct counter_device *counter,/* Only 24-bit values are supported */ if (val >0xFFFFFF ) -return -EINVAL ; +return -ERANGE ;mutex_lock (&priv->lock);
@@ -669,7 +669,7 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,/* Only 24-bit values are supported */ if (preset >0xFFFFFF ) -return -EINVAL ; +return -ERANGE ;mutex_lock (&priv->lock);
@@ -714,7 +714,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,/* Only 24-bit values are supported */ if (ceiling >0xFFFFFF ) -return -EINVAL ; +return -ERANGE ;mutex_lock (&priv->lock);
drivers/counter/intel-qep.c +1 -1
@@ -320,7 +320,7 @@ static ssize_t spike_filter_ns_write(struct counter_device *counter, }if (length >INTEL_QEPFLT_MAX_COUNT (length)) -return -EINVAL ; +return -ERANGE ;mutex_lock (&qep->lock);if (qep->enabled) {
drivers/counter/interrupt-cnt.c +3 -0
@@ -107,6 +107,9 @@ static int interrupt_cnt_write(struct counter_device *counter, {struct interrupt_cnt_priv *priv = counter->priv; + if (val != (typeof(priv->count.counter))val) +return -ERANGE ; +atomic_set (&priv->count, val);return 0 ;
drivers/counter/stm32-lptimer-cnt.c +1 -1
@@ -283,7 +283,7 @@ static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter,return ret;if (ceiling >STM32_LPTIM_MAX_ARR ) -return -EINVAL ; +return -ERANGE ; priv->ceiling = ceiling;
Signature · 1 line
2.32.0
PATCH v12 4/17 counter: Rename counter_signal_value to counter_signal_level
In reply to William Breathitt GraySignal values will always be levels so let's be explicit it about it to make the intent of the code clear.
--- drivers/counter/104-quad-8.c | 5 +++-- drivers/counter/counter.c | 12 ++++++------ drivers/counter/interrupt-cnt.c | 4 ++-- drivers/counter/microchip-tcb-capture.c | 4 ++-- include/linux/counter.h | 12 ++++++------ 5 files changed, 19 insertions(+), 18 deletions(-)
drivers/counter/104-quad-8.c +3 -2
@@ -97,7 +97,8 @@ struct quad8 {#define QUAD8_CMR_QUADRATURE_X4 0x18static int quad8_signal_read (struct counter_device *counter, -struct counter_signal *signal,enum counter_signal_value *val) +struct counter_signal *signal, +enum counter_signal_level *level) {const struct quad8 *const priv = counter->priv;unsigned int state;
@@ -109,7 +110,7 @@ static int quad8_signal_read(struct counter_device *counter, state =inb (priv->base +QUAD8_REG_INDEX_INPUT_LEVELS ) &BIT (signal->id -16 ); - *val = (state) ?COUNTER_SIGNAL_HIGH :COUNTER_SIGNAL_LOW ; + *level = (state) ?COUNTER_SIGNAL_LEVEL_HIGH :COUNTER_SIGNAL_LEVEL_LOW ;return 0 ; }
drivers/counter/counter.c +6 -6
@@ -289,9 +289,9 @@ struct counter_signal_unit {struct counter_signal *signal; }; -static const char *const counter_signal_value_str[] = { - [COUNTER_SIGNAL_LOW ] ="low" , - [COUNTER_SIGNAL_HIGH ] ="high" +static const char *const counter_signal_level_str[] = { + [COUNTER_SIGNAL_LEVEL_LOW ] ="low" , + [COUNTER_SIGNAL_LEVEL_HIGH ] ="high" }; static ssize_t counter_signal_show(struct device *dev,
@@ -302,13 +302,13 @@ static ssize_t counter_signal_show(struct device *dev,const struct counter_signal_unit *const component = devattr->component;struct counter_signal *const signal = component->signal;int err; -enum counter_signal_value val; +enum counter_signal_level level; - err = counter->ops->signal_read (counter, signal, &val); + err = counter->ops->signal_read (counter, signal, &level);if (err)return err; -return sprintf (buf,"%s\n" , counter_signal_value_str[val]); +return sprintf (buf,"%s\n" , counter_signal_level_str[level]); }struct counter_name_unit {
drivers/counter/interrupt-cnt.c +2 -2
@@ -130,7 +130,7 @@ static int interrupt_cnt_function_get(struct counter_device *counter,static int interrupt_cnt_signal_read (struct counter_device *counter,struct counter_signal *signal, -enum counter_signal_value *val) +enum counter_signal_level *level) {struct interrupt_cnt_priv *priv = counter->priv;int ret;
@@ -142,7 +142,7 @@ static int interrupt_cnt_signal_read(struct counter_device *counter,if (ret <0 )return ret; - *val = ret ?COUNTER_SIGNAL_HIGH :COUNTER_SIGNAL_LOW ; + *level = ret ?COUNTER_SIGNAL_LEVEL_HIGH :COUNTER_SIGNAL_LEVEL_LOW ;return 0 ; }
drivers/counter/microchip-tcb-capture.c +2 -2
@@ -158,7 +158,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,static int mchp_tc_count_signal_read (struct counter_device *counter,struct counter_signal *signal, -enum counter_signal_value *val) +enum counter_signal_level *lvl) {struct mchp_tc_data *const priv = counter->priv;bool sigstatus;
@@ -171,7 +171,7 @@ static int mchp_tc_count_signal_read(struct counter_device *counter,else sigstatus = (sr &ATMEL_TC_MTIOA ); - *val = sigstatus ?COUNTER_SIGNAL_HIGH :COUNTER_SIGNAL_LOW ; + *lvl = sigstatus ?COUNTER_SIGNAL_LEVEL_HIGH :COUNTER_SIGNAL_LEVEL_LOW ;return 0 ; }
include/linux/counter.h +6 -6
@@ -290,16 +290,16 @@ struct counter_device_state {const struct attribute_group **groups; }; -enum counter_signal_value { -COUNTER_SIGNAL_LOW =0 , -COUNTER_SIGNAL_HIGH +enum counter_signal_level { +COUNTER_SIGNAL_LEVEL_LOW , +COUNTER_SIGNAL_LEVEL_HIGH , };/** * struct counter_ops - Callbacks from driver * @signal_read: optional read callback for Signal attribute. The read -* value of the respective Signal should be passed back via -* the val parameter. +* level of the respective Signal should be passed back via +* the level parameter. * @count_read: optional read callback for Count attribute. The read * value of the respective Count should be passed back via * the val parameter.
@@ -324,7 +324,7 @@ enum counter_signal_value {struct counter_ops {int (*signal_read)(struct counter_device *counter,struct counter_signal *signal, -enum counter_signal_value *val); +enum counter_signal_level *level);int (*count_read)(struct counter_device *counter,struct counter_count *count,unsigned long *val); int (*count_write)(struct counter_device *counter,
Signature · 1 line
2.32.0
PATCH v12 5/17 counter: Rename counter_count_function to counter_function
In reply to William Breathitt GrayThe phrase "Counter Count function" is verbose and unintentionally implies that function is a Count extension. This patch adjusts the Counter subsystem code to use the more direct "Counter function" phrase to make the intent of this code clearer.
--- drivers/counter/104-quad-8.c | 10 +++---- drivers/counter/counter.c | 38 ++++++++++++------------- drivers/counter/ftm-quaddec.c | 5 ++-- drivers/counter/intel-qep.c | 4 +-- drivers/counter/interrupt-cnt.c | 4 +-- drivers/counter/microchip-tcb-capture.c | 4 +-- drivers/counter/stm32-lptimer-cnt.c | 6 ++-- drivers/counter/stm32-timer-cnt.c | 10 +++---- drivers/counter/ti-eqep.c | 10 +++---- include/linux/counter.h | 20 ++++++------- 10 files changed, 55 insertions(+), 56 deletions(-)
drivers/counter/104-quad-8.c +5 -5
@@ -194,11 +194,11 @@ enum quad8_count_function {QUAD8_COUNT_FUNCTION_QUADRATURE_X4 }; -static const enum counter_count_function quad8_count_functions_list[] = { - [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION ] =COUNTER_COUNT_FUNCTION_PULSE_DIRECTION , - [QUAD8_COUNT_FUNCTION_QUADRATURE_X1 ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A , - [QUAD8_COUNT_FUNCTION_QUADRATURE_X2 ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A , - [QUAD8_COUNT_FUNCTION_QUADRATURE_X4 ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X4 +static const enum counter_function quad8_count_functions_list[] = { + [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION ] =COUNTER_FUNCTION_PULSE_DIRECTION , + [QUAD8_COUNT_FUNCTION_QUADRATURE_X1 ] =COUNTER_FUNCTION_QUADRATURE_X1_A , + [QUAD8_COUNT_FUNCTION_QUADRATURE_X2 ] =COUNTER_FUNCTION_QUADRATURE_X2_A , + [QUAD8_COUNT_FUNCTION_QUADRATURE_X4 ] =COUNTER_FUNCTION_QUADRATURE_X4 }; static int quad8_function_get(struct counter_device *counter,
drivers/counter/counter.c +19 -19
@@ -744,15 +744,15 @@ static ssize_t counter_count_store(struct device *dev,return len; } -static const char *const counter_count_function_str[] = { - [COUNTER_COUNT_FUNCTION_INCREASE ] ="increase" , - [COUNTER_COUNT_FUNCTION_DECREASE ] ="decrease" , - [COUNTER_COUNT_FUNCTION_PULSE_DIRECTION ] ="pulse-direction" , - [COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A ] ="quadrature x1 a" , - [COUNTER_COUNT_FUNCTION_QUADRATURE_X1_B ] ="quadrature x1 b" , - [COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A ] ="quadrature x2 a" , - [COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B ] ="quadrature x2 b" , - [COUNTER_COUNT_FUNCTION_QUADRATURE_X4 ] ="quadrature x4" +static const char *const counter_function_str[] = { + [COUNTER_FUNCTION_INCREASE ] ="increase" , + [COUNTER_FUNCTION_DECREASE ] ="decrease" , + [COUNTER_FUNCTION_PULSE_DIRECTION ] ="pulse-direction" , + [COUNTER_FUNCTION_QUADRATURE_X1_A ] ="quadrature x1 a" , + [COUNTER_FUNCTION_QUADRATURE_X1_B ] ="quadrature x1 b" , + [COUNTER_FUNCTION_QUADRATURE_X2_A ] ="quadrature x2 a" , + [COUNTER_FUNCTION_QUADRATURE_X2_B ] ="quadrature x2 b" , + [COUNTER_FUNCTION_QUADRATURE_X4 ] ="quadrature x4" }; static ssize_t counter_function_show(struct device *dev,
@@ -764,7 +764,7 @@ static ssize_t counter_function_show(struct device *dev,const struct counter_count_unit *const component = devattr->component;struct counter_count *const count = component->count;size_t func_index; -enum counter_count_function function; +enum counter_function function; err = counter->ops->function_get (counter, count, &func_index);if (err)
@@ -773,7 +773,7 @@ static ssize_t counter_function_show(struct device *dev, count->function = func_index; function = count->functions_list[func_index]; -return sprintf (buf,"%s\n" , counter_count_function_str[function]); +return sprintf (buf,"%s\n" , counter_function_str[function]); } static ssize_t counter_function_store(struct device *dev,
@@ -785,14 +785,14 @@ static ssize_t counter_function_store(struct device *dev,struct counter_count *const count = component->count;const size_t num_functions = count->num_functions;size_t func_index; -enum counter_count_function function; +enum counter_function function;int err;struct counter_device *const counter =dev_get_drvdata (dev);/* Find requested Count function mode */ for (func_index =0 ; func_index < num_functions; func_index++) { function = count->functions_list[func_index]; -if (sysfs_streq (buf, counter_count_function_str[function])) +if (sysfs_streq (buf, counter_function_str[function]))break ; }/* Return error if requested Count function mode not found */
@@ -880,25 +880,25 @@ static int counter_count_ext_register( }struct counter_func_avail_unit { -const enum counter_count_function *functions_list; +const enum counter_function *functions_list;size_t num_functions; }; -static ssize_t counter_count_function_available_show (struct device *dev, +static ssize_t counter_function_available_show (struct device *dev,struct device_attribute *attr,char *buf) {const struct counter_device_attr *const devattr =to_counter_attr (attr);const struct counter_func_avail_unit *const component = devattr->component; -const enum counter_count_function *const func_list = component->functions_list; +const enum counter_function *const func_list = component->functions_list;const size_t num_functions = component->num_functions;size_t i; -enum counter_count_function function; +enum counter_function function;ssize_t len =0 ;for (i =0 ; i < num_functions; i++) { function = func_list[i]; len +=sprintf (buf + len,"%s\n" , - counter_count_function_str[function]); + counter_function_str[function]); }return len;
@@ -968,7 +968,7 @@ static int counter_count_attributes_create( parm.group = group; parm.prefix ="" ; parm.name ="function_available" ; - parm.show = counter_count_function_available_show; + parm.show = counter_function_available_show; parm.store =NULL ; parm.component = avail_comp; err =counter_attribute_create (&parm);
drivers/counter/ftm-quaddec.c +2 -3
@@ -171,9 +171,8 @@ enum ftm_quaddec_count_function { FTM_QUADDEC_COUNT_ENCODER_MODE_1, }; -static const enum counter_count_function ftm_quaddec_count_functions[] = { - [FTM_QUADDEC_COUNT_ENCODER_MODE_1 ] = -COUNTER_COUNT_FUNCTION_QUADRATURE_X4 +static const enum counter_function ftm_quaddec_count_functions[] = { + [FTM_QUADDEC_COUNT_ENCODER_MODE_1 ] =COUNTER_FUNCTION_QUADRATURE_X4 }; static int ftm_quaddec_count_read(struct counter_device *counter,
drivers/counter/intel-qep.c +2 -2
@@ -127,8 +127,8 @@ static int intel_qep_count_read(struct counter_device *counter,return 0 ; } -static const enum counter_count_function intel_qep_count_functions[] = { -COUNTER_COUNT_FUNCTION_QUADRATURE_X4 , +static const enum counter_function intel_qep_count_functions[] = { +COUNTER_FUNCTION_QUADRATURE_X4 , }; static int intel_qep_function_get(struct counter_device *counter,
drivers/counter/interrupt-cnt.c +2 -2
@@ -115,8 +115,8 @@ static int interrupt_cnt_write(struct counter_device *counter,return 0 ; } -static const enum counter_count_function interrupt_cnt_functions[] = { -COUNTER_COUNT_FUNCTION_INCREASE , +static const enum counter_function interrupt_cnt_functions[] = { +COUNTER_FUNCTION_INCREASE , }; static int interrupt_cnt_function_get(struct counter_device *counter,
drivers/counter/microchip-tcb-capture.c +2 -2
@@ -38,8 +38,8 @@ enum mchp_tc_count_function { };static const enum counter_count_function mchp_tc_count_functions[] = { - [MCHP_TC_FUNCTION_INCREASE ] =COUNTER_COUNT_FUNCTION_INCREASE , - [MCHP_TC_FUNCTION_QUADRATURE ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X4 , + [MCHP_TC_FUNCTION_INCREASE ] =COUNTER_FUNCTION_INCREASE , + [MCHP_TC_FUNCTION_QUADRATURE ] =COUNTER_FUNCTION_QUADRATURE_X4 , };enum mchp_tc_synapse_action {
drivers/counter/stm32-lptimer-cnt.c +3 -3
@@ -134,9 +134,9 @@ enum stm32_lptim_cnt_function { STM32_LPTIM_ENCODER_BOTH_EDGE, }; -static const enum counter_count_function stm32_lptim_cnt_functions[] = { - [STM32_LPTIM_COUNTER_INCREASE ] =COUNTER_COUNT_FUNCTION_INCREASE , - [STM32_LPTIM_ENCODER_BOTH_EDGE ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X4 , +static const enum counter_function stm32_lptim_cnt_functions[] = { + [STM32_LPTIM_COUNTER_INCREASE ] =COUNTER_FUNCTION_INCREASE , + [STM32_LPTIM_ENCODER_BOTH_EDGE ] =COUNTER_FUNCTION_QUADRATURE_X4 , };enum stm32_lptim_synapse_action {
drivers/counter/stm32-timer-cnt.c +5 -5
@@ -50,11 +50,11 @@ enum stm32_count_function { STM32_COUNT_ENCODER_MODE_3, }; -static const enum counter_count_function stm32_count_functions[] = { - [STM32_COUNT_SLAVE_MODE_DISABLED ] =COUNTER_COUNT_FUNCTION_INCREASE , - [STM32_COUNT_ENCODER_MODE_1 ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A , - [STM32_COUNT_ENCODER_MODE_2 ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B , - [STM32_COUNT_ENCODER_MODE_3 ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X4 , +static const enum counter_function stm32_count_functions[] = { + [STM32_COUNT_SLAVE_MODE_DISABLED ] =COUNTER_FUNCTION_INCREASE , + [STM32_COUNT_ENCODER_MODE_1 ] =COUNTER_FUNCTION_QUADRATURE_X2_A , + [STM32_COUNT_ENCODER_MODE_2 ] =COUNTER_FUNCTION_QUADRATURE_X2_B , + [STM32_COUNT_ENCODER_MODE_3 ] =COUNTER_FUNCTION_QUADRATURE_X4 , }; static int stm32_count_read(struct counter_device *counter,
drivers/counter/ti-eqep.c +5 -5
@@ -294,11 +294,11 @@ static struct counter_signal ti_eqep_signals[] = { }, }; -static const enum counter_count_function ti_eqep_position_functions[] = { - [TI_EQEP_COUNT_FUNC_QUAD_COUNT ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X4 , - [TI_EQEP_COUNT_FUNC_DIR_COUNT ] =COUNTER_COUNT_FUNCTION_PULSE_DIRECTION , - [TI_EQEP_COUNT_FUNC_UP_COUNT ] =COUNTER_COUNT_FUNCTION_INCREASE , - [TI_EQEP_COUNT_FUNC_DOWN_COUNT ] =COUNTER_COUNT_FUNCTION_DECREASE , +static const enum counter_function ti_eqep_position_functions[] = { + [TI_EQEP_COUNT_FUNC_QUAD_COUNT ] =COUNTER_FUNCTION_QUADRATURE_X4 , + [TI_EQEP_COUNT_FUNC_DIR_COUNT ] =COUNTER_FUNCTION_PULSE_DIRECTION , + [TI_EQEP_COUNT_FUNC_UP_COUNT ] =COUNTER_FUNCTION_INCREASE , + [TI_EQEP_COUNT_FUNC_DOWN_COUNT ] =COUNTER_FUNCTION_DECREASE , };static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = {
include/linux/counter.h +10 -10
@@ -162,15 +162,15 @@ struct counter_count_ext {void *priv; }; -enum counter_count_function { -COUNTER_COUNT_FUNCTION_INCREASE =0 , -COUNTER_COUNT_FUNCTION_DECREASE , -COUNTER_COUNT_FUNCTION_PULSE_DIRECTION , -COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A , -COUNTER_COUNT_FUNCTION_QUADRATURE_X1_B , -COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A , -COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B , -COUNTER_COUNT_FUNCTION_QUADRATURE_X4 +enum counter_function { +COUNTER_FUNCTION_INCREASE =0 , +COUNTER_FUNCTION_DECREASE , +COUNTER_FUNCTION_PULSE_DIRECTION , +COUNTER_FUNCTION_QUADRATURE_X1_A , +COUNTER_FUNCTION_QUADRATURE_X1_B , +COUNTER_FUNCTION_QUADRATURE_X2_A , +COUNTER_FUNCTION_QUADRATURE_X2_B , +COUNTER_FUNCTION_QUADRATURE_X4 };/**
@@ -192,7 +192,7 @@ struct counter_count {const char *name;size_t function; -const enum counter_count_function *functions_list; +const enum counter_function *functions_list;size_t num_functions;struct counter_synapse *synapses;
Signature · 1 line
2.32.0
PATCH v12 6/17 counter: Internalize sysfs interface code
In reply to William Breathitt GrayThis is a reimplementation of the Generic Counter driver interface. There are no modifications to the Counter subsystem userspace interface, so existing userspace applications should continue to run seamlessly.
The purpose of this patch is to internalize the sysfs interface code among the various counter drivers into a shared module. Counter drivers pass and take data natively (i.e. u8, u64, etc.) and the shared counter module handles the translation between the sysfs interface and the device drivers. This guarantees a standard userspace interface for all counter drivers, and helps generalize the Generic Counter driver ABI in order to support the Generic Counter chrdev interface (introduced in a subsequent patch) without significant changes to the existing counter drivers.
Note, Counter device registration is the same as before: drivers populate a struct counter_device with components and callbacks, then pass the structure to the devm_counter_register function. However, what's different now is how the Counter subsystem code handles this registration internally.
Whereas before callbacks would interact directly with sysfs data, this interaction is now abstracted and instead callbacks interact with native C data types. The counter_comp structure forms the basis for Counter extensions.
The counter-sysfs.c file contains the code to parse through the counter_device structure and register the requested components and extensions. Attributes are created and populated based on type, with respective translation functions to handle the mapping between sysfs and the counter driver callbacks.
The translation performed for each attribute is straightforward: the attribute type and data is parsed from the counter_attribute structure, the respective counter driver read/write callback is called, and sysfs I/O is handled before or after the driver read/write function is called.
--- MAINTAINERS | 1 - drivers/counter/104-quad-8.c | 449 +++---- drivers/counter/Makefile | 1 + drivers/counter/counter-core.c | 145 +++ drivers/counter/counter-sysfs.c | 846 +++++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------------- drivers/counter/ftm-quaddec.c | 56 +- drivers/counter/intel-qep.c | 144 +-- drivers/counter/interrupt-cnt.c | 62 +- drivers/counter/microchip-tcb-capture.c | 93 +- drivers/counter/stm32-lptimer-cnt.c | 162 ++- drivers/counter/stm32-timer-cnt.c | 147 +-- drivers/counter/ti-eqep.c | 180 +-- include/linux/counter.h | 658 +++++----- include/linux/counter_enum.h | 45 - 16 files changed, 1920 insertions(+), 2578 deletions(-) create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h
MAINTAINERS +0 -1
@@ -4687,7 +4687,6 @@ F: Documentation/ABI/testing/sysfs-bus-counter F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h -F: include/linux/counter_enum.h CP2615 I2C DRIVER M: Bence Csókás <[email protected]>
drivers/counter/104-quad-8.c +191 -258
796 lines not on this page · raw message
drivers/counter/Makefile +1 -0
@@ -4,6 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o +counter-y := counter-core.o counter-sysfs.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o
drivers/counter/counter-core.c new file +145 -0
145 lines not on this page · raw message
drivers/counter/counter-sysfs.c new file +846 -0
846 lines not on this page · raw message
drivers/counter/counter-sysfs.h new file +13 -0
@@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Counter sysfs interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _COUNTER_SYSFS_H_ +#define _COUNTER_SYSFS_H_ + +#include <linux/counter.h> + +int counter_sysfs_add (struct counter_device *const counter); + +#endif /* _COUNTER_SYSFS_H_ */
drivers/counter/counter.c deleted +0 -1496
1,186 lines not on this page · raw message
drivers/counter/ftm-quaddec.c +23 -33
56 lines not on this page · raw message
drivers/counter/intel-qep.c +61 -83
144 lines not on this page · raw message
drivers/counter/interrupt-cnt.c +26 -36
62 lines not on this page · raw message
drivers/counter/microchip-tcb-capture.c +40 -53
93 lines not on this page · raw message
drivers/counter/stm32-lptimer-cnt.c +79 -83
162 lines not on this page · raw message
drivers/counter/stm32-timer-cnt.c +59 -88
147 lines not on this page · raw message
drivers/counter/ti-eqep.c +98 -82
180 lines not on this page · raw message
include/linux/counter.h +338 -320
658 lines not on this page · raw message
include/linux/counter_enum.h deleted +0 -45
45 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.32.0
Part of the diff is not shown here. View the raw message.
Re: [PATCH v12 06/17] counter: Internalize sysfs interface code
Quoted 48 lines · William Breathitt Gray
This is a reimplementation of the Generic Counter driver interface. There are no modifications to the Counter subsystem userspace interface, so existing userspace applications should continue to run seamlessly.
The purpose of this patch is to internalize the sysfs interface code among the various counter drivers into a shared module. Counter drivers pass and take data natively (i.e. u8, u64, etc.) and the shared counter module handles the translation between the sysfs interface and the device drivers. This guarantees a standard userspace interface for all counter drivers, and helps generalize the Generic Counter driver ABI in order to support the Generic Counter chrdev interface (introduced in a subsequent patch) without significant changes to the existing counter drivers.
Note, Counter device registration is the same as before: drivers populate a struct
counter_devicewith components and callbacks, then pass the structure to thedevm_counter_registerfunction. However, what's different now is how the Counter subsystem code handles this registration internally.Whereas before callbacks would interact directly with sysfs data, this interaction is now abstracted and instead callbacks interact with native C data types. The
counter_compstructure forms the basis for Counter extensions.The
counter-sysfs.cfile contains the code to parse through thecounter_devicestructure and register the requested components and extensions. Attributes are created and populated based on type, with respective translation functions to handle the mapping between sysfs and the counter driver callbacks.The translation performed for each attribute is straightforward: the attribute type and data is parsed from the
counter_attributestructure, the respective counter driver read/write callback is called, and sysfs I/O is handled before or after the driver read/write function is called.Cc Syed Nayyar Waris <[email protected]>Cc Jarkko Nikula <[email protected]>Cc Patrick Havelange <[email protected]>Cc Kamel Bouhara <[email protected]>Cc Fabrice Gasnier <[email protected]>Cc Maxime Coquelin <[email protected]>Cc Alexandre Torgue <[email protected]>Cc Dan Carpenter <[email protected]>Reviewed-by David Lechner <[email protected]>Tested-by David Lechner <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>
Quoted 3 lines
--- MAINTAINERS | 1 - drivers/counter/104-quad-8.c | 449 +++----
For 104-Quad-8,
Quoted 5,150 lines
drivers/counter/Makefile | 1 + drivers/counter/counter-core.c | 145 +++ drivers/counter/counter-sysfs.c | 846 +++++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------------- drivers/counter/ftm-quaddec.c | 56 +- drivers/counter/intel-qep.c | 144 +-- drivers/counter/interrupt-cnt.c | 62 +- drivers/counter/microchip-tcb-capture.c | 93 +- drivers/counter/stm32-lptimer-cnt.c | 162 ++- drivers/counter/stm32-timer-cnt.c | 147 +-- drivers/counter/ti-eqep.c | 180 +-- include/linux/counter.h | 658 +++++----- include/linux/counter_enum.h | 45 - 16 files changed, 1920 insertions(+), 2578 deletions(-) create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.hMAINTAINERS +0
-1@@ -4687,7 +4687,6 @@ F: Documentation/ABI/testing/sysfs-bus-counter F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h -F: include/linux/counter_enum.h CP2615 I2C DRIVER M: Bence Csókás <[email protected]>drivers/counter/104-quad-8.c +191
-258@@ -116,7 +116,7 @@ static int quad8_signal_read(struct counter_device *counter, }static int quad8_count_read (struct counter_device *counter, -struct counter_count *count,unsigned long *val) +struct counter_count *count,u64 *val) {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id;@@ -147,7 +147,7 @@ static int quad8_count_read(struct counter_device *counter, }static int quad8_count_write (struct counter_device *counter, -struct counter_count *count,unsigned long val) +struct counter_count *count,u64 val) {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id;@@ -187,22 +187,16 @@ static int quad8_count_write(struct counter_device *counter,return 0 ; } -enum quad8_count_function { -QUAD8_COUNT_FUNCTION_PULSE_DIRECTION =0 , -QUAD8_COUNT_FUNCTION_QUADRATURE_X1 , -QUAD8_COUNT_FUNCTION_QUADRATURE_X2 , -QUAD8_COUNT_FUNCTION_QUADRATURE_X4 -}; -static const enum counter_function quad8_count_functions_list[] = { - [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION ] =COUNTER_FUNCTION_PULSE_DIRECTION , - [QUAD8_COUNT_FUNCTION_QUADRATURE_X1 ] =COUNTER_FUNCTION_QUADRATURE_X1_A , - [QUAD8_COUNT_FUNCTION_QUADRATURE_X2 ] =COUNTER_FUNCTION_QUADRATURE_X2_A , - [QUAD8_COUNT_FUNCTION_QUADRATURE_X4 ] =COUNTER_FUNCTION_QUADRATURE_X4 +COUNTER_FUNCTION_PULSE_DIRECTION , +COUNTER_FUNCTION_QUADRATURE_X1_A , +COUNTER_FUNCTION_QUADRATURE_X2_A , +COUNTER_FUNCTION_QUADRATURE_X4 , }; -static int quad8_function_get (struct counter_device *counter, -struct counter_count *count,size_t *function) +static int quad8_function_read (struct counter_device *counter, +struct counter_count *count, +enum counter_function *function) {struct quad8 *const priv = counter->priv;const int id = count->id;@@ -212,25 +206,26 @@ static int quad8_function_get(struct counter_device *counter,if (priv->quadrature_mode[id])switch (priv->quadrature_scale[id]) {case 0 : - *function =QUAD8_COUNT_FUNCTION_QUADRATURE_X1 ; + *function =COUNTER_FUNCTION_QUADRATURE_X1_A ;break ;case 1 : - *function =QUAD8_COUNT_FUNCTION_QUADRATURE_X2 ; + *function =COUNTER_FUNCTION_QUADRATURE_X2_A ;break ;case 2 : - *function =QUAD8_COUNT_FUNCTION_QUADRATURE_X4 ; + *function =COUNTER_FUNCTION_QUADRATURE_X4 ;break ; }else - *function =QUAD8_COUNT_FUNCTION_PULSE_DIRECTION ; + *function =COUNTER_FUNCTION_PULSE_DIRECTION ;mutex_unlock (&priv->lock);return 0 ; } -static int quad8_function_set (struct counter_device *counter, -struct counter_count *count,size_t function) +static int quad8_function_write (struct counter_device *counter, +struct counter_count *count, +enum counter_function function) {struct quad8 *const priv = counter->priv;const int id = count->id;@@ -246,7 +241,7 @@ static int quad8_function_set(struct counter_device *counter, mode_cfg = priv->count_mode[id] <<1 ; idr_cfg = priv->index_polarity[id] <<1 ; -if (function ==QUAD8_COUNT_FUNCTION_PULSE_DIRECTION ) { +if (function ==COUNTER_FUNCTION_PULSE_DIRECTION ) { *quadrature_mode =0 ;/* Quadrature scaling only available in quadrature mode */ @@ -262,15 +257,15 @@ static int quad8_function_set(struct counter_device *counter, *quadrature_mode =1 ;switch (function) { -case QUAD8_COUNT_FUNCTION_QUADRATURE_X1 : +case COUNTER_FUNCTION_QUADRATURE_X1_A : *scale =0 ; mode_cfg |=QUAD8_CMR_QUADRATURE_X1 ;break ; -case QUAD8_COUNT_FUNCTION_QUADRATURE_X2 : +case COUNTER_FUNCTION_QUADRATURE_X2_A : *scale =1 ; mode_cfg |=QUAD8_CMR_QUADRATURE_X2 ;break ; -case QUAD8_COUNT_FUNCTION_QUADRATURE_X4 : +case COUNTER_FUNCTION_QUADRATURE_X4 : *scale =2 ; mode_cfg |=QUAD8_CMR_QUADRATURE_X4 ;break ;@@ -289,8 +284,9 @@ static int quad8_function_set(struct counter_device *counter,return 0 ; } -static void quad8_direction_get (struct counter_device *counter, -struct counter_count *count,enum counter_count_direction *direction) +static int quad8_direction_read (struct counter_device *counter, +struct counter_count *count, +enum counter_count_direction *direction) {const struct quad8 *const priv = counter->priv;unsigned int ud_flag;@@ -301,76 +297,74 @@ static void quad8_direction_get(struct counter_device *counter, *direction = (ud_flag) ?COUNTER_COUNT_DIRECTION_FORWARD :COUNTER_COUNT_DIRECTION_BACKWARD ; -} -enum quad8_synapse_action { -QUAD8_SYNAPSE_ACTION_NONE =0 , -QUAD8_SYNAPSE_ACTION_RISING_EDGE , -QUAD8_SYNAPSE_ACTION_FALLING_EDGE , -QUAD8_SYNAPSE_ACTION_BOTH_EDGES -}; +return 0 ; +}static const enum counter_synapse_action quad8_index_actions_list[] = { - [QUAD8_SYNAPSE_ACTION_NONE ] =COUNTER_SYNAPSE_ACTION_NONE , - [QUAD8_SYNAPSE_ACTION_RISING_EDGE ] =COUNTER_SYNAPSE_ACTION_RISING_EDGE +COUNTER_SYNAPSE_ACTION_NONE , +COUNTER_SYNAPSE_ACTION_RISING_EDGE , };static const enum counter_synapse_action quad8_synapse_actions_list[] = { - [QUAD8_SYNAPSE_ACTION_NONE ] =COUNTER_SYNAPSE_ACTION_NONE , - [QUAD8_SYNAPSE_ACTION_RISING_EDGE ] =COUNTER_SYNAPSE_ACTION_RISING_EDGE , - [QUAD8_SYNAPSE_ACTION_FALLING_EDGE ] =COUNTER_SYNAPSE_ACTION_FALLING_EDGE , - [QUAD8_SYNAPSE_ACTION_BOTH_EDGES ] =COUNTER_SYNAPSE_ACTION_BOTH_EDGES +COUNTER_SYNAPSE_ACTION_NONE , +COUNTER_SYNAPSE_ACTION_RISING_EDGE , +COUNTER_SYNAPSE_ACTION_FALLING_EDGE , +COUNTER_SYNAPSE_ACTION_BOTH_EDGES , }; -static int quad8_action_get (struct counter_device *counter, -struct counter_count *count,struct counter_synapse *synapse, -size_t *action) +static int quad8_action_read (struct counter_device *counter, +struct counter_count *count, +struct counter_synapse *synapse, +enum counter_synapse_action *action) {struct quad8 *const priv = counter->priv;int err; -size_t function =0 ; +enum counter_function function;const size_t signal_a_id = count->synapses[0 ].signal->id;enum counter_count_direction direction;/* Handle Index signals */ if (synapse->signal->id >=16 ) {if (priv->preset_enable[count->id]) - *action =QUAD8_SYNAPSE_ACTION_RISING_EDGE ; + *action =COUNTER_SYNAPSE_ACTION_RISING_EDGE ;else - *action =QUAD8_SYNAPSE_ACTION_NONE ; + *action =COUNTER_SYNAPSE_ACTION_NONE ;return 0 ; } - err =quad8_function_get (counter, count, &function); + err =quad8_function_read (counter, count, &function);if (err)return err;/* Default action mode */ - *action =QUAD8_SYNAPSE_ACTION_NONE ; + *action =COUNTER_SYNAPSE_ACTION_NONE ;/* Determine action mode based on current count function mode */ switch (function) { -case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION : +case COUNTER_FUNCTION_PULSE_DIRECTION :if (synapse->signal->id == signal_a_id) - *action =QUAD8_SYNAPSE_ACTION_RISING_EDGE ; + *action =COUNTER_SYNAPSE_ACTION_RISING_EDGE ;return 0 ; -case QUAD8_COUNT_FUNCTION_QUADRATURE_X1 : +case COUNTER_FUNCTION_QUADRATURE_X1_A :if (synapse->signal->id == signal_a_id) { -quad8_direction_get (counter, count, &direction); + err =quad8_direction_read (counter, count, &direction); +if (err) +return err;if (direction ==COUNTER_COUNT_DIRECTION_FORWARD ) - *action =QUAD8_SYNAPSE_ACTION_RISING_EDGE ; + *action =COUNTER_SYNAPSE_ACTION_RISING_EDGE ;else - *action =QUAD8_SYNAPSE_ACTION_FALLING_EDGE ; + *action =COUNTER_SYNAPSE_ACTION_FALLING_EDGE ; }return 0 ; -case QUAD8_COUNT_FUNCTION_QUADRATURE_X2 : +case COUNTER_FUNCTION_QUADRATURE_X2_A :if (synapse->signal->id == signal_a_id) - *action =QUAD8_SYNAPSE_ACTION_BOTH_EDGES ; + *action =COUNTER_SYNAPSE_ACTION_BOTH_EDGES ;return 0 ; -case QUAD8_COUNT_FUNCTION_QUADRATURE_X4 : - *action =QUAD8_SYNAPSE_ACTION_BOTH_EDGES ; +case COUNTER_FUNCTION_QUADRATURE_X4 : + *action =COUNTER_SYNAPSE_ACTION_BOTH_EDGES ;return 0 ;default :/* should never reach this path */ @@ -382,9 +376,9 @@ static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, - .function_get = quad8_function_get, - .function_set = quad8_function_set, - .action_get = quad8_action_get + .function_read = quad8_function_read, + .function_write = quad8_function_write, + .action_read = quad8_action_read };static const char *const quad8_index_polarity_modes[] = {@@ -393,7 +387,8 @@ static const char *const quad8_index_polarity_modes[] = { };static int quad8_index_polarity_get (struct counter_device *counter, -struct counter_signal *signal,size_t *index_polarity) +struct counter_signal *signal, +u32 *index_polarity) {const struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id -16 ;@@ -404,7 +399,8 @@ static int quad8_index_polarity_get(struct counter_device *counter, }static int quad8_index_polarity_set (struct counter_device *counter, -struct counter_signal *signal,size_t index_polarity) +struct counter_signal *signal, +u32 index_polarity) {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id -16 ;@@ -425,20 +421,14 @@ static int quad8_index_polarity_set(struct counter_device *counter,return 0 ; } -static struct counter_signal_enum_ext quad8_index_pol_enum = { - .items = quad8_index_polarity_modes, - .num_items =ARRAY_SIZE (quad8_index_polarity_modes), - .get = quad8_index_polarity_get, - .set = quad8_index_polarity_set -}; -static const char *const quad8_synchronous_modes[] = {"non-synchronous" ,"synchronous" };static int quad8_synchronous_mode_get (struct counter_device *counter, -struct counter_signal *signal,size_t *synchronous_mode) +struct counter_signal *signal, +u32 *synchronous_mode) {const struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id -16 ;@@ -449,7 +439,8 @@ static int quad8_synchronous_mode_get(struct counter_device *counter, }static int quad8_synchronous_mode_set (struct counter_device *counter, -struct counter_signal *signal,size_t synchronous_mode) +struct counter_signal *signal, +u32 synchronous_mode) {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id -16 ;@@ -476,22 +467,18 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,return 0 ; } -static struct counter_signal_enum_ext quad8_syn_mode_enum = { - .items = quad8_synchronous_modes, - .num_items =ARRAY_SIZE (quad8_synchronous_modes), - .get = quad8_synchronous_mode_get, - .set = quad8_synchronous_mode_set -}; - -static ssize_t quad8_count_floor_read (struct counter_device *counter, -struct counter_count *count,void *private,char *buf) +static int quad8_count_floor_read (struct counter_device *counter, +struct counter_count *count,u64 *floor) {/* Only a floor of 0 is supported */ -return sprintf (buf,"0\n" ); + *floor =0 ; + +return 0 ; } -static int quad8_count_mode_get (struct counter_device *counter, -struct counter_count *count,size_t *cnt_mode) +static int quad8_count_mode_read (struct counter_device *counter, +struct counter_count *count, +enum counter_count_mode *cnt_mode) {const struct quad8 *const priv = counter->priv;@@ -514,26 +501,28 @@ static int quad8_count_mode_get(struct counter_device *counter,return 0 ; } -static int quad8_count_mode_set (struct counter_device *counter, -struct counter_count *count,size_t cnt_mode) +static int quad8_count_mode_write (struct counter_device *counter, +struct counter_count *count, +enum counter_count_mode cnt_mode) {struct quad8 *const priv = counter->priv; +unsigned int count_mode;unsigned int mode_cfg;const int base_offset = priv->base +2 * count->id +1 ;/* Map Generic Counter count mode to 104-QUAD-8 count mode */ switch (cnt_mode) {case COUNTER_COUNT_MODE_NORMAL : - cnt_mode =0 ; + count_mode =0 ;break ;case COUNTER_COUNT_MODE_RANGE_LIMIT : - cnt_mode =1 ; + count_mode =1 ;break ;case COUNTER_COUNT_MODE_NON_RECYCLE : - cnt_mode =2 ; + count_mode =2 ;break ;case COUNTER_COUNT_MODE_MODULO_N : - cnt_mode =3 ; + count_mode =3 ;break ;default :/* should never reach this path */ @@ -542,10 +531,10 @@ static int quad8_count_mode_set(struct counter_device *counter,mutex_lock (&priv->lock); - priv->count_mode[count->id] = cnt_mode; + priv->count_mode[count->id] = count_mode;/* Set count mode configuration value */ - mode_cfg = cnt_mode <<1 ; + mode_cfg = count_mode <<1 ;/* Add quadrature mode configuration */ if (priv->quadrature_mode[count->id])@@ -559,56 +548,35 @@ static int quad8_count_mode_set(struct counter_device *counter,return 0 ; } -static struct counter_count_enum_ext quad8_cnt_mode_enum = { - .items = counter_count_mode_str, - .num_items =ARRAY_SIZE (counter_count_mode_str), - .get = quad8_count_mode_get, - .set = quad8_count_mode_set -}; - -static ssize_t quad8_count_direction_read (struct counter_device *counter, -struct counter_count *count,void *priv,char *buf) -{ -enum counter_count_direction dir; - -quad8_direction_get (counter, count, &dir); - -return sprintf (buf,"%s\n" , counter_count_direction_str[dir]); -} - -static ssize_t quad8_count_enable_read (struct counter_device *counter, -struct counter_count *count,void *private,char *buf) +static int quad8_count_enable_read (struct counter_device *counter, +struct counter_count *count,u8 *enable) {const struct quad8 *const priv = counter->priv; -return sprintf (buf,"%u\n" , priv->ab_enable[count->id]); + *enable = priv->ab_enable[count->id]; + +return 0 ; } -static ssize_t quad8_count_enable_write (struct counter_device *counter, -struct counter_count *count,void *private,const char *buf,size_t len) +static int quad8_count_enable_write (struct counter_device *counter, +struct counter_count *count,u8 enable) {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id; -int err; -bool ab_enable;unsigned int ior_cfg; - err =kstrtobool (buf, &ab_enable); -if (err) -return err; -mutex_lock (&priv->lock); - priv->ab_enable[count->id] = ab_enable; + priv->ab_enable[count->id] = enable; - ior_cfg = ab_enable | priv->preset_enable[count->id] <<1 ; + ior_cfg = enable | priv->preset_enable[count->id] <<1 ;/* Load I/O control configuration */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset +1 );mutex_unlock (&priv->lock); -return len; +return 0 ; }static const char *const quad8_noise_error_states[] = {@@ -617,7 +585,7 @@ static const char *const quad8_noise_error_states[] = { };static int quad8_error_noise_get (struct counter_device *counter, -struct counter_count *count,size_t *noise_error) +struct counter_count *count,u32 *noise_error) {const struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id +1 ;@@ -627,18 +595,14 @@ static int quad8_error_noise_get(struct counter_device *counter,return 0 ; } -static struct counter_count_enum_ext quad8_error_noise_enum = { - .items = quad8_noise_error_states, - .num_items =ARRAY_SIZE (quad8_noise_error_states), - .get = quad8_error_noise_get -}; - -static ssize_t quad8_count_preset_read (struct counter_device *counter, -struct counter_count *count,void *private,char *buf) +static int quad8_count_preset_read (struct counter_device *counter, +struct counter_count *count,u64 *preset) {const struct quad8 *const priv = counter->priv; -return sprintf (buf,"%u\n" , priv->preset[count->id]); + *preset = priv->preset[count->id]; + +return 0 ; } static void quad8_preset_register_set(struct quad8 *const priv, const int id,@@ -657,16 +621,10 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id,outb (preset >> (8 * i), base_offset); } -static ssize_t quad8_count_preset_write (struct counter_device *counter, -struct counter_count *count,void *private,const char *buf,size_t len) +static int quad8_count_preset_write (struct counter_device *counter, +struct counter_count *count,u64 preset) {struct quad8 *const priv = counter->priv; -unsigned int preset; -int ret; - - ret =kstrtouint (buf,0 , &preset); -if (ret) -return ret;/* Only 24-bit values are supported */ if (preset >0xFFFFFF )@@ -678,11 +636,11 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,mutex_unlock (&priv->lock); -return len; +return 0 ; } -static ssize_t quad8_count_ceiling_read (struct counter_device *counter, -struct counter_count *count,void *private,char *buf) +static int quad8_count_ceiling_read (struct counter_device *counter, +struct counter_count *count,u64 *ceiling) {struct quad8 *const priv = counter->priv;@@ -692,26 +650,23 @@ static ssize_t quad8_count_ceiling_read(struct counter_device *counter,switch (priv->count_mode[count->id]) {case 1 :case 3 : -mutex_unlock (&priv->lock); -return sprintf (buf,"%u\n" , priv->preset[count->id]); + *ceiling = priv->preset[count->id]; +break ; +default : +/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ + *ceiling =0x1FFFFFF ; +break ; }mutex_unlock (&priv->lock); -/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ -return sprintf (buf,"33554431\n" ); +return 0 ; } -static ssize_t quad8_count_ceiling_write (struct counter_device *counter, -struct counter_count *count,void *private,const char *buf,size_t len) +static int quad8_count_ceiling_write (struct counter_device *counter, +struct counter_count *count,u64 ceiling) {struct quad8 *const priv = counter->priv; -unsigned int ceiling; -int ret; - - ret =kstrtouint (buf,0 , &ceiling); -if (ret) -return ret;/* Only 24-bit values are supported */ if (ceiling >0xFFFFFF )@@ -725,7 +680,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,case 3 :quad8_preset_register_set (priv, count->id, ceiling);mutex_unlock (&priv->lock); -return len; +return 0 ; }mutex_unlock (&priv->lock);@@ -733,27 +688,25 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,return -EINVAL ; } -static ssize_t quad8_count_preset_enable_read (struct counter_device *counter, -struct counter_count *count,void *private,char *buf) +static int quad8_count_preset_enable_read (struct counter_device *counter, +struct counter_count *count, +u8 *preset_enable) {const struct quad8 *const priv = counter->priv; -return sprintf (buf,"%u\n" , !priv->preset_enable[count->id]); + *preset_enable = !priv->preset_enable[count->id]; + +return 0 ; } -static ssize_t quad8_count_preset_enable_write (struct counter_device *counter, -struct counter_count *count,void *private,const char *buf,size_t len) +static int quad8_count_preset_enable_write (struct counter_device *counter, +struct counter_count *count, +u8 preset_enable) {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id +1 ; -bool preset_enable; -int ret;unsigned int ior_cfg; - ret =kstrtobool (buf, &preset_enable); -if (ret) -return ret; -/* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable;@@ -761,25 +714,24 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | (unsigned int )preset_enable <<1 ; + ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 ;/* Load I/O control configuration to Input / Output Control Register */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset);mutex_unlock (&priv->lock); -return len; +return 0 ; } -static ssize_t quad8_signal_cable_fault_read (struct counter_device *counter, -struct counter_signal *signal, -void *private,char *buf) +static int quad8_signal_cable_fault_read (struct counter_device *counter, +struct counter_signal *signal, +u8 *cable_fault) {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ;bool disabled;unsigned int status; -unsigned int fault;mutex_lock (&priv->lock);@@ -796,36 +748,31 @@ static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,mutex_unlock (&priv->lock);/* Mask respective channel and invert logic */ - fault = !(status &BIT (channel_id)); + *cable_fault = !(status &BIT (channel_id)); -return sprintf (buf,"%u\n" , fault); +return 0 ; } -static ssize_t quad8_signal_cable_fault_enable_read ( -struct counter_device *counter,struct counter_signal *signal, -void *private,char *buf) +static int quad8_signal_cable_fault_enable_read (struct counter_device *counter, +struct counter_signal *signal, +u8 *enable) {const struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ; -const unsigned int enb = !!(priv->cable_fault_enable &BIT (channel_id)); -return sprintf (buf,"%u\n" , enb); + *enable = !!(priv->cable_fault_enable &BIT (channel_id)); + +return 0 ; } -static ssize_t quad8_signal_cable_fault_enable_write ( -struct counter_device *counter,struct counter_signal *signal, -void *private,const char *buf,size_t len) +static int quad8_signal_cable_fault_enable_write (struct counter_device *counter, +struct counter_signal *signal, +u8 enable) {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ; -bool enable; -int ret;unsigned int cable_fault_enable; - ret =kstrtobool (buf, &enable); -if (ret) -return ret; -mutex_lock (&priv->lock);if (enable)@@ -840,31 +787,27 @@ static ssize_t quad8_signal_cable_fault_enable_write(mutex_unlock (&priv->lock); -return len; +return 0 ; } -static ssize_t quad8_signal_fck_prescaler_read (struct counter_device *counter, -struct counter_signal *signal,void *private,char *buf) +static int quad8_signal_fck_prescaler_read (struct counter_device *counter, +struct counter_signal *signal, +u8 *prescaler) {const struct quad8 *const priv = counter->priv; -const size_t channel_id = signal->id /2 ; -return sprintf (buf,"%u\n" , priv->fck_prescaler[channel_id]); + *prescaler = priv->fck_prescaler[signal->id /2 ]; + +return 0 ; } -static ssize_t quad8_signal_fck_prescaler_write (struct counter_device *counter, -struct counter_signal *signal,void *private,const char *buf, -size_t len) +static int quad8_signal_fck_prescaler_write (struct counter_device *counter, +struct counter_signal *signal, +u8 prescaler) {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ;const int base_offset = priv->base +2 * channel_id; -u8 prescaler; -int ret; - - ret =kstrtou8 (buf,0 , &prescaler); -if (ret) -return ret;mutex_lock (&priv->lock);@@ -880,31 +823,30 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,mutex_unlock (&priv->lock); -return len; +return 0 ; } -static const struct counter_signal_ext quad8_signal_ext[] = { - { - .name ="cable_fault" , - .read = quad8_signal_cable_fault_read - }, - { - .name ="cable_fault_enable" , - .read = quad8_signal_cable_fault_enable_read, - .write = quad8_signal_cable_fault_enable_write - }, - { - .name ="filter_clock_prescaler" , - .read = quad8_signal_fck_prescaler_read, - .write = quad8_signal_fck_prescaler_write - } +static struct counter_comp quad8_signal_ext[] = { +COUNTER_COMP_SIGNAL_BOOL ("cable_fault" , quad8_signal_cable_fault_read, +NULL ), +COUNTER_COMP_SIGNAL_BOOL ("cable_fault_enable" , + quad8_signal_cable_fault_enable_read, + quad8_signal_cable_fault_enable_write), +COUNTER_COMP_SIGNAL_U8 ("filter_clock_prescaler" , + quad8_signal_fck_prescaler_read, + quad8_signal_fck_prescaler_write) }; -static const struct counter_signal_ext quad8_index_ext[] = { -COUNTER_SIGNAL_ENUM ("index_polarity" , &quad8_index_pol_enum), -COUNTER_SIGNAL_ENUM_AVAILABLE ("index_polarity" , &quad8_index_pol_enum), -COUNTER_SIGNAL_ENUM ("synchronous_mode" , &quad8_syn_mode_enum), -COUNTER_SIGNAL_ENUM_AVAILABLE ("synchronous_mode" , &quad8_syn_mode_enum) +static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes); +static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes); + +static struct counter_comp quad8_index_ext[] = { +COUNTER_COMP_SIGNAL_ENUM ("index_polarity" , quad8_index_polarity_get, + quad8_index_polarity_set, + quad8_index_pol_enum), +COUNTER_COMP_SIGNAL_ENUM ("synchronous_mode" , quad8_synchronous_mode_get, + quad8_synchronous_mode_set, + quad8_synch_mode_enum), };#define QUAD8_QUAD_SIGNAL (_id, _name) { \@@ -973,39 +915,30 @@ static struct counter_synapse quad8_count_synapses[][3] = {QUAD8_COUNT_SYNAPSES (6 ),QUAD8_COUNT_SYNAPSES (7 ) }; -static const struct counter_count_ext quad8_count_ext[] = { - { - .name ="ceiling" , - .read = quad8_count_ceiling_read, - .write = quad8_count_ceiling_write - }, - { - .name ="floor" , - .read = quad8_count_floor_read - }, -COUNTER_COUNT_ENUM ("count_mode" , &quad8_cnt_mode_enum), -COUNTER_COUNT_ENUM_AVAILABLE ("count_mode" , &quad8_cnt_mode_enum), - { - .name ="direction" , - .read = quad8_count_direction_read - }, - { - .name ="enable" , - .read = quad8_count_enable_read, - .write = quad8_count_enable_write - }, -COUNTER_COUNT_ENUM ("error_noise" , &quad8_error_noise_enum), -COUNTER_COUNT_ENUM_AVAILABLE ("error_noise" , &quad8_error_noise_enum), - { - .name ="preset" , - .read = quad8_count_preset_read, - .write = quad8_count_preset_write - }, - { - .name ="preset_enable" , - .read = quad8_count_preset_enable_read, - .write = quad8_count_preset_enable_write - } +static const enum counter_count_mode quad8_cnt_modes[] = { +COUNTER_COUNT_MODE_NORMAL , +COUNTER_COUNT_MODE_RANGE_LIMIT , +COUNTER_COUNT_MODE_NON_RECYCLE , +COUNTER_COUNT_MODE_MODULO_N , +}; + +static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes); + +static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states); + +static struct counter_comp quad8_count_ext[] = { +COUNTER_COMP_CEILING (quad8_count_ceiling_read, + quad8_count_ceiling_write), +COUNTER_COMP_FLOOR (quad8_count_floor_read,NULL ), +COUNTER_COMP_COUNT_MODE (quad8_count_mode_read, quad8_count_mode_write, + quad8_count_mode_available), +COUNTER_COMP_DIRECTION (quad8_direction_read), +COUNTER_COMP_ENABLE (quad8_count_enable_read, quad8_count_enable_write), +COUNTER_COMP_COUNT_ENUM ("error_noise" , quad8_error_noise_get,NULL , + quad8_error_noise_enum), +COUNTER_COMP_PRESET (quad8_count_preset_read, quad8_count_preset_write), +COUNTER_COMP_PRESET_ENABLE (quad8_count_preset_enable_read, + quad8_count_preset_enable_write), };#define QUAD8_COUNT (_id, _cntname) { \drivers/counter/Makefile +1
-0@@ -4,6 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o +counter-y := counter-core.o counter-sysfs.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.odrivers/counter/counter-core.c new file +145
-0@@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Generic Counter interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#include <linux/counter.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/gfp.h> +#include <linux/idr.h> +#include <linux/init.h> +#include <linux/module.h> + +#include "counter-sysfs.h" + +/* Provides a unique ID for each counter device */ +static DEFINE_IDA (counter_ida); + +static void counter_device_release (struct device *dev) +{ +struct counter_device *const counter =dev_get_drvdata (dev); + +counter_chrdev_remove (counter); +ida_free (&counter_ida, dev->id); +} + +static struct device_type counter_device_type = { + .name ="counter_device" , + .release = counter_device_release, +}; + +static struct bus_type counter_bus_type = { + .name ="counter" , + .dev_name ="counter" , +}; + +/** +* counter_register - register Counter to the system +* @counter: pointer to Counter to register +* +* This function registers a Counter to the system. A sysfs "counter" directory +* will be created and populated with sysfs attributes correlating with the +* Counter Signals, Synapses, and Counts respectively. +*/ +int counter_register (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +int id; +int err; + +/* Acquire unique ID */ + id =ida_alloc (&counter_ida,GFP_KERNEL ); +if (id <0 ) +return id; + +/* Configure device structure for Counter */ + dev->id = id; + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; +if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } +device_initialize (dev); +dev_set_drvdata (dev, counter); + +/* Add Counter sysfs attributes */ + err =counter_sysfs_add (counter); +if (err <0 ) + goto err_free_id; + +/* Add device to system */ + err =device_add (dev); +if (err <0 ) + goto err_free_id; + +return 0 ; + +err_free_id: +put_device (dev); +return err; +} +EXPORT_SYMBOL_GPL (counter_register); + +/** +* counter_unregister - unregister Counter from the system +* @counter: pointer to Counter to unregister +* +* The Counter is unregistered from the system. +*/ +void counter_unregister (struct counter_device *const counter) +{ +if (!counter) +return ; + +device_unregister (&counter->dev); +} +EXPORT_SYMBOL_GPL (counter_unregister); + +static void devm_counter_release (void *counter) +{ +counter_unregister (counter); +} + +/** +* devm_counter_register - Resource-managed counter_register +* @dev: device to allocate counter_device for +* @counter: pointer to Counter to register +* +* Managed counter_register. The Counter registered with this function is +* automatically unregistered on driver detach. This function calls +* counter_register internally. Refer to that function for more information. +* +* RETURNS: +* 0 on success, negative error number on failure. +*/ +int devm_counter_register (struct device *dev, +struct counter_device *const counter) +{ +int err; + + err =counter_register (counter); +if (err <0 ) +return err; + +return devm_add_action_or_reset (dev, devm_counter_release, counter); +} +EXPORT_SYMBOL_GPL (devm_counter_register); + +static int __init counter_init (void ) +{ +return bus_register (&counter_bus_type); +} + +static void __exit counter_exit(void) +{ +bus_unregister (&counter_bus_type); +} + +subsys_initcall (counter_init); +module_exit (counter_exit); + +MODULE_AUTHOR ("William Breathitt Gray <[email protected]>" ); +MODULE_DESCRIPTION ("Generic Counter interface" ); +MODULE_LICENSE ("GPL v2" );drivers/counter/counter-sysfs.c new file +846
-0@@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Generic Counter sysfs interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#include <linux/counter.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> + +#include "counter-sysfs.h" + +/** +* struct counter_attribute - Counter sysfs attribute +* @dev_attr: device attribute for sysfs +* @l: node to add Counter attribute to attribute group list +* @comp: Counter component callbacks and data +* @scope: Counter scope of the attribute +* @parent: pointer to the parent component +*/ +struct counter_attribute { +struct device_attribute dev_attr; +struct list_head l; + +struct counter_comp comp; +enum counter_scope scope; +void *parent; +}; + +#define to_counter_attribute (_dev_attr) \ + container_of(_dev_attr, struct counter_attribute, dev_attr) + +/** +* struct counter_attribute_group - container for attribute group +* @name: name of the attribute group +* @attr_list: list to keep track of created attributes +* @num_attr: number of attributes +*/ +struct counter_attribute_group { +const char *name; +struct list_head attr_list; +size_t num_attr; +}; + +static const char *const counter_function_str[] = { + [COUNTER_FUNCTION_INCREASE ] ="increase" , + [COUNTER_FUNCTION_DECREASE ] ="decrease" , + [COUNTER_FUNCTION_PULSE_DIRECTION ] ="pulse-direction" , + [COUNTER_FUNCTION_QUADRATURE_X1_A ] ="quadrature x1 a" , + [COUNTER_FUNCTION_QUADRATURE_X1_B ] ="quadrature x1 b" , + [COUNTER_FUNCTION_QUADRATURE_X2_A ] ="quadrature x2 a" , + [COUNTER_FUNCTION_QUADRATURE_X2_B ] ="quadrature x2 b" , + [COUNTER_FUNCTION_QUADRATURE_X4 ] ="quadrature x4" +}; + +static const char *const counter_signal_value_str[] = { + [COUNTER_SIGNAL_LEVEL_LOW ] ="low" , + [COUNTER_SIGNAL_LEVEL_HIGH ] ="high" +}; + +static const char *const counter_synapse_action_str[] = { + [COUNTER_SYNAPSE_ACTION_NONE ] ="none" , + [COUNTER_SYNAPSE_ACTION_RISING_EDGE ] ="rising edge" , + [COUNTER_SYNAPSE_ACTION_FALLING_EDGE ] ="falling edge" , + [COUNTER_SYNAPSE_ACTION_BOTH_EDGES ] ="both edges" +}; + +static const char *const counter_count_direction_str[] = { + [COUNTER_COUNT_DIRECTION_FORWARD ] ="forward" , + [COUNTER_COUNT_DIRECTION_BACKWARD ] ="backward" +}; + +static const char *const counter_count_mode_str[] = { + [COUNTER_COUNT_MODE_NORMAL ] ="normal" , + [COUNTER_COUNT_MODE_RANGE_LIMIT ] ="range limit" , + [COUNTER_COUNT_MODE_NON_RECYCLE ] ="non-recycle" , + [COUNTER_COUNT_MODE_MODULO_N ] ="modulo-n" +}; + +static ssize_t counter_comp_u8_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +int err; +u8 data =0 ; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u8_read (counter, &data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u8_read (counter, a->parent, &data); +break ; +case COUNTER_SCOPE_COUNT : + err = a->comp.count_u8_read (counter, a->parent, &data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +if (a->comp.type ==COUNTER_COMP_BOOL ) + data = !!data; + +return sprintf (buf,"%u\n" , (unsigned int )data); +} + +static ssize_t counter_comp_u8_store (struct device *dev, +struct device_attribute *attr, +const char *buf,size_t len) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +int err; +bool bool_data =0 ; +u8 data =0 ; + +if (a->comp.type ==COUNTER_COMP_BOOL ) { + err =kstrtobool (buf, &bool_data); + data = bool_data; + }else + err =kstrtou8 (buf,0 , &data); +if (err <0 ) +return err; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u8_write (counter, data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u8_write (counter, a->parent, data); +break ; +case COUNTER_SCOPE_COUNT : + err = a->comp.count_u8_write (counter, a->parent, data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +return len; +} + +static ssize_t counter_comp_u32_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +const struct counter_available *const avail = a->comp.priv; +int err; +u32 data =0 ; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u32_read (counter, &data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u32_read (counter, a->parent, &data); +break ; +case COUNTER_SCOPE_COUNT : +if (a->comp.type ==COUNTER_COMP_SYNAPSE_ACTION ) + err = a->comp.action_read (counter, a->parent, + a->comp.priv, &data); +else + err = a->comp.count_u32_read (counter, a->parent, &data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +switch (a->comp.type) { +case COUNTER_COMP_FUNCTION : +return sysfs_emit (buf,"%s\n" , counter_function_str[data]); +case COUNTER_COMP_SIGNAL_LEVEL : +return sysfs_emit (buf,"%s\n" , counter_signal_value_str[data]); +case COUNTER_COMP_SYNAPSE_ACTION : +return sysfs_emit (buf,"%s\n" , counter_synapse_action_str[data]); +case COUNTER_COMP_ENUM : +return sysfs_emit (buf,"%s\n" , avail->strs[data]); +case COUNTER_COMP_COUNT_DIRECTION : +return sysfs_emit (buf,"%s\n" , counter_count_direction_str[data]); +case COUNTER_COMP_COUNT_MODE : +return sysfs_emit (buf,"%s\n" , counter_count_mode_str[data]); +default : +return sprintf (buf,"%u\n" , (unsigned int )data); + } +} + +static int counter_find_enum (u32 *const enum_item,const u32 *const enums, +const size_t num_enums,const char *const buf, +const char *const string_array[]) +{ +size_t index; + +for (index =0 ; index < num_enums; index++) { + *enum_item = enums[index]; +if (sysfs_streq (buf, string_array[*enum_item])) +return 0 ; + } + +return -EINVAL ; +} + +static ssize_t counter_comp_u32_store (struct device *dev, +struct device_attribute *attr, +const char *buf,size_t len) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +struct counter_count *const count = a->parent; +struct counter_synapse *const synapse = a->comp.priv; +const struct counter_available *const avail = a->comp.priv; +int err; +u32 data =0 ; + +switch (a->comp.type) { +case COUNTER_COMP_FUNCTION : + err =counter_find_enum (&data, count->functions_list, + count->num_functions, buf, + counter_function_str); +break ; +case COUNTER_COMP_SYNAPSE_ACTION : + err =counter_find_enum (&data, synapse->actions_list, + synapse->num_actions, buf, + counter_synapse_action_str); +break ; +case COUNTER_COMP_ENUM : + err =__sysfs_match_string (avail->strs, avail->num_items, buf); + data = err; +break ; +case COUNTER_COMP_COUNT_MODE : + err =counter_find_enum (&data, avail->enums, avail->num_items, + buf, counter_count_mode_str); +break ; +default : + err =kstrtou32 (buf,0 , &data); +break ; + } +if (err <0 ) +return err; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u32_write (counter, data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u32_write (counter, a->parent, data); +break ; +case COUNTER_SCOPE_COUNT : +if (a->comp.type ==COUNTER_COMP_SYNAPSE_ACTION ) + err = a->comp.action_write (counter, count, synapse, + data); +else + err = a->comp.count_u32_write (counter, count, data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +return len; +} + +static ssize_t counter_comp_u64_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +int err; +u64 data =0 ; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u64_read (counter, &data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u64_read (counter, a->parent, &data); +break ; +case COUNTER_SCOPE_COUNT : + err = a->comp.count_u64_read (counter, a->parent, &data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +return sprintf (buf,"%llu\n" , (unsigned long long )data); +} + +static ssize_t counter_comp_u64_store (struct device *dev, +struct device_attribute *attr, +const char *buf,size_t len) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +int err; +u64 data =0 ; + + err =kstrtou64 (buf,0 , &data); +if (err <0 ) +return err; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u64_write (counter, data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u64_write (counter, a->parent, data); +break ; +case COUNTER_SCOPE_COUNT : + err = a->comp.count_u64_write (counter, a->parent, data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +return len; +} + +static ssize_t enums_available_show (const u32 *const enums, +const size_t num_enums, +const char *const strs[],char *buf) +{ +size_t len =0 ; +size_t index; + +for (index =0 ; index < num_enums; index++) + len +=sysfs_emit_at (buf, len,"%s\n" , strs[enums[index]]); + +return len; +} + +static ssize_t strs_available_show (const struct counter_available *const avail, +char *buf) +{ +size_t len =0 ; +size_t index; + +for (index =0 ; index < avail->num_items; index++) + len +=sysfs_emit_at (buf, len,"%s\n" , avail->strs[index]); + +return len; +} + +static ssize_t counter_comp_available_show (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +const struct counter_count *const count = a->parent; +const struct counter_synapse *const synapse = a->comp.priv; +const struct counter_available *const avail = a->comp.priv; + +switch (a->comp.type) { +case COUNTER_COMP_FUNCTION : +return enums_available_show (count->functions_list, + count->num_functions, + counter_function_str, buf); +case COUNTER_COMP_SYNAPSE_ACTION : +return enums_available_show (synapse->actions_list, + synapse->num_actions, + counter_synapse_action_str, buf); +case COUNTER_COMP_ENUM : +return strs_available_show (avail, buf); +case COUNTER_COMP_COUNT_MODE : +return enums_available_show (avail->enums, avail->num_items, + counter_count_mode_str, buf); +default : +return -EINVAL ; + } +} + +static int counter_avail_attr_create (struct device *const dev, +struct counter_attribute_group *const group, +const struct counter_comp *const comp,void *const parent) +{ +struct counter_attribute *counter_attr; +struct device_attribute *dev_attr; + +/* Allocate Counter attribute */ + counter_attr =devm_kzalloc (dev,sizeof (*counter_attr),GFP_KERNEL ); +if (!counter_attr) +return -ENOMEM ; + +/* Configure Counter attribute */ + counter_attr->comp.type = comp->type; + counter_attr->comp.priv = comp->priv; + counter_attr->parent = parent; + +/* Initialize sysfs attribute */ + dev_attr = &counter_attr->dev_attr; +sysfs_attr_init (&dev_attr->attr); + +/* Configure device attribute */ + dev_attr->attr.name =devm_kasprintf (dev,GFP_KERNEL ,"%s_available" , + comp->name); +if (!dev_attr->attr.name) +return -ENOMEM ; + dev_attr->attr.mode =0444 ; + dev_attr->show = counter_comp_available_show; + +/* Store list node */ +list_add (&counter_attr->l, &group->attr_list); + group->num_attr++; + +return 0 ; +} + +static int counter_attr_create (struct device *const dev, +struct counter_attribute_group *const group, +const struct counter_comp *const comp, +const enum counter_scope scope, +void *const parent) +{ +struct counter_attribute *counter_attr; +struct device_attribute *dev_attr; + +/* Allocate Counter attribute */ + counter_attr =devm_kzalloc (dev,sizeof (*counter_attr),GFP_KERNEL ); +if (!counter_attr) +return -ENOMEM ; + +/* Configure Counter attribute */ + counter_attr->comp = *comp; + counter_attr->scope = scope; + counter_attr->parent = parent; + +/* Configure device attribute */ + dev_attr = &counter_attr->dev_attr; +sysfs_attr_init (&dev_attr->attr); + dev_attr->attr.name = comp->name; +switch (comp->type) { +case COUNTER_COMP_U8 : +case COUNTER_COMP_BOOL : +if (comp->device_u8_read) { + dev_attr->attr.mode |=0444 ; + dev_attr->show = counter_comp_u8_show; + } +if (comp->device_u8_write) { + dev_attr->attr.mode |=0200 ; + dev_attr->store = counter_comp_u8_store; + } +break ; +case COUNTER_COMP_SIGNAL_LEVEL : +case COUNTER_COMP_FUNCTION : +case COUNTER_COMP_SYNAPSE_ACTION : +case COUNTER_COMP_ENUM : +case COUNTER_COMP_COUNT_DIRECTION : +case COUNTER_COMP_COUNT_MODE : +if (comp->device_u32_read) { + dev_attr->attr.mode |=0444 ; + dev_attr->show = counter_comp_u32_show; + } +if (comp->device_u32_write) { + dev_attr->attr.mode |=0200 ; + dev_attr->store = counter_comp_u32_store; + } +break ; +case COUNTER_COMP_U64 : +if (comp->device_u64_read) { + dev_attr->attr.mode |=0444 ; + dev_attr->show = counter_comp_u64_show; + } +if (comp->device_u64_write) { + dev_attr->attr.mode |=0200 ; + dev_attr->store = counter_comp_u64_store; + } +break ; +default : +return -EINVAL ; + } + +/* Store list node */ +list_add (&counter_attr->l, &group->attr_list); + group->num_attr++; + +/* Create "*_available" attribute if needed */ +switch (comp->type) { +case COUNTER_COMP_FUNCTION : +case COUNTER_COMP_SYNAPSE_ACTION : +case COUNTER_COMP_ENUM : +case COUNTER_COMP_COUNT_MODE : +return counter_avail_attr_create (dev, group, comp, parent); +default : +return 0 ; + } +} + +static ssize_t counter_comp_name_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +return sysfs_emit (buf,"%s\n" ,to_counter_attribute (attr)->comp.name); +} + +static int counter_name_attr_create (struct device *const dev, +struct counter_attribute_group *const group, +const char *const name) +{ +struct counter_attribute *counter_attr; + +/* Allocate Counter attribute */ + counter_attr =devm_kzalloc (dev,sizeof (*counter_attr),GFP_KERNEL ); +if (!counter_attr) +return -ENOMEM ; + +/* Configure Counter attribute */ + counter_attr->comp.name = name; + +/* Configure device attribute */ +sysfs_attr_init (&counter_attr->dev_attr.attr); + counter_attr->dev_attr.attr.name ="name" ; + counter_attr->dev_attr.attr.mode =0444 ; + counter_attr->dev_attr.show = counter_comp_name_show; + +/* Store list node */ +list_add (&counter_attr->l, &group->attr_list); + group->num_attr++; + +return 0 ; +} + +static struct counter_comp counter_signal_comp = { + .type =COUNTER_COMP_SIGNAL_LEVEL , + .name ="signal" , +}; + +static int counter_signal_attrs_create (struct counter_device *const counter, +struct counter_attribute_group *const group, +struct counter_signal *const signal) +{ +const enum counter_scope scope =COUNTER_SCOPE_SIGNAL ; +struct device *const dev = &counter->dev; +int err; +struct counter_comp comp; +size_t i; + +/* Create main Signal attribute */ + comp = counter_signal_comp; + comp.signal_u32_read = counter->ops->signal_read; + err =counter_attr_create (dev, group, &comp, scope, signal); +if (err <0 ) +return err; + +/* Create Signal name attribute */ + err =counter_name_attr_create (dev, group, signal->name); +if (err <0 ) +return err; + +/* Create an attribute for each extension */ +for (i =0 ; i < signal->num_ext; i++) { + err =counter_attr_create (dev, group, signal->ext + i, scope, + signal); +if (err <0 ) +return err; + } + +return 0 ; +} + +static int counter_sysfs_signals_add (struct counter_device *const counter, +struct counter_attribute_group *const groups) +{ +size_t i; +int err; + +/* Add each Signal */ +for (i =0 ; i < counter->num_signals; i++) { +/* Generate Signal attribute directory name */ + groups[i].name =devm_kasprintf (&counter->dev,GFP_KERNEL , +"signal%zu" , i); +if (!groups[i].name) +return -ENOMEM ; + +/* Create all attributes associated with Signal */ + err =counter_signal_attrs_create (counter, groups + i, + counter->signals + i); +if (err <0 ) +return err; + } + +return 0 ; +} + +static int counter_sysfs_synapses_add (struct counter_device *const counter, +struct counter_attribute_group *const group, +struct counter_count *const count) +{ +size_t i; + +/* Add each Synapse */ +for (i =0 ; i < count->num_synapses; i++) { +struct device *const dev = &counter->dev; +struct counter_synapse *synapse; +size_t id; +struct counter_comp comp; +int err; + + synapse = count->synapses + i; + +/* Generate Synapse action name */ + id = synapse->signal - counter->signals; + comp.name =devm_kasprintf (dev,GFP_KERNEL ,"signal%zu_action" , + id); +if (!comp.name) +return -ENOMEM ; + +/* Create action attribute */ + comp.type =COUNTER_COMP_SYNAPSE_ACTION ; + comp.action_read = counter->ops->action_read; + comp.action_write = counter->ops->action_write; + comp.priv = synapse; + err =counter_attr_create (dev, group, &comp, +COUNTER_SCOPE_COUNT , count); +if (err <0 ) +return err; + } + +return 0 ; +} + +static struct counter_comp counter_count_comp = +COUNTER_COMP_COUNT_U64 ("count" ,NULL ,NULL ); + +static struct counter_comp counter_function_comp = { + .type =COUNTER_COMP_FUNCTION , + .name ="function" , +}; + +static int counter_count_attrs_create (struct counter_device *const counter, +struct counter_attribute_group *const group, +struct counter_count *const count) +{ +const enum counter_scope scope =COUNTER_SCOPE_COUNT ; +struct device *const dev = &counter->dev; +int err; +struct counter_comp comp; +size_t i; + +/* Create main Count attribute */ + comp = counter_count_comp; + comp.count_u64_read = counter->ops->count_read; + comp.count_u64_write = counter->ops->count_write; + err =counter_attr_create (dev, group, &comp, scope, count); +if (err <0 ) +return err; + +/* Create Count name attribute */ + err =counter_name_attr_create (dev, group, count->name); +if (err <0 ) +return err; + +/* Create Count function attribute */ + comp = counter_function_comp; + comp.count_u32_read = counter->ops->function_read; + comp.count_u32_write = counter->ops->function_write; + err =counter_attr_create (dev, group, &comp, scope, count); +if (err <0 ) +return err; + +/* Create an attribute for each extension */ +for (i =0 ; i < count->num_ext; i++) { + err =counter_attr_create (dev, group, count->ext + i, scope, + count); +if (err <0 ) +return err; + } + +return 0 ; +} + +static int counter_sysfs_counts_add (struct counter_device *const counter, +struct counter_attribute_group *const groups) +{ +size_t i; +struct counter_count *count; +int err; + +/* Add each Count */ +for (i =0 ; i < counter->num_counts; i++) { + count = counter->counts + i; + +/* Generate Count attribute directory name */ + groups[i].name =devm_kasprintf (&counter->dev,GFP_KERNEL , +"count%zu" , i); +if (!groups[i].name) +return -ENOMEM ; + +/* Add sysfs attributes of the Synapses */ + err =counter_sysfs_synapses_add (counter, groups + i, count); +if (err <0 ) +return err; + +/* Create all attributes associated with Count */ + err =counter_count_attrs_create (counter, groups + i, count); +if (err <0 ) +return err; + } + +return 0 ; +} + +static int counter_num_signals_read (struct counter_device *counter,u8 *val) +{ + *val = counter->num_signals; +return 0 ; +} + +static int counter_num_counts_read (struct counter_device *counter,u8 *val) +{ + *val = counter->num_counts; +return 0 ; +} + +static struct counter_comp counter_num_signals_comp = +COUNTER_COMP_DEVICE_U8 ("num_signals" , counter_num_signals_read,NULL ); + +static struct counter_comp counter_num_counts_comp = +COUNTER_COMP_DEVICE_U8 ("num_counts" , counter_num_counts_read,NULL ); + +static int counter_sysfs_attr_add (struct counter_device *const counter, +struct counter_attribute_group *group) +{ +const enum counter_scope scope =COUNTER_SCOPE_DEVICE ; +struct device *const dev = &counter->dev; +int err; +size_t i; + +/* Add Signals sysfs attributes */ + err =counter_sysfs_signals_add (counter, group); +if (err <0 ) +return err; + group += counter->num_signals; + +/* Add Counts sysfs attributes */ + err =counter_sysfs_counts_add (counter, group); +if (err <0 ) +return err; + group += counter->num_counts; + +/* Create name attribute */ + err =counter_name_attr_create (dev, group, counter->name); +if (err <0 ) +return err; + +/* Create num_signals attribute */ + err =counter_attr_create (dev, group, &counter_num_signals_comp, scope, +NULL ); +if (err <0 ) +return err; + +/* Create num_counts attribute */ + err =counter_attr_create (dev, group, &counter_num_counts_comp, scope, +NULL ); +if (err <0 ) +return err; + +/* Create an attribute for each extension */ +for (i =0 ; i < counter->num_ext; i++) { + err =counter_attr_create (dev, group, counter->ext + i, scope, +NULL ); +if (err <0 ) +return err; + } + +return 0 ; +} + +/** +* counter_sysfs_add - Adds Counter sysfs attributes to the device structure +* @counter: Pointer to the Counter device structure +* +* Counter sysfs attributes are created and added to the respective device +* structure for later registration to the system. Resource-managed memory +* allocation is performed by this function, and this memory should be freed +* when no longer needed (automatically by a device_unregister call, or +* manually by a devres_release_all call). +*/ +int counter_sysfs_add (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +const size_t num_groups = counter->num_signals + counter->num_counts +1 ; +struct counter_attribute_group *groups; +size_t i, j; +int err; +struct attribute_group *group; +struct counter_attribute *p; + +/* Allocate space for attribute groups (signals, counts, and ext) */ + groups =devm_kcalloc (dev, num_groups,sizeof (*groups),GFP_KERNEL ); +if (!groups) +return -ENOMEM ; + +/* Initialize attribute lists */ +for (i =0 ; i < num_groups; i++) +INIT_LIST_HEAD (&groups[i].attr_list); + +/* Add Counter device sysfs attributes */ + err =counter_sysfs_attr_add (counter, groups); +if (err <0 ) +return err; + +/* Allocate attribute groups for association with device */ + dev->groups =devm_kcalloc (dev, num_groups +1 ,sizeof (*dev->groups), +GFP_KERNEL ); +if (!dev->groups) +return -ENOMEM ; + +/* Prepare each group of attributes for association */ +for (i =0 ; i < num_groups; i++) { +/* Allocate space for attribute group */ + group =devm_kzalloc (dev,sizeof (*group),GFP_KERNEL ); +if (!group) +return -ENOMEM ; + group->name = groups[i].name; + +/* Allocate space for attribute pointers */ + group->attrs =devm_kcalloc (dev, groups[i].num_attr +1 , +sizeof (*group->attrs),GFP_KERNEL ); +if (!group->attrs) +return -ENOMEM ; + +/* Add attribute pointers to attribute group */ + j =0 ; +list_for_each_entry (p, &groups[i].attr_list, l) + group->attrs[j++] = &p->dev_attr.attr; + +/* Associate attribute group */ + dev->groups[i] = group; + } + +return 0 ; +}drivers/counter/counter-sysfs.h new file +13
-0@@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Counter sysfs interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _COUNTER_SYSFS_H_ +#define _COUNTER_SYSFS_H_ + +#include <linux/counter.h> + +int counter_sysfs_add (struct counter_device *const counter); + +#endif /* _COUNTER_SYSFS_H_ */ drivers/counter/counter.c deleted +0
-1496@@ -1,1496 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Generic Counter interface -* Copyright (C) 2018 William Breathitt Gray -*/ -#include <linux/counter.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/export.h> -#include <linux/fs.h> -#include <linux/gfp.h> -#include <linux/idr.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/printk.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/sysfs.h> -#include <linux/types.h> - -const char *const counter_count_direction_str[2 ] = { - [COUNTER_COUNT_DIRECTION_FORWARD ] ="forward" , - [COUNTER_COUNT_DIRECTION_BACKWARD ] ="backward" -}; -EXPORT_SYMBOL_GPL (counter_count_direction_str); - -const char *const counter_count_mode_str[4 ] = { - [COUNTER_COUNT_MODE_NORMAL ] ="normal" , - [COUNTER_COUNT_MODE_RANGE_LIMIT ] ="range limit" , - [COUNTER_COUNT_MODE_NON_RECYCLE ] ="non-recycle" , - [COUNTER_COUNT_MODE_MODULO_N ] ="modulo-n" -}; -EXPORT_SYMBOL_GPL (counter_count_mode_str); - -ssize_t counter_signal_enum_read (struct counter_device *counter, -struct counter_signal *signal,void *priv, -char *buf) -{ -const struct counter_signal_enum_ext *const e = priv; -int err; -size_t index; - -if (!e->get) -return -EINVAL ; - - err = e->get (counter, signal, &index); -if (err) -return err; - -if (index >= e->num_items) -return -EINVAL ; - -return sprintf (buf,"%s\n" , e->items[index]); -} -EXPORT_SYMBOL_GPL (counter_signal_enum_read); - -ssize_t counter_signal_enum_write (struct counter_device *counter, -struct counter_signal *signal,void *priv, -const char *buf,size_t len) -{ -const struct counter_signal_enum_ext *const e = priv; -ssize_t index; -int err; - -if (!e->set) -return -EINVAL ; - - index =__sysfs_match_string (e->items, e->num_items, buf); -if (index <0 ) -return index; - - err = e->set (counter, signal, index); -if (err) -return err; - -return len; -} -EXPORT_SYMBOL_GPL (counter_signal_enum_write); - -ssize_t counter_signal_enum_available_read (struct counter_device *counter, -struct counter_signal *signal, -void *priv,char *buf) -{ -const struct counter_signal_enum_ext *const e = priv; -size_t i; -size_t len =0 ; - -if (!e->num_items) -return 0 ; - -for (i =0 ; i < e->num_items; i++) - len +=sprintf (buf + len,"%s\n" , e->items[i]); - -return len; -} -EXPORT_SYMBOL_GPL (counter_signal_enum_available_read); - -ssize_t counter_count_enum_read (struct counter_device *counter, -struct counter_count *count,void *priv, -char *buf) -{ -const struct counter_count_enum_ext *const e = priv; -int err; -size_t index; - -if (!e->get) -return -EINVAL ; - - err = e->get (counter, count, &index); -if (err) -return err; - -if (index >= e->num_items) -return -EINVAL ; - -return sprintf (buf,"%s\n" , e->items[index]); -} -EXPORT_SYMBOL_GPL (counter_count_enum_read); - -ssize_t counter_count_enum_write (struct counter_device *counter, -struct counter_count *count,void *priv, -const char *buf,size_t len) -{ -const struct counter_count_enum_ext *const e = priv; -ssize_t index; -int err; - -if (!e->set) -return -EINVAL ; - - index =__sysfs_match_string (e->items, e->num_items, buf); -if (index <0 ) -return index; - - err = e->set (counter, count, index); -if (err) -return err; - -return len; -} -EXPORT_SYMBOL_GPL (counter_count_enum_write); - -ssize_t counter_count_enum_available_read (struct counter_device *counter, -struct counter_count *count, -void *priv,char *buf) -{ -const struct counter_count_enum_ext *const e = priv; -size_t i; -size_t len =0 ; - -if (!e->num_items) -return 0 ; - -for (i =0 ; i < e->num_items; i++) - len +=sprintf (buf + len,"%s\n" , e->items[i]); - -return len; -} -EXPORT_SYMBOL_GPL (counter_count_enum_available_read); - -ssize_t counter_device_enum_read (struct counter_device *counter,void *priv, -char *buf) -{ -const struct counter_device_enum_ext *const e = priv; -int err; -size_t index; - -if (!e->get) -return -EINVAL ; - - err = e->get (counter, &index); -if (err) -return err; - -if (index >= e->num_items) -return -EINVAL ; - -return sprintf (buf,"%s\n" , e->items[index]); -} -EXPORT_SYMBOL_GPL (counter_device_enum_read); - -ssize_t counter_device_enum_write (struct counter_device *counter,void *priv, -const char *buf,size_t len) -{ -const struct counter_device_enum_ext *const e = priv; -ssize_t index; -int err; - -if (!e->set) -return -EINVAL ; - - index =__sysfs_match_string (e->items, e->num_items, buf); -if (index <0 ) -return index; - - err = e->set (counter, index); -if (err) -return err; - -return len; -} -EXPORT_SYMBOL_GPL (counter_device_enum_write); - -ssize_t counter_device_enum_available_read (struct counter_device *counter, -void *priv,char *buf) -{ -const struct counter_device_enum_ext *const e = priv; -size_t i; -size_t len =0 ; - -if (!e->num_items) -return 0 ; - -for (i =0 ; i < e->num_items; i++) - len +=sprintf (buf + len,"%s\n" , e->items[i]); - -return len; -} -EXPORT_SYMBOL_GPL (counter_device_enum_available_read); - -struct counter_attr_parm { -struct counter_device_attr_group *group; -const char *prefix; -const char *name; -ssize_t (*show)(struct device *dev,struct device_attribute *attr, -char *buf); -ssize_t (*store)(struct device *dev,struct device_attribute *attr, -const char *buf,size_t len); -void *component; -}; - -struct counter_device_attr { -struct device_attribute dev_attr; -struct list_head l; -void *component; -}; - -static int counter_attribute_create (const struct counter_attr_parm *const parm) -{ -struct counter_device_attr *counter_attr; -struct device_attribute *dev_attr; -int err; -struct list_head *const attr_list = &parm->group->attr_list; - -/* Allocate a Counter device attribute */ - counter_attr =kzalloc (sizeof (*counter_attr),GFP_KERNEL ); -if (!counter_attr) -return -ENOMEM ; - dev_attr = &counter_attr->dev_attr; - -sysfs_attr_init (&dev_attr->attr); - -/* Configure device attribute */ - dev_attr->attr.name =kasprintf (GFP_KERNEL ,"%s%s" , parm->prefix, - parm->name); -if (!dev_attr->attr.name) { - err = -ENOMEM ; - goto err_free_counter_attr; - } -if (parm->show) { - dev_attr->attr.mode |=0444 ; - dev_attr->show = parm->show; - } -if (parm->store) { - dev_attr->attr.mode |=0200 ; - dev_attr->store = parm->store; - } - -/* Store associated Counter component with attribute */ - counter_attr->component = parm->component; - -/* Keep track of the attribute for later cleanup */ -list_add (&counter_attr->l, attr_list); - parm->group->num_attr++; - -return 0 ; - -err_free_counter_attr: -kfree (counter_attr); -return err; -} - -#define to_counter_attr (_dev_attr) \ - container_of(_dev_attr, struct counter_device_attr, dev_attr) - -struct counter_signal_unit { -struct counter_signal *signal; -}; - -static const char *const counter_signal_level_str[] = { - [COUNTER_SIGNAL_LEVEL_LOW ] ="low" , - [COUNTER_SIGNAL_LEVEL_HIGH ] ="high" -}; - -static ssize_t counter_signal_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -struct counter_device *const counter =dev_get_drvdata (dev); -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_signal_unit *const component = devattr->component; -struct counter_signal *const signal = component->signal; -int err; -enum counter_signal_level level; - - err = counter->ops->signal_read (counter, signal, &level); -if (err) -return err; - -return sprintf (buf,"%s\n" , counter_signal_level_str[level]); -} - -struct counter_name_unit { -const char *name; -}; - -static ssize_t counter_device_attr_name_show (struct device *dev, -struct device_attribute *attr, -char *buf) -{ -const struct counter_name_unit *const comp =to_counter_attr (attr)->component; - -return sprintf (buf,"%s\n" , comp->name); -} - -static int counter_name_attribute_create ( -struct counter_device_attr_group *const group, -const char *const name) -{ -struct counter_name_unit *name_comp; -struct counter_attr_parm parm; -int err; - -/* Skip if no name */ -if (!name) -return 0 ; - -/* Allocate name attribute component */ - name_comp =kmalloc (sizeof (*name_comp),GFP_KERNEL ); -if (!name_comp) -return -ENOMEM ; - name_comp->name = name; - -/* Allocate Signal name attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name ="name" ; - parm.show = counter_device_attr_name_show; - parm.store =NULL ; - parm.component = name_comp; - err =counter_attribute_create (&parm); -if (err) - goto err_free_name_comp; - -return 0 ; - -err_free_name_comp: -kfree (name_comp); -return err; -} - -struct counter_signal_ext_unit { -struct counter_signal *signal; -const struct counter_signal_ext *ext; -}; - -static ssize_t counter_signal_ext_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_signal_ext_unit *const comp = devattr->component; -const struct counter_signal_ext *const ext = comp->ext; - -return ext->read (dev_get_drvdata (dev), comp->signal, ext->priv, buf); -} - -static ssize_t counter_signal_ext_store (struct device *dev, -struct device_attribute *attr, -const char *buf,size_t len) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_signal_ext_unit *const comp = devattr->component; -const struct counter_signal_ext *const ext = comp->ext; - -return ext->write (dev_get_drvdata (dev), comp->signal, ext->priv, buf, - len); -} - -static void counter_device_attr_list_free (struct list_head *attr_list) -{ -struct counter_device_attr *p, *n; - -list_for_each_entry_safe (p, n, attr_list, l) { -/* free attribute name and associated component memory */ -kfree (p->dev_attr.attr.name); -kfree (p->component); -list_del (&p->l); -kfree (p); - } -} - -static int counter_signal_ext_register ( -struct counter_device_attr_group *const group, -struct counter_signal *const signal) -{ -const size_t num_ext = signal->num_ext; -size_t i; -const struct counter_signal_ext *ext; -struct counter_signal_ext_unit *signal_ext_comp; -struct counter_attr_parm parm; -int err; - -/* Create an attribute for each extension */ -for (i =0 ; i < num_ext; i++) { - ext = signal->ext + i; - -/* Allocate signal_ext attribute component */ - signal_ext_comp =kmalloc (sizeof (*signal_ext_comp),GFP_KERNEL ); -if (!signal_ext_comp) { - err = -ENOMEM ; - goto err_free_attr_list; - } - signal_ext_comp->signal = signal; - signal_ext_comp->ext = ext; - -/* Allocate a Counter device attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name = ext->name; - parm.show = (ext->read) ? counter_signal_ext_show :NULL ; - parm.store = (ext->write) ? counter_signal_ext_store :NULL ; - parm.component = signal_ext_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (signal_ext_comp); - goto err_free_attr_list; - } - } - -return 0 ; - -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -static int counter_signal_attributes_create ( -struct counter_device_attr_group *const group, -const struct counter_device *const counter, -struct counter_signal *const signal) -{ -struct counter_signal_unit *signal_comp; -struct counter_attr_parm parm; -int err; - -/* Allocate Signal attribute component */ - signal_comp =kmalloc (sizeof (*signal_comp),GFP_KERNEL ); -if (!signal_comp) -return -ENOMEM ; - signal_comp->signal = signal; - -/* Create main Signal attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name ="signal" ; - parm.show = (counter->ops->signal_read) ? counter_signal_show :NULL ; - parm.store =NULL ; - parm.component = signal_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (signal_comp); -return err; - } - -/* Create Signal name attribute */ - err =counter_name_attribute_create (group, signal->name); -if (err) - goto err_free_attr_list; - -/* Register Signal extension attributes */ - err =counter_signal_ext_register (group, signal); -if (err) - goto err_free_attr_list; - -return 0 ; - -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -static int counter_signals_register ( -struct counter_device_attr_group *const groups_list, -const struct counter_device *const counter) -{ -const size_t num_signals = counter->num_signals; -size_t i; -struct counter_signal *signal; -const char *name; -int err; - -/* Register each Signal */ -for (i =0 ; i < num_signals; i++) { - signal = counter->signals + i; - -/* Generate Signal attribute directory name */ - name =kasprintf (GFP_KERNEL ,"signal%d" , signal->id); -if (!name) { - err = -ENOMEM ; - goto err_free_attr_groups; - } - groups_list[i].attr_group.name = name; - -/* Create all attributes associated with Signal */ - err =counter_signal_attributes_create (groups_list + i, counter, - signal); -if (err) - goto err_free_attr_groups; - } - -return 0 ; - -err_free_attr_groups: -do { -kfree (groups_list[i].attr_group.name); -counter_device_attr_list_free (&groups_list[i].attr_list); - }while (i--); -return err; -} - -static const char *const counter_synapse_action_str[] = { - [COUNTER_SYNAPSE_ACTION_NONE ] ="none" , - [COUNTER_SYNAPSE_ACTION_RISING_EDGE ] ="rising edge" , - [COUNTER_SYNAPSE_ACTION_FALLING_EDGE ] ="falling edge" , - [COUNTER_SYNAPSE_ACTION_BOTH_EDGES ] ="both edges" -}; - -struct counter_action_unit { -struct counter_synapse *synapse; -struct counter_count *count; -}; - -static ssize_t counter_action_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -int err; -struct counter_device *const counter =dev_get_drvdata (dev); -const struct counter_action_unit *const component = devattr->component; -struct counter_count *const count = component->count; -struct counter_synapse *const synapse = component->synapse; -size_t action_index; -enum counter_synapse_action action; - - err = counter->ops->action_get (counter, count, synapse, &action_index); -if (err) -return err; - - synapse->action = action_index; - - action = synapse->actions_list[action_index]; -return sprintf (buf,"%s\n" , counter_synapse_action_str[action]); -} - -static ssize_t counter_action_store (struct device *dev, -struct device_attribute *attr, -const char *buf,size_t len) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_action_unit *const component = devattr->component; -struct counter_synapse *const synapse = component->synapse; -size_t action_index; -const size_t num_actions = synapse->num_actions; -enum counter_synapse_action action; -int err; -struct counter_device *const counter =dev_get_drvdata (dev); -struct counter_count *const count = component->count; - -/* Find requested action mode */ -for (action_index =0 ; action_index < num_actions; action_index++) { - action = synapse->actions_list[action_index]; -if (sysfs_streq (buf, counter_synapse_action_str[action])) -break ; - } -/* If requested action mode not found */ -if (action_index >= num_actions) -return -EINVAL ; - - err = counter->ops->action_set (counter, count, synapse, action_index); -if (err) -return err; - - synapse->action = action_index; - -return len; -} - -struct counter_action_avail_unit { -const enum counter_synapse_action *actions_list; -size_t num_actions; -}; - -static ssize_t counter_synapse_action_available_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_action_avail_unit *const component = devattr->component; -size_t i; -enum counter_synapse_action action; -ssize_t len =0 ; - -for (i =0 ; i < component->num_actions; i++) { - action = component->actions_list[i]; - len +=sprintf (buf + len,"%s\n" , - counter_synapse_action_str[action]); - } - -return len; -} - -static int counter_synapses_register ( -struct counter_device_attr_group *const group, -const struct counter_device *const counter, -struct counter_count *const count,const char *const count_attr_name) -{ -size_t i; -struct counter_synapse *synapse; -const char *prefix; -struct counter_action_unit *action_comp; -struct counter_attr_parm parm; -int err; -struct counter_action_avail_unit *avail_comp; - -/* Register each Synapse */ -for (i =0 ; i < count->num_synapses; i++) { - synapse = count->synapses + i; - -/* Generate attribute prefix */ - prefix =kasprintf (GFP_KERNEL ,"signal%d_" , - synapse->signal->id); -if (!prefix) { - err = -ENOMEM ; - goto err_free_attr_list; - } - -/* Allocate action attribute component */ - action_comp =kmalloc (sizeof (*action_comp),GFP_KERNEL ); -if (!action_comp) { - err = -ENOMEM ; - goto err_free_prefix; - } - action_comp->synapse = synapse; - action_comp->count = count; - -/* Create action attribute */ - parm.group = group; - parm.prefix = prefix; - parm.name ="action" ; - parm.show = (counter->ops->action_get) ? counter_action_show :NULL ; - parm.store = (counter->ops->action_set) ? counter_action_store :NULL ; - parm.component = action_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (action_comp); - goto err_free_prefix; - } - -/* Allocate action available attribute component */ - avail_comp =kmalloc (sizeof (*avail_comp),GFP_KERNEL ); -if (!avail_comp) { - err = -ENOMEM ; - goto err_free_prefix; - } - avail_comp->actions_list = synapse->actions_list; - avail_comp->num_actions = synapse->num_actions; - -/* Create action_available attribute */ - parm.group = group; - parm.prefix = prefix; - parm.name ="action_available" ; - parm.show = counter_synapse_action_available_show; - parm.store =NULL ; - parm.component = avail_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (avail_comp); - goto err_free_prefix; - } - -kfree (prefix); - } - -return 0 ; - -err_free_prefix: -kfree (prefix); -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -struct counter_count_unit { -struct counter_count *count; -}; - -static ssize_t counter_count_show (struct device *dev, -struct device_attribute *attr, -char *buf) -{ -struct counter_device *const counter =dev_get_drvdata (dev); -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_count_unit *const component = devattr->component; -struct counter_count *const count = component->count; -int err; -unsigned long val; - - err = counter->ops->count_read (counter, count, &val); -if (err) -return err; - -return sprintf (buf,"%lu\n" , val); -} - -static ssize_t counter_count_store (struct device *dev, -struct device_attribute *attr, -const char *buf,size_t len) -{ -struct counter_device *const counter =dev_get_drvdata (dev); -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_count_unit *const component = devattr->component; -struct counter_count *const count = component->count; -int err; -unsigned long val; - - err =kstrtoul (buf,0 , &val); -if (err) -return err; - - err = counter->ops->count_write (counter, count, val); -if (err) -return err; - -return len; -} - -static const char *const counter_function_str[] = { - [COUNTER_FUNCTION_INCREASE ] ="increase" , - [COUNTER_FUNCTION_DECREASE ] ="decrease" , - [COUNTER_FUNCTION_PULSE_DIRECTION ] ="pulse-direction" , - [COUNTER_FUNCTION_QUADRATURE_X1_A ] ="quadrature x1 a" , - [COUNTER_FUNCTION_QUADRATURE_X1_B ] ="quadrature x1 b" , - [COUNTER_FUNCTION_QUADRATURE_X2_A ] ="quadrature x2 a" , - [COUNTER_FUNCTION_QUADRATURE_X2_B ] ="quadrature x2 b" , - [COUNTER_FUNCTION_QUADRATURE_X4 ] ="quadrature x4" -}; - -static ssize_t counter_function_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -int err; -struct counter_device *const counter =dev_get_drvdata (dev); -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_count_unit *const component = devattr->component; -struct counter_count *const count = component->count; -size_t func_index; -enum counter_function function; - - err = counter->ops->function_get (counter, count, &func_index); -if (err) -return err; - - count->function = func_index; - - function = count->functions_list[func_index]; -return sprintf (buf,"%s\n" , counter_function_str[function]); -} - -static ssize_t counter_function_store (struct device *dev, -struct device_attribute *attr, -const char *buf,size_t len) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_count_unit *const component = devattr->component; -struct counter_count *const count = component->count; -const size_t num_functions = count->num_functions; -size_t func_index; -enum counter_function function; -int err; -struct counter_device *const counter =dev_get_drvdata (dev); - -/* Find requested Count function mode */ -for (func_index =0 ; func_index < num_functions; func_index++) { - function = count->functions_list[func_index]; -if (sysfs_streq (buf, counter_function_str[function])) -break ; - } -/* Return error if requested Count function mode not found */ -if (func_index >= num_functions) -return -EINVAL ; - - err = counter->ops->function_set (counter, count, func_index); -if (err) -return err; - - count->function = func_index; - -return len; -} - -struct counter_count_ext_unit { -struct counter_count *count; -const struct counter_count_ext *ext; -}; - -static ssize_t counter_count_ext_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_count_ext_unit *const comp = devattr->component; -const struct counter_count_ext *const ext = comp->ext; - -return ext->read (dev_get_drvdata (dev), comp->count, ext->priv, buf); -} - -static ssize_t counter_count_ext_store (struct device *dev, -struct device_attribute *attr, -const char *buf,size_t len) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_count_ext_unit *const comp = devattr->component; -const struct counter_count_ext *const ext = comp->ext; - -return ext->write (dev_get_drvdata (dev), comp->count, ext->priv, buf, - len); -} - -static int counter_count_ext_register ( -struct counter_device_attr_group *const group, -struct counter_count *const count) -{ -size_t i; -const struct counter_count_ext *ext; -struct counter_count_ext_unit *count_ext_comp; -struct counter_attr_parm parm; -int err; - -/* Create an attribute for each extension */ -for (i =0 ; i < count->num_ext; i++) { - ext = count->ext + i; - -/* Allocate count_ext attribute component */ - count_ext_comp =kmalloc (sizeof (*count_ext_comp),GFP_KERNEL ); -if (!count_ext_comp) { - err = -ENOMEM ; - goto err_free_attr_list; - } - count_ext_comp->count = count; - count_ext_comp->ext = ext; - -/* Allocate count_ext attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name = ext->name; - parm.show = (ext->read) ? counter_count_ext_show :NULL ; - parm.store = (ext->write) ? counter_count_ext_store :NULL ; - parm.component = count_ext_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (count_ext_comp); - goto err_free_attr_list; - } - } - -return 0 ; - -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -struct counter_func_avail_unit { -const enum counter_function *functions_list; -size_t num_functions; -}; - -static ssize_t counter_function_available_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_func_avail_unit *const component = devattr->component; -const enum counter_function *const func_list = component->functions_list; -const size_t num_functions = component->num_functions; -size_t i; -enum counter_function function; -ssize_t len =0 ; - -for (i =0 ; i < num_functions; i++) { - function = func_list[i]; - len +=sprintf (buf + len,"%s\n" , - counter_function_str[function]); - } - -return len; -} - -static int counter_count_attributes_create ( -struct counter_device_attr_group *const group, -const struct counter_device *const counter, -struct counter_count *const count) -{ -struct counter_count_unit *count_comp; -struct counter_attr_parm parm; -int err; -struct counter_count_unit *func_comp; -struct counter_func_avail_unit *avail_comp; - -/* Allocate count attribute component */ - count_comp =kmalloc (sizeof (*count_comp),GFP_KERNEL ); -if (!count_comp) -return -ENOMEM ; - count_comp->count = count; - -/* Create main Count attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name ="count" ; - parm.show = (counter->ops->count_read) ? counter_count_show :NULL ; - parm.store = (counter->ops->count_write) ? counter_count_store :NULL ; - parm.component = count_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (count_comp); -return err; - } - -/* Allocate function attribute component */ - func_comp =kmalloc (sizeof (*func_comp),GFP_KERNEL ); -if (!func_comp) { - err = -ENOMEM ; - goto err_free_attr_list; - } - func_comp->count = count; - -/* Create Count function attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name ="function" ; - parm.show = (counter->ops->function_get) ? counter_function_show :NULL ; - parm.store = (counter->ops->function_set) ? counter_function_store :NULL ; - parm.component = func_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (func_comp); - goto err_free_attr_list; - } - -/* Allocate function available attribute component */ - avail_comp =kmalloc (sizeof (*avail_comp),GFP_KERNEL ); -if (!avail_comp) { - err = -ENOMEM ; - goto err_free_attr_list; - } - avail_comp->functions_list = count->functions_list; - avail_comp->num_functions = count->num_functions; - -/* Create Count function_available attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name ="function_available" ; - parm.show = counter_function_available_show; - parm.store =NULL ; - parm.component = avail_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (avail_comp); - goto err_free_attr_list; - } - -/* Create Count name attribute */ - err =counter_name_attribute_create (group, count->name); -if (err) - goto err_free_attr_list; - -/* Register Count extension attributes */ - err =counter_count_ext_register (group, count); -if (err) - goto err_free_attr_list; - -return 0 ; - -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -static int counter_counts_register ( -struct counter_device_attr_group *const groups_list, -const struct counter_device *const counter) -{ -size_t i; -struct counter_count *count; -const char *name; -int err; - -/* Register each Count */ -for (i =0 ; i < counter->num_counts; i++) { - count = counter->counts + i; - -/* Generate Count attribute directory name */ - name =kasprintf (GFP_KERNEL ,"count%d" , count->id); -if (!name) { - err = -ENOMEM ; - goto err_free_attr_groups; - } - groups_list[i].attr_group.name = name; - -/* Register the Synapses associated with each Count */ - err =counter_synapses_register (groups_list + i, counter, count, - name); -if (err) - goto err_free_attr_groups; - -/* Create all attributes associated with Count */ - err =counter_count_attributes_create (groups_list + i, counter, - count); -if (err) - goto err_free_attr_groups; - } - -return 0 ; - -err_free_attr_groups: -do { -kfree (groups_list[i].attr_group.name); -counter_device_attr_list_free (&groups_list[i].attr_list); - }while (i--); -return err; -} - -struct counter_size_unit { -size_t size; -}; - -static ssize_t counter_device_attr_size_show (struct device *dev, -struct device_attribute *attr, -char *buf) -{ -const struct counter_size_unit *const comp =to_counter_attr (attr)->component; - -return sprintf (buf,"%zu\n" , comp->size); -} - -static int counter_size_attribute_create ( -struct counter_device_attr_group *const group, -const size_t size,const char *const name) -{ -struct counter_size_unit *size_comp; -struct counter_attr_parm parm; -int err; - -/* Allocate size attribute component */ - size_comp =kmalloc (sizeof (*size_comp),GFP_KERNEL ); -if (!size_comp) -return -ENOMEM ; - size_comp->size = size; - - parm.group = group; - parm.prefix ="" ; - parm.name = name; - parm.show = counter_device_attr_size_show; - parm.store =NULL ; - parm.component = size_comp; - err =counter_attribute_create (&parm); -if (err) - goto err_free_size_comp; - -return 0 ; - -err_free_size_comp: -kfree (size_comp); -return err; -} - -struct counter_ext_unit { -const struct counter_device_ext *ext; -}; - -static ssize_t counter_device_ext_show (struct device *dev, -struct device_attribute *attr,char *buf) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_ext_unit *const component = devattr->component; -const struct counter_device_ext *const ext = component->ext; - -return ext->read (dev_get_drvdata (dev), ext->priv, buf); -} - -static ssize_t counter_device_ext_store (struct device *dev, -struct device_attribute *attr, -const char *buf,size_t len) -{ -const struct counter_device_attr *const devattr =to_counter_attr (attr); -const struct counter_ext_unit *const component = devattr->component; -const struct counter_device_ext *const ext = component->ext; - -return ext->write (dev_get_drvdata (dev), ext->priv, buf, len); -} - -static int counter_device_ext_register ( -struct counter_device_attr_group *const group, -struct counter_device *const counter) -{ -size_t i; -struct counter_ext_unit *ext_comp; -struct counter_attr_parm parm; -int err; - -/* Create an attribute for each extension */ -for (i =0 ; i < counter->num_ext; i++) { -/* Allocate extension attribute component */ - ext_comp =kmalloc (sizeof (*ext_comp),GFP_KERNEL ); -if (!ext_comp) { - err = -ENOMEM ; - goto err_free_attr_list; - } - - ext_comp->ext = counter->ext + i; - -/* Allocate extension attribute */ - parm.group = group; - parm.prefix ="" ; - parm.name = counter->ext[i].name; - parm.show = (counter->ext[i].read) ? counter_device_ext_show :NULL ; - parm.store = (counter->ext[i].write) ? counter_device_ext_store :NULL ; - parm.component = ext_comp; - err =counter_attribute_create (&parm); -if (err) { -kfree (ext_comp); - goto err_free_attr_list; - } - } - -return 0 ; - -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -static int counter_global_attr_register ( -struct counter_device_attr_group *const group, -struct counter_device *const counter) -{ -int err; - -/* Create name attribute */ - err =counter_name_attribute_create (group, counter->name); -if (err) -return err; - -/* Create num_counts attribute */ - err =counter_size_attribute_create (group, counter->num_counts, -"num_counts" ); -if (err) - goto err_free_attr_list; - -/* Create num_signals attribute */ - err =counter_size_attribute_create (group, counter->num_signals, -"num_signals" ); -if (err) - goto err_free_attr_list; - -/* Register Counter device extension attributes */ - err =counter_device_ext_register (group, counter); -if (err) - goto err_free_attr_list; - -return 0 ; - -err_free_attr_list: -counter_device_attr_list_free (&group->attr_list); -return err; -} - -static void counter_device_groups_list_free(Lines of this file are not shown. View the raw message.
drivers/counter/microchip-tcb-capture.c +40
-53Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
Jonathan Cameron
maintainerRe: [PATCH v12 06/17] counter: Internalize sysfs interface code
In reply to William Breathitt GrayQuoted 36 lines · On Mon, 5 Jul 2021 17:18:54 +0900 William Breathitt Gray…
This is a reimplementation of the Generic Counter driver interface. There are no modifications to the Counter subsystem userspace interface, so existing userspace applications should continue to run seamlessly.
The purpose of this patch is to internalize the sysfs interface code among the various counter drivers into a shared module. Counter drivers pass and take data natively (i.e. u8, u64, etc.) and the shared counter module handles the translation between the sysfs interface and the device drivers. This guarantees a standard userspace interface for all counter drivers, and helps generalize the Generic Counter driver ABI in order to support the Generic Counter chrdev interface (introduced in a subsequent patch) without significant changes to the existing counter drivers.
Note, Counter device registration is the same as before: drivers populate a struct
counter_devicewith components and callbacks, then pass the structure to thedevm_counter_registerfunction. However, what's different now is how the Counter subsystem code handles this registration internally.Whereas before callbacks would interact directly with sysfs data, this interaction is now abstracted and instead callbacks interact with native C data types. The
counter_compstructure forms the basis for Counter extensions.The
counter-sysfs.cfile contains the code to parse through thecounter_devicestructure and register the requested components and extensions. Attributes are created and populated based on type, with respective translation functions to handle the mapping between sysfs and the counter driver callbacks.The translation performed for each attribute is straightforward: the attribute type and data is parsed from the
counter_attributestructure, the respective counter driver read/write callback is called, and sysfs I/O is handled before or after the driver read/write function is called.
Quoted 11 lines
Cc Syed Nayyar Waris <[email protected]>Cc Jarkko Nikula <[email protected]>Cc Patrick Havelange <[email protected]>Cc Kamel Bouhara <[email protected]>Cc Fabrice Gasnier <[email protected]>Cc Maxime Coquelin <[email protected]>Cc Alexandre Torgue <[email protected]>Cc Dan Carpenter <[email protected]>Reviewed-by David Lechner <[email protected]>Tested-by David Lechner <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>
Hi William,
There is some type confusion going on in the stm32_lptim driver that should be cleaned up.
A few other minor things but basically looks good to me so I probably won't look through this one again for v13 unless some discussion starts!
One to note is that Andy just moved the kstrtoxx definitions to a new header, so we should include that where they are used going forwards. Can do that as a cleanup later though given you probably don't want to rebase this on a mid merge window state.
Jonathan
Quoted 1 line
---
...
Quoted 5 lines
drivers/counter/counter-sysfs.c new file +0
-0
...
Quoted 16 lines
drivers/counter/counter-sysfs.c new file +16
-0+static ssize_t counter_comp_u8_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +const struct counter_attribute *const a =to_counter_attribute (attr); +struct counter_device *const counter =dev_get_drvdata (dev); +int err; +u8 data =0 ; + +switch (a->scope) { +case COUNTER_SCOPE_DEVICE : + err = a->comp.device_u8_read (counter, &data); +break ; +case COUNTER_SCOPE_SIGNAL : + err = a->comp.signal_u8_read (counter, a->parent, &data); +break ; +case COUNTER_SCOPE_COUNT :
drivers/counter/counter-sysfs.c line 110
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+ err = a->comp.count_u8_read (counter, a->parent, &data); +break ; +default : +return -EINVAL ; + } +if (err <0 ) +return err; + +if (a->comp.type ==COUNTER_COMP_BOOL ) + data = !!data;
You have some drivers that do this internally then you replicate it here? An example is cable_fault in the quad 8. That is not necessarily a problem, but perhaps a comment to say this one should be paranoia and expectation is drivers will deliver 0 or 1.
Quoted 4 lines
drivers/counter/counter-sysfs.c new file +4
-0+ +return sprintf (buf,"%u\n" , (unsigned int )data); +} +
...
drivers/counter/counter-sysfs.c line 735
Quoted 2 lines
drivers/counter/counter-sysfs.c new file +2
-0+static int counter_sysfs_attr_add (struct counter_device *const counter, +struct counter_attribute_group *group)
As below, group is a confusing name, so I'd prefix it with something.
Quoted 51 lines
drivers/counter/counter-sysfs.c new file +51
-0+{ +const enum counter_scope scope =COUNTER_SCOPE_DEVICE ; +struct device *const dev = &counter->dev; +int err; +size_t i; + +/* Add Signals sysfs attributes */ + err =counter_sysfs_signals_add (counter, group); +if (err <0 ) +return err; + group += counter->num_signals; + +/* Add Counts sysfs attributes */ + err =counter_sysfs_counts_add (counter, group); +if (err <0 ) +return err; + group += counter->num_counts; + +/* Create name attribute */ + err =counter_name_attr_create (dev, group, counter->name); +if (err <0 ) +return err; + +/* Create num_signals attribute */ + err =counter_attr_create (dev, group, &counter_num_signals_comp, scope, +NULL ); +if (err <0 ) +return err; + +/* Create num_counts attribute */ + err =counter_attr_create (dev, group, &counter_num_counts_comp, scope, +NULL ); +if (err <0 ) +return err; + +/* Create an attribute for each extension */ +for (i =0 ; i < counter->num_ext; i++) { + err =counter_attr_create (dev, group, counter->ext + i, scope, +NULL ); +if (err <0 ) +return err; + } + +return 0 ; +} + +/** +* counter_sysfs_add - Adds Counter sysfs attributes to the device structure +* @counter: Pointer to the Counter device structure +* +* Counter sysfs attributes are created and added to the respective device
drivers/counter/counter-sysfs.c line 796
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+* structure for later registration to the system. Resource-managed memory +* allocation is performed by this function, and this memory should be freed +* when no longer needed (automatically by a device_unregister call, or +* manually by a devres_release_all call). +*/ +int counter_sysfs_add (struct counter_device *const counter) +{ +struct device *const dev = &counter->dev; +const size_t num_groups = counter->num_signals + counter->num_counts +1 ; +struct counter_attribute_group *groups;
Naming is a bit too similar to 'group' to lead to nice readable code. Maybe cattr_groups or something like that? groups and dev->groups not being the same thing is definitely confusing!
Quoted 21 lines
drivers/counter/counter-sysfs.c new file +21
-0+size_t i, j; +int err; +struct attribute_group *group; +struct counter_attribute *p; + +/* Allocate space for attribute groups (signals, counts, and ext) */ + groups =devm_kcalloc (dev, num_groups,sizeof (*groups),GFP_KERNEL ); +if (!groups) +return -ENOMEM ; + +/* Initialize attribute lists */ +for (i =0 ; i < num_groups; i++) +INIT_LIST_HEAD (&groups[i].attr_list); + +/* Add Counter device sysfs attributes */ + err =counter_sysfs_attr_add (counter, groups); +if (err <0 ) +return err; + +/* Allocate attribute groups for association with device */ + dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
drivers/counter/counter-sysfs.c line 827
Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10
-0+ GFP_KERNEL); +if (!dev->groups) +return -ENOMEM ; + +/* Prepare each group of attributes for association */ +for (i =0 ; i < num_groups; i++) { +/* Allocate space for attribute group */ + group =devm_kzalloc (dev,sizeof (*group),GFP_KERNEL ); +if (!group) +return -ENOMEM ;
Slight nitpick, is I'd have been tempted to reduce the number of allocations by doing this as a single array allocation then use the groups within this loop. Really trivial though!
Quoted 19 lines
drivers/counter/counter-sysfs.c new file +19
-0+ group->name = groups[i].name; + +/* Allocate space for attribute pointers */ + group->attrs =devm_kcalloc (dev, groups[i].num_attr +1 , +sizeof (*group->attrs),GFP_KERNEL ); +if (!group->attrs) +return -ENOMEM ; + +/* Add attribute pointers to attribute group */ + j =0 ; +list_for_each_entry (p, &groups[i].attr_list, l) + group->attrs[j++] = &p->dev_attr.attr; + +/* Associate attribute group */ + dev->groups[i] = group; + } + +return 0 ; +}
...
Quoted 80 lines
drivers/counter/stm32-lptimer-cnt.c +25
-10@@ -17,6 +17,7 @@#include <linux/module.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> +#include <linux/types.h> struct stm32_lptim_cnt {struct counter_device counter;@@ -130,32 +131,46 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)* +---------+----------+----------+---------+----------+---------+ */ enum stm32_lptim_cnt_function { -STM32_LPTIM_COUNTER_INCREASE , -STM32_LPTIM_ENCODER_BOTH_EDGE , +STM32_LPTIM_COUNTER_INCREASE =COUNTER_FUNCTION_INCREASE , +STM32_LPTIM_ENCODER_BOTH_EDGE =COUNTER_FUNCTION_QUADRATURE_X4 , };static const enum counter_function stm32_lptim_cnt_functions[] = { - [STM32_LPTIM_COUNTER_INCREASE ] =COUNTER_FUNCTION_INCREASE , - [STM32_LPTIM_ENCODER_BOTH_EDGE ] =COUNTER_FUNCTION_QUADRATURE_X4 , +STM32_LPTIM_COUNTER_INCREASE , +STM32_LPTIM_ENCODER_BOTH_EDGE , };enum stm32_lptim_synapse_action { +/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */ STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE ,STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE ,STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES ,STM32_LPTIM_SYNAPSE_ACTION_NONE , }; -static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = { -/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */ +static const enum stm32_lptim_synapse_action stm32_lptim_c2l_actions_map[] = { + [COUNTER_SYNAPSE_ACTION_RISING_EDGE ] =STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE , + [COUNTER_SYNAPSE_ACTION_FALLING_EDGE ] =STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE , + [COUNTER_SYNAPSE_ACTION_BOTH_EDGES ] =STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES , + [COUNTER_SYNAPSE_ACTION_NONE ] =STM32_LPTIM_SYNAPSE_ACTION_NONE , +}; + +static const enum counter_synapse_action stm32_lptim_l2c_actions_map[] = { [STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE ] =COUNTER_SYNAPSE_ACTION_RISING_EDGE , [STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE ] =COUNTER_SYNAPSE_ACTION_FALLING_EDGE , [STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES ] =COUNTER_SYNAPSE_ACTION_BOTH_EDGES , [STM32_LPTIM_SYNAPSE_ACTION_NONE ] =COUNTER_SYNAPSE_ACTION_NONE , }; +static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = { +COUNTER_SYNAPSE_ACTION_RISING_EDGE , +COUNTER_SYNAPSE_ACTION_FALLING_EDGE , +COUNTER_SYNAPSE_ACTION_BOTH_EDGES , +COUNTER_SYNAPSE_ACTION_NONE , +}; +static int stm32_lptim_cnt_read (struct counter_device *counter, -struct counter_count *count,unsigned long *val) +struct counter_count *count,u64 *val) {struct stm32_lptim_cnt *const priv = counter->priv;u32 cnt;@@ -170,9 +185,9 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,return 0 ; } -static int stm32_lptim_cnt_function_get (struct counter_device *counter, -struct counter_count *count, -size_t *function) +static int stm32_lptim_cnt_function_read (struct counter_device *counter, +struct counter_count *count, +enum counter_function *function) {struct stm32_lptim_cnt *const priv = counter->priv;
drivers/counter/stm32-lptimer-cnt.c static int stm32_lptim_cnt_function_get(struct counter_device *counter, · line 209
Quoted 9 lines
drivers/counter/stm32-lptimer-cnt.c +3
-3@@ -189,9 +204,9 @@ static int stm32_lptim_cnt_function_get(struct counter_device *counter,return -EINVAL ; } -static int stm32_lptim_cnt_function_set (struct counter_device *counter, -struct counter_count *count, -size_t function) +static int stm32_lptim_cnt_function_write (struct counter_device *counter, +struct counter_count *count, +enum counter_function function)
As mentioned below, there is a bit of a mess of types here that you should clean up.
Quoted 177 lines
drivers/counter/stm32-lptimer-cnt.c +40
-56{struct stm32_lptim_cnt *const priv = counter->priv;@@ -212,9 +227,9 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter, } } -static ssize_t stm32_lptim_cnt_enable_read (struct counter_device *counter, -struct counter_count *count, -void *private,char *buf) +static int stm32_lptim_cnt_enable_read (struct counter_device *counter, +struct counter_count *count, +u8 *enable) {struct stm32_lptim_cnt *const priv = counter->priv;int ret;@@ -223,22 +238,18 @@ static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,if (ret <0 )return ret; -return scnprintf (buf,PAGE_SIZE ,"%u\n" , ret); + *enable = ret; + +return 0 ; } -static ssize_t stm32_lptim_cnt_enable_write (struct counter_device *counter, -struct counter_count *count, -void *private, -const char *buf,size_t len) +static int stm32_lptim_cnt_enable_write (struct counter_device *counter, +struct counter_count *count, +u8 enable) {struct stm32_lptim_cnt *const priv = counter->priv; -bool enable;int ret; - ret =kstrtobool (buf, &enable); -if (ret) -return ret; -/* Check nobody uses the timer, or already disabled/enabled */ ret =stm32_lptim_is_enabled (priv);if ((ret <0 ) || (!ret && !enable))@@ -254,65 +265,54 @@ static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,if (ret)return ret; -return len; +return 0 ; } -static ssize_t stm32_lptim_cnt_ceiling_read (struct counter_device *counter, -struct counter_count *count, -void *private,char *buf) +static int stm32_lptim_cnt_ceiling_read (struct counter_device *counter, +struct counter_count *count, +u64 *ceiling) {struct stm32_lptim_cnt *const priv = counter->priv; -return snprintf (buf,PAGE_SIZE ,"%u\n" , priv->ceiling); + *ceiling = priv->ceiling; + +return 0 ; } -static ssize_t stm32_lptim_cnt_ceiling_write (struct counter_device *counter, -struct counter_count *count, -void *private, -const char *buf,size_t len) +static int stm32_lptim_cnt_ceiling_write (struct counter_device *counter, +struct counter_count *count, +u64 ceiling) {struct stm32_lptim_cnt *const priv = counter->priv; -unsigned int ceiling; -int ret;if (stm32_lptim_is_enabled (priv))return -EBUSY ; - ret =kstrtouint (buf,0 , &ceiling); -if (ret) -return ret; -if (ceiling >STM32_LPTIM_MAX_ARR )return -ERANGE ; priv->ceiling = ceiling; -return len; +return 0 ; } -static const struct counter_count_ext stm32_lptim_cnt_ext[] = { - { - .name ="enable" , - .read = stm32_lptim_cnt_enable_read, - .write = stm32_lptim_cnt_enable_write - }, - { - .name ="ceiling" , - .read = stm32_lptim_cnt_ceiling_read, - .write = stm32_lptim_cnt_ceiling_write - }, +static struct counter_comp stm32_lptim_cnt_ext[] = { +COUNTER_COMP_ENABLE (stm32_lptim_cnt_enable_read, + stm32_lptim_cnt_enable_write), +COUNTER_COMP_CEILING (stm32_lptim_cnt_ceiling_read, + stm32_lptim_cnt_ceiling_write), }; -static int stm32_lptim_cnt_action_get (struct counter_device *counter, -struct counter_count *count, -struct counter_synapse *synapse, -size_t *action) +static int stm32_lptim_cnt_action_read (struct counter_device *counter, +struct counter_count *count, +struct counter_synapse *synapse, +enum counter_synapse_action *action) {struct stm32_lptim_cnt *const priv = counter->priv; -size_t function; +enum counter_function function;int err; - err =stm32_lptim_cnt_function_get (counter, count, &function); + err =stm32_lptim_cnt_function_read (counter, count, &function);if (err)return err;@@ -320,12 +320,12 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,case STM32_LPTIM_COUNTER_INCREASE :/* LP Timer acts as up-counter on input 1 */ if (synapse->signal->id == count->synapses[0 ].signal->id) - *action = priv->polarity; + *action = stm32_lptim_l2c_actions_map[priv->polarity];else - *action =STM32_LPTIM_SYNAPSE_ACTION_NONE ; + *action =COUNTER_SYNAPSE_ACTION_NONE ;return 0 ;case STM32_LPTIM_ENCODER_BOTH_EDGE : - *action = priv->polarity; + *action = stm32_lptim_l2c_actions_map[priv->polarity];return 0 ;default :/* should never reach this path */ @@ -333,43 +333,39 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter, } } -static int stm32_lptim_cnt_action_set (struct counter_device *counter, -struct counter_count *count, -struct counter_synapse *synapse, -size_t action) +static int stm32_lptim_cnt_action_write (struct counter_device *counter, +struct counter_count *count, +struct counter_synapse *synapse, +enum counter_synapse_action action) {struct stm32_lptim_cnt *const priv = counter->priv; -size_t function; +enum counter_function function;int err;if (stm32_lptim_is_enabled (priv))return -EBUSY ; - err =stm32_lptim_cnt_function_get (counter, count, &function); + err =stm32_lptim_cnt_function_read (counter, count, &function);if (err)return err;/* only set polarity when in counter mode (on input 1) */ - if (function == STM32_LPTIM_COUNTER_INCREASE
drivers/counter/stm32-lptimer-cnt.c static int stm32_lptim_cnt_action_get(struct counter_device *counter, · line 353
Quoted 10 lines
drivers/counter/stm32-lptimer-cnt.c +1
-9- && synapse->signal->id == count->synapses[0].signal->id) { -switch (action) { -case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE : -case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE : -case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES : - priv->polarity = action; -return 0 ; - } - } + if ((enum stm32_lptim_cnt_function)function != STM32_LPTIM_COUNTER_INCREASE
This bothers me a little. Why are you casting from enum counter_function to enum stm32_lptim_cnt_function? That function is indeed putting an enum stm32_lptim_cnt_function into function and it really shouldn't be. Whilst the values of the enums are the same, it would be better to have explicit item by item conversion if that's necessary. I 'think' you can just get rid of the
enum stm32_lptim_cnt_function though and use the counter one throughout the driver.
If you want to keep changes minimal, put a conversion function in place.
Quoted 23 lines
drivers/counter/stm32-lptimer-cnt.c +10
-5+ || synapse->signal->id != count->synapses[0].signal->id + || action == COUNTER_SYNAPSE_ACTION_NONE) +return -EINVAL ; -return -EINVAL ; + priv->polarity = stm32_lptim_c2l_actions_map[action]; + +return 0 ; }static const struct counter_ops stm32_lptim_cnt_ops = { .count_read = stm32_lptim_cnt_read, - .function_get = stm32_lptim_cnt_function_get, - .function_set = stm32_lptim_cnt_function_set, - .action_get = stm32_lptim_cnt_action_get, - .action_set = stm32_lptim_cnt_action_set, + .function_read = stm32_lptim_cnt_function_read, + .function_write = stm32_lptim_cnt_function_write, + .action_read = stm32_lptim_cnt_action_read, + .action_write = stm32_lptim_cnt_action_write, };static struct counter_signal stm32_lptim_cnt_signals[] = {
PATCH v12 7/17 counter: Update counter.h comments to reflect sysfs internalization
In reply to William Breathitt GrayThe Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface header file comments to reflect the changes.
--- drivers/counter/counter-core.c | 3 +++ include/linux/counter.h | 43 ++++++++++++++++------------------ 2 files changed, 23 insertions(+), 23 deletions(-)
drivers/counter/counter-core.c +3 -0
@@ -41,6 +41,9 @@ static struct bus_type counter_bus_type = {* This function registers a Counter to the system. A sysfs "counter" directory * will be created and populated with sysfs attributes correlating with the * Counter Signals, Synapses, and Counts respectively. +* +* RETURNS: +* 0 on success, negative error number on failure. */ int counter_register (struct counter_device *const counter) {
include/linux/counter.h +20 -23
75 lines not on this page · raw message
Signature · 1 line
2.32.0
David Lechner
reviewerRe: [PATCH v12 07/17] counter: Update counter.h comments to reflect sysfs internalization
Quoted 78 lines · William Breathitt Gray
The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface header file comments to reflect the changes.
Signed-off-by William Breathitt Gray <[email protected]>--- drivers/counter/counter-core.c | 3 +++ include/linux/counter.h | 43 ++++++++++++++++------------------ 2 files changed, 23 insertions(+), 23 deletions(-)drivers/counter/counter-core.c +3
-0@@ -41,6 +41,9 @@ static struct bus_type counter_bus_type = {* This function registers a Counter to the system. A sysfs "counter" directory * will be created and populated with sysfs attributes correlating with the * Counter Signals, Synapses, and Counts respectively. +* +* RETURNS: +* 0 on success, negative error number on failure. */ int counter_register (struct counter_device *const counter) {include/linux/counter.h +12
-14@@ -188,11 +188,10 @@ struct counter_comp {/** * struct counter_signal - Counter Signal node -* @id: unique ID used to identify signal -* @name: device-specific Signal name; ideally, this should match the name -* as it appears in the datasheet documentation -* @ext: optional array of Counter Signal extensions -* @num_ext: number of Counter Signal extensions specified in @ext +* @id: unique ID used to identify the Signal +* @name: device-specific Signal name +* @ext: optional array of Signal extensions +* @num_ext: number of Signal extensions specified in @ext */ struct counter_signal {int id;@@ -206,7 +205,7 @@ struct counter_signal {* struct counter_synapse - Counter Synapse node * @actions_list: array of available action modes * @num_actions: number of action modes specified in @actions_list -* @signal: pointer to associated signal +* @signal: pointer to the associated Signal */ struct counter_synapse {const enum counter_synapse_action *actions_list;@@ -217,15 +216,14 @@ struct counter_synapse {/** * struct counter_count - Counter Count node -* @id: unique ID used to identify Count -* @name: device-specific Count name; ideally, this should match -* the name as it appears in the datasheet documentation -* @functions_list: array available function modes +* @id: unique ID used to identify the Count +* @name: device-specific Count name +* @functions_list: array of available function modes * @num_functions: number of function modes specified in @functions_list -* @synapses: array of synapses for initialization -* @num_synapses: number of synapses specified in @synapses -* @ext: optional array of Counter Count extensions -* @num_ext: number of Counter Count extensions specified in @ext +* @synapses: array of Synapses for initialization +* @num_synapses: number of Synapses specified in @synapses +* @ext: optional array of Count extensions +* @num_ext: number of Count extensions specified in @ext */ struct counter_count {int id;
include/linux/counter.h struct counter_count { · line 251
Quoted 9 lines
include/linux/counter.h +0
-6@@ -243,15 +241,14 @@ struct counter_count {/** * struct counter_ops - Callbacks from driver -* @signal_read: optional read callback for Signal attribute. The read -* level of the respective Signal should be passed back via -* the level parameter. -* @count_read: optional read callback for Count attribute. The read -* value of the respective Count should be passed back via -* the val parameter.
Are these no longer optional? If they really are optional, it would be nice to keep that information in the description.
Quoted 14 lines
include/linux/counter.h +7
-2-* @count_write: optional write callback for Count attribute. The write -* value for the respective Count is passed in via the val +* @signal_read: read callback for Signals. The read level of the +* respective Signal should be passed back via the level +* parameter. +* @count_read: read callback for Counts. The read value of the +* respective Count should be passed back via the value * parameter. +* @count_write: write callback for Counts. The write value for the +* respective Count is passed in via the value parameter. * @function_read: read callback the Count function modes. The read * function mode of the respective Count should be passed * back via the function parameter.
include/linux/counter.h struct counter_ops { · line 291
Quoted 5 lines
include/linux/counter.h +1
-1@@ -291,7 +288,7 @@ struct counter_ops {/** * struct counter_device - Counter data structure -* @name: name of the device as it appears in the datasheet +* @name: name of the device
Is there a recommended naming convention if using the datasheet is no longer recommended?
Quoted 4 lines
include/linux/counter.h +0
-0* @parent: optional parent device providing the counters * @ops: callbacks from driver * @signals: array of Signals
Re: [PATCH v12 07/17] counter: Update counter.h comments to reflect sysfs internalization
Quoted 80 lines · David Lechner
Quoted 79 lines · William Breathitt Gray
The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface header file comments to reflect the changes.
Signed-off-by William Breathitt Gray <[email protected]>--- drivers/counter/counter-core.c | 3 +++ include/linux/counter.h | 43 ++++++++++++++++------------------ 2 files changed, 23 insertions(+), 23 deletions(-)drivers/counter/counter-core.c +3
-0@@ -41,6 +41,9 @@ static struct bus_type counter_bus_type = {* This function registers a Counter to the system. A sysfs "counter" directory * will be created and populated with sysfs attributes correlating with the * Counter Signals, Synapses, and Counts respectively. +* +* RETURNS: +* 0 on success, negative error number on failure. */ int counter_register (struct counter_device *const counter) {include/linux/counter.h +12
-14@@ -188,11 +188,10 @@ struct counter_comp {/** * struct counter_signal - Counter Signal node -* @id: unique ID used to identify signal -* @name: device-specific Signal name; ideally, this should match the name -* as it appears in the datasheet documentation -* @ext: optional array of Counter Signal extensions -* @num_ext: number of Counter Signal extensions specified in @ext +* @id: unique ID used to identify the Signal +* @name: device-specific Signal name +* @ext: optional array of Signal extensions +* @num_ext: number of Signal extensions specified in @ext */ struct counter_signal {int id;@@ -206,7 +205,7 @@ struct counter_signal {* struct counter_synapse - Counter Synapse node * @actions_list: array of available action modes * @num_actions: number of action modes specified in @actions_list -* @signal: pointer to associated signal +* @signal: pointer to the associated Signal */ struct counter_synapse {const enum counter_synapse_action *actions_list;@@ -217,15 +216,14 @@ struct counter_synapse {/** * struct counter_count - Counter Count node -* @id: unique ID used to identify Count -* @name: device-specific Count name; ideally, this should match -* the name as it appears in the datasheet documentation -* @functions_list: array available function modes +* @id: unique ID used to identify the Count +* @name: device-specific Count name +* @functions_list: array of available function modes * @num_functions: number of function modes specified in @functions_list -* @synapses: array of synapses for initialization -* @num_synapses: number of synapses specified in @synapses -* @ext: optional array of Counter Count extensions -* @num_ext: number of Counter Count extensions specified in @ext +* @synapses: array of Synapses for initialization +* @num_synapses: number of Synapses specified in @synapses +* @ext: optional array of Count extensions +* @num_ext: number of Count extensions specified in @ext */ struct counter_count {int id;@@ -243,15 +241,14 @@ struct counter_count {
Quoted 11 lines
include/linux/counter.h struct counter_count { · line 251
Quoted 8 lines
include/linux/counter.h +0
-6/** * struct counter_ops - Callbacks from driver -* @signal_read: optional read callback for Signal attribute. The read -* level of the respective Signal should be passed back via -* the level parameter. -* @count_read: optional read callback for Count attribute. The read -* value of the respective Count should be passed back via -* the val parameter. Are these no longer optional? If they really are optional, it would be nice to keep that information in the description.
I'd expect drivers to have at least the count_read() and function_read() callbacks defined and set; otherwise such a counter driver would be rather useless to a user. I'll update the documentation here to make it clear the other callbacks are optional.
Quoted 15 lines
Quoted 15 lines
include/linux/counter.h +7
-2-* @count_write: optional write callback for Count attribute. The write -* value for the respective Count is passed in via the val +* @signal_read: read callback for Signals. The read level of the +* respective Signal should be passed back via the level +* parameter. +* @count_read: read callback for Counts. The read value of the +* respective Count should be passed back via the value * parameter. +* @count_write: write callback for Counts. The write value for the +* respective Count is passed in via the value parameter. * @function_read: read callback the Count function modes. The read * function mode of the respective Count should be passed * back via the function parameter. @@ -291,7 +288,7 @@ struct counter_ops {
Quoted 7 lines
include/linux/counter.h struct counter_ops { · line 291
Quoted 4 lines
include/linux/counter.h +1
-1/** * struct counter_device - Counter data structure -* @name: name of the device as it appears in the datasheet +* @name: name of the device Is there a recommended naming convention if using the datasheet is no longer recommended?
I decided to remove the "as it appears in the datasheet" phrase because there may be cases where the datasheet name isn't the best name to provide for the counter device. For example, with the 104-quad-8 driver there is ambiguity about whether it's more useful for the name to be "104-QUAD-8" to match the PC104 card, or "LS7266R1" to match the counter chip integrated on the 104-QUAD-8 card. Another example would be the recent interrupt-cnt driver which isn't for a particular device (it counts any interrupt source) so thus does not have a corresponding datasheet. I figure it's best to leave it up to the driver maintainer to choose an apt name that will make sense for the users.
William Breathitt Gray
PATCH v12 8/17 docs: counter: Update to reflect sysfs internalization
In reply to William Breathitt GrayThe Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes.
--- Documentation/ABI/testing/sysfs-bus-counter | 9 +- Documentation/driver-api/generic-counter.rst | 243 ++++++++++++++----- 2 files changed, 185 insertions(+), 67 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter +8 -1
@@ -286,7 +286,14 @@ What: /sys/bus/counter/devices/counterX/signalY/signal KernelVersion: 5.2 Contact: [email protected] Description: - Signal data of Signal Y represented as a string. + Signal level state of Signal Y. The following signal level + states are available: + + low: + Low level state. + + high: + High level state. What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode KernelVersion: 5.2
Documentation/driver-api/generic-counter.rst +177 -66
262 lines not on this page · raw message
Signature · 1 line
2.32.0
Re: [PATCH v12 08/17] docs: counter: Update to reflect sysfs internalization
Quoted 7 lines · William Breathitt Gray
The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes.
Signed-off-by William Breathitt Gray<[email protected]>---
PATCH v12 9/17 counter: Move counter enums to uapi header
In reply to William Breathitt GrayThis is in preparation for a subsequent patch implementing a character device interface for the Counter subsystem.
--- MAINTAINERS | 1 + include/linux/counter.h | 42 +-------------------------- include/uapi/linux/counter.h | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 include/uapi/linux/counter.h
MAINTAINERS +1 -0
@@ -4687,6 +4687,7 @@ F: Documentation/ABI/testing/sysfs-bus-counter F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h +F: include/uapi/linux/counter.h CP2615 I2C DRIVER M: Bence Csókás <[email protected]>
include/linux/counter.h +1 -41
54 lines not on this page · raw message
include/uapi/linux/counter.h new file +56 -0
56 lines not on this page · raw message
Signature · 1 line
2.32.0
PATCH v12 10/17 counter: Add character device interface
In reply to William Breathitt GrayThis patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
--- drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 494 +++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 44 ++- include/linux/counter.h | 45 +++ include/uapi/linux/counter.h | 77 +++++ 6 files changed, 670 insertions(+), 6 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h
drivers/counter/Makefile +1 -1
@@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o
drivers/counter/counter-chrdev.c new file +494 -0
494 lines not on this page · raw message
drivers/counter/counter-chrdev.h new file +14 -0
@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Counter character device interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _COUNTER_CHRDEV_H_ +#define _COUNTER_CHRDEV_H_ + +#include <linux/counter.h> + +int counter_chrdev_add (struct counter_device *const counter); +void counter_chrdev_remove (struct counter_device *const counter); + +#endif /* _COUNTER_CHRDEV_H_ */
drivers/counter/counter-core.c +39 -5
103 lines not on this page · raw message
include/linux/counter.h +45 -0
90 lines not on this page · raw message
include/uapi/linux/counter.h +77 -0
89 lines not on this page · raw message
Signature · 1 line
2.32.0
David Lechner
reviewerRe: [PATCH v12 10/17] counter: Add character device interface
Quoted 29 lines · William Breathitt Gray
This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
drivers/counter/counter-chrdev.c new file +13
-0@@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Generic Counter character device interface +* Copyright (C) 2020 William Breathitt Gray +*/ + +#include <linux/cdev.h> +#include <linux/counter.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/kfifo.h>
drivers/counter/counter-chrdev.c line 23
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/nospec.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timekeeping.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/uaccess.h>
Move this up one for alphabetical order.
Quoted 74 lines
drivers/counter/counter-chrdev.c new file +74
-0+ +#include "counter-chrdev.h" + +struct counter_comp_node { +struct list_head l; +struct counter_component component; +struct counter_comp comp; +void *parent; +}; + +static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ; + +do { +if (kfifo_is_empty (&counter->events)) { +if (filp->f_flags &O_NONBLOCK ) +return -EAGAIN ; + + err =wait_event_interruptible (counter->events_wait, + !kfifo_is_empty (&counter->events)); +if (err <0 ) +return err; + } + +if (mutex_lock_interruptible (&counter->events_lock)) +return -ERESTARTSYS ; + err =kfifo_to_user (&counter->events, buf, len, &copied); +mutex_unlock (&counter->events_lock); +if (err <0 ) +return err; + }while (!copied); + +return copied; +} + +static __poll_t counter_chrdev_poll (struct file *filp, +struct poll_table_struct *pollt) +{ +struct counter_device *const counter = filp->private_data; +__poll_t events =0 ; + +poll_wait (filp, &counter->events_wait, pollt); + +if (!kfifo_is_empty (&counter->events)) + events =EPOLLIN |EPOLLRDNORM ; + +return events; +} + +static void counter_events_list_free (struct list_head *const events_list) +{ +struct counter_event_node *p, *n; +struct counter_comp_node *q, *o; + +list_for_each_entry_safe (p, n, events_list, l) { +/* Free associated component nodes */ +list_for_each_entry_safe (q, o, &p->comp_list, l) { +list_del (&q->l); +kfree (q); + } + +/* Free event node */ +list_del (&p->l); +kfree (p); + } +} +
drivers/counter/counter-chrdev.c line 107
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+static int counter_set_event_node (struct counter_device *const counter, +struct counter_watch *const watch, +const struct counter_comp_node *const cfg) +{ +unsigned long flags; +struct counter_event_node *event_node; +int err =0 ; +struct counter_comp_node *comp_node; + +spin_lock_irqsave (&counter->events_list_lock, flags);
Since this is just the next watch list and doesn't involve enabled events, we should be able to use a mutex here instead of disabling interrupts. This is a rather length operation to have interrupts disabled when we don't really need to.
Quoted 17 lines
drivers/counter/counter-chrdev.c new file +17
-0+ +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->next_events_list, l) +if (event_node->event == watch->event && + event_node->channel == watch->channel) +break ; + +/* If event is not already in the list */ +if (&event_node->l == &counter->next_events_list) { +/* Allocate new event node */ + event_node =kmalloc (sizeof (*event_node),GFP_ATOMIC ); +if (!event_node) { + err = -ENOMEM ; + goto exit_early; + } + +/* Configure event node and add to the list */
drivers/counter/counter-chrdev.c line 134
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ event_node->event = watch->event; + event_node->channel = watch->channel; +INIT_LIST_HEAD (&event_node->comp_list); +list_add (&event_node->l, &counter->next_events_list); + } + +/* Check if component watch has already been set before */ +list_for_each_entry (comp_node, &event_node->comp_list, l) +if (comp_node->parent == cfg->parent && + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
Do we need to free event_node here?
Quoted 31 lines
drivers/counter/counter-chrdev.c new file +31
-0+ err = -EINVAL ; + goto exit_early; + } + +/* Allocate component node */ + comp_node =kmalloc (sizeof (*comp_node),GFP_ATOMIC ); +if (!comp_node) { +/* Free event node if no one else is watching */ +if (list_empty (&event_node->comp_list)) { +list_del (&event_node->l); +kfree (event_node); + } + err = -ENOMEM ; + goto exit_early; + } + *comp_node = *cfg; + +/* Add component node to event node */ +list_add_tail (&comp_node->l, &event_node->comp_list); + +exit_early: +spin_unlock_irqrestore (&counter->events_list_lock, flags); + +return err; +} + +static int counter_disable_events (struct counter_device *const counter) +{ +unsigned long flags; +int err =0 ; +
drivers/counter/counter-chrdev.c line 175
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+spin_lock_irqsave (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->events_list); + +if (counter->ops->events_configure) + err = counter->ops->events_configure (counter); + +spin_unlock_irqrestore (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->next_events_list);
Do we need to hold a lock on counter->next_events_list here?
Quoted 103 lines
drivers/counter/counter-chrdev.c new file +103
-0+ +return err; +} + +static int counter_add_watch (struct counter_device *const counter, +const unsigned long arg) +{ +void __user *const uwatch = (void __user *)arg; +struct counter_watch watch; +struct counter_comp_node comp_node = {}; +size_t parent, id; +struct counter_comp *ext; +size_t num_ext; +int err; + +if (copy_from_user (&watch, uwatch,sizeof (watch))) +return -EFAULT ; + +if (watch.component.type ==COUNTER_COMPONENT_NONE ) + goto no_component; + + parent = watch.component.parent; + +/* Configure parent component info for comp node */ +switch (watch.component.scope) { +case COUNTER_SCOPE_DEVICE : + ext = counter->ext; + num_ext = counter->num_ext; +break ; +case COUNTER_SCOPE_SIGNAL : +if (parent >= counter->num_signals) +return -EINVAL ; + parent =array_index_nospec (parent, counter->num_signals); + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; +break ; +case COUNTER_SCOPE_COUNT : +if (parent >= counter->num_counts) +return -EINVAL ; + parent =array_index_nospec (parent, counter->num_counts); + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; +break ; +default : +return -EINVAL ; + } + + id = watch.component.id; + +/* Configure component info for comp node */ +switch (watch.component.type) { +case COUNTER_COMPONENT_SIGNAL : +if (watch.component.scope !=COUNTER_SCOPE_SIGNAL ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SIGNAL_LEVEL ; + comp_node.comp.signal_u32_read = counter->ops->signal_read; +break ; +case COUNTER_COMPONENT_COUNT : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_U64 ; + comp_node.comp.count_u64_read = counter->ops->count_read; +break ; +case COUNTER_COMPONENT_FUNCTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_FUNCTION ; + comp_node.comp.count_u32_read = counter->ops->function_read; +break ; +case COUNTER_COMPONENT_SYNAPSE_ACTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; +if (id >= counter->counts[parent].num_synapses) +return -EINVAL ; + id =array_index_nospec (id, counter->counts[parent].num_synapses); + + comp_node.comp.type =COUNTER_COMP_SYNAPSE_ACTION ; + comp_node.comp.action_read = counter->ops->action_read; + comp_node.comp.priv = counter->counts[parent].synapses + id; +break ; +case COUNTER_COMPONENT_EXTENSION : +if (id >= num_ext) +return -EINVAL ; + id =array_index_nospec (id, num_ext); + + comp_node.comp = ext[id]; +break ; +default : +return -EINVAL ; + } +/* Check if any read callback is set; this is part of a union */ +if (!comp_node.comp.count_u8_read) +return -EOPNOTSUPP ; +
drivers/counter/counter-chrdev.c line 288
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+no_component: +if (counter->ops->watch_validate) { + err = counter->ops->watch_validate (counter, &watch); +if (err <0 ) +return err; + } + + comp_node.component = watch.component; + +return counter_set_event_node (counter, &watch, &comp_node);
The mutex I suggested for next_events_list should probably be held across the call to watch_validate() and counter_set_event_node(). Otherwise a new item could be added in between the time it was validated and when it was added making it no longer valid.
Probably not likely to ever happen, but theoretially possible.
Quoted 14 lines
drivers/counter/counter-chrdev.c new file +14
-0+} + +static long counter_chrdev_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; +int err =0 ; + +switch (cmd) { +case COUNTER_ADD_WATCH_IOCTL : +return counter_add_watch (counter, arg); +case COUNTER_ENABLE_EVENTS_IOCTL : +spin_lock_irqsave (&counter->events_list_lock, flags);
drivers/counter/counter-chrdev.c line 312
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ +counter_events_list_free (&counter->events_list); +list_replace_init (&counter->next_events_list, + &counter->events_list); + +if (counter->ops->events_configure) + err = counter->ops->events_configure (counter); + +spin_unlock_irqrestore (&counter->events_list_lock, flags); +return err;
Could be nice to pull this out into a counter_enable_events() function to balance counter_disable_events().
Quoted 46 lines
drivers/counter/counter-chrdev.c new file +46
-0+case COUNTER_DISABLE_EVENTS_IOCTL : +return counter_disable_events (counter); +default : +return -ENOIOCTLCMD ; + } +} + +static int counter_chrdev_open (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter =container_of (inode->i_cdev, +typeof (*counter), + chrdev); + +get_device (&counter->dev); + filp->private_data = counter; + +return nonseekable_open (inode, filp); +} + +static int counter_chrdev_release (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter = filp->private_data; +int err; + + err =counter_disable_events (counter); +if (err <0 ) +return err; + +put_device (&counter->dev); + +return 0 ; +} + +static const struct file_operations counter_fops = { + .llseek = no_llseek, + .read = counter_chrdev_read, + .poll = counter_chrdev_poll, + .unlocked_ioctl = counter_chrdev_ioctl, + .open = counter_chrdev_open, + .release = counter_chrdev_release, +}; + +int counter_chrdev_add (struct counter_device *const counter) +{ +/* Initialize Counter events lists */ +INIT_LIST_HEAD (&counter->events_list);
drivers/counter/counter-chrdev.c line 368
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+INIT_LIST_HEAD (&counter->next_events_list); +spin_lock_init (&counter->events_list_lock); +init_waitqueue_head (&counter->events_wait); +mutex_init (&counter->events_lock); + +/* Initialize character device */ +cdev_init (&counter->chrdev, &counter_fops); + +/* Allocate Counter events queue */ +return kfifo_alloc (&counter->events,64 ,GFP_ATOMIC );
GFP_KERNEL?
Quoted 79 lines
drivers/counter/counter-chrdev.c new file +79
-0+} + +void counter_chrdev_remove (struct counter_device *const counter) +{ +kfifo_free (&counter->events); +} + +static int counter_get_data (struct counter_device *const counter, +const struct counter_comp_node *const comp_node, +u64 *const value) +{ +const struct counter_comp *const comp = &comp_node->comp; +void *const parent = comp_node->parent; +u8 value_u8 =0 ; +u32 value_u32 =0 ; +int ret; + +if (comp_node->component.type ==COUNTER_COMPONENT_NONE ) +return 0 ; + +switch (comp->type) { +case COUNTER_COMP_U8 : +case COUNTER_COMP_BOOL : +switch (comp_node->component.scope) { +case COUNTER_SCOPE_DEVICE : + ret = comp->device_u8_read (counter, &value_u8); +break ; +case COUNTER_SCOPE_SIGNAL : + ret = comp->signal_u8_read (counter, parent, &value_u8); +break ; +case COUNTER_SCOPE_COUNT : + ret = comp->count_u8_read (counter, parent, &value_u8); +break ; + } + *value = value_u8; +return ret; +case COUNTER_COMP_SIGNAL_LEVEL : +case COUNTER_COMP_FUNCTION : +case COUNTER_COMP_ENUM : +case COUNTER_COMP_COUNT_DIRECTION : +case COUNTER_COMP_COUNT_MODE : +switch (comp_node->component.scope) { +case COUNTER_SCOPE_DEVICE : + ret = comp->device_u32_read (counter, &value_u32); +break ; +case COUNTER_SCOPE_SIGNAL : + ret = comp->signal_u32_read (counter, parent, + &value_u32); +break ; +case COUNTER_SCOPE_COUNT : + ret = comp->count_u32_read (counter, parent, &value_u32); +break ; + } + *value = value_u32; +return ret; +case COUNTER_COMP_U64 : +switch (comp_node->component.scope) { +case COUNTER_SCOPE_DEVICE : +return comp->device_u64_read (counter, value); +case COUNTER_SCOPE_SIGNAL : +return comp->signal_u64_read (counter, parent, value); +case COUNTER_SCOPE_COUNT : +return comp->count_u64_read (counter, parent, value); +default : +return -EINVAL ; + } +case COUNTER_COMP_SYNAPSE_ACTION : + ret = comp->action_read (counter, parent, comp->priv, + &value_u32); + *value = value_u32; +return ret; +default : +return -EINVAL ; + } +} + +/** +* counter_push_event - queue event for userspace reading +* @counter: pointer to Counter structure
drivers/counter/counter-chrdev.c line 457
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+* @event: triggered event +* @channel: event channel +* +* Note: If no one is watching for the respective event, it is silently +* discarded. +*/ +void counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel) +{ +struct counter_event ev = {};
It looks like we set all of the fields so we shouldn't need to initialize here.
drivers/counter/counter-chrdev.c line 467
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+unsigned int copied =0 ; +unsigned long flags; +struct counter_event_node *event_node; +struct counter_comp_node *comp_node; + + ev.timestamp =ktime_get_ns (); + ev.watch.event = event; + ev.watch.channel = channel; + +/* Could be in an interrupt context, so use a raw spin lock */
this is not a raw spin lock
Quoted 12 lines
drivers/counter/counter-chrdev.c new file +12
-0+spin_lock_irqsave (&counter->events_list_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->events_list, l) +if (event_node->event == event && + event_node->channel == channel) +break ; + +/* If event is not in the list */ +if (&event_node->l == &counter->events_list) + goto exit_early; +
drivers/counter/counter-chrdev.c line 489
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+/* Read and queue relevant comp for userspace */ +list_for_each_entry (comp_node, &event_node->comp_list, l) { + ev.watch.component = comp_node->component; + ev.status = -counter_get_data (counter, comp_node, &ev.value); + + copied +=kfifo_in (&counter->events, &ev,1 ); + } + +if (copied) +wake_up_poll (&counter->events_wait,EPOLLIN );
wake_up_poll() can probably be called after the lock is released.
Quoted 40 lines
drivers/counter/counter-chrdev.c new file +5
-0+ +exit_early: +spin_unlock_irqrestore (&counter->events_list_lock, flags); +} +EXPORT_SYMBOL_GPL (counter_push_event);include/uapi/linux/counter.h +18
-0@@ -6,6 +6,19 @@#ifndef _UAPI_COUNTER_H_#define _UAPI_COUNTER_H_ +#include <linux/ioctl.h> +#include <linux/types.h> + +/* Component type definitions */ +enum counter_component_type { +COUNTER_COMPONENT_NONE , +COUNTER_COMPONENT_SIGNAL , +COUNTER_COMPONENT_COUNT , +COUNTER_COMPONENT_FUNCTION , +COUNTER_COMPONENT_SYNAPSE_ACTION , +COUNTER_COMPONENT_EXTENSION , +}; +/* Component scope definitions */ enum counter_scope { COUNTER_SCOPE_DEVICE,@@ -13,6 +26,70 @@ enum counter_scope { COUNTER_SCOPE_COUNT, }; +/** +* struct counter_component - Counter component identification +* @type: component type (one of enum counter_component_type) +* @scope: component scope (one of enum counter_scope) +* @parent: parent ID (matching the ID suffix of the respective parent sysfs
include/uapi/linux/counter.h enum counter_scope { · line 43
Quoted 10 lines
include/uapi/linux/counter.h +10
-0+* path as described by the ABI documentation file +* Documentation/ABI/testing/sysfs-bus-counter; e.g. if the component +* attribute path is /sys/bus/counter/devices/counter4/count2/count, +* the parent is count2 and thus parent ID is 2) +* @id: component ID (matching the ID provided by the respective *_component_id +* sysfs attribute of the desired component; for example, if the component +* attribute path is /sys/bus/counter/devices/counter4/count2/ceiling, the +* respective /sys/bus/counter/devices/counter4/count2/ceiling_component_id +* attribute will provide the necessary component ID) +* )
It could be a bit nicer to have a separate complete example of populating the struct instead of partial examples in the field descripions.
include/uapi/linux/counter.h enum counter_scope { · line 48
Quoted 5 lines
include/uapi/linux/counter.h +5
-0+*/ +struct counter_component { +__u8 type; +__u8 scope; +__u8 parent;
Would it make more sense to call this parent_id instead?
include/uapi/linux/counter.h enum counter_scope { · line 52
Quoted 4 lines
include/uapi/linux/counter.h +4
-0+__u8 id; +}; + +/* Event type definitions */
Should we document the descriptions of each member here?
include/uapi/linux/counter.h enum counter_scope { · line 56
Quoted 4 lines
include/uapi/linux/counter.h +4
-0+enum counter_event_type { +COUNTER_EVENT_OVERFLOW , +COUNTER_EVENT_UNDERFLOW , +COUNTER_EVENT_OVERFLOW_UNDERFLOW ,
Is this overflow _and_ underflow or overflow _or_ underflow or something else?
include/uapi/linux/counter.h enum counter_scope { · line 63
Quoted 7 lines
include/uapi/linux/counter.h +7
-0+COUNTER_EVENT_THRESHOLD , + COUNTER_EVENT_INDEX, +}; + +/** +* struct counter_watch - Counter component watch configuration +* @component: component to watch when event triggers
Maybe better?
component that will trigger this event
include/uapi/linux/counter.h enum counter_scope { · line 65
Quoted 2 lines
include/uapi/linux/counter.h +2
-0+* @event: event that triggers (one of enum counter_event_type) +* @channel: event channel (typically 0 unless the device supports concurrent
Should this say "component" instead of "device"?
Quoted 15 lines
include/uapi/linux/counter.h +15
-0+* events of the same type) +*/ +struct counter_watch { +struct counter_component component; +__u8 event; +__u8 channel; +}; + +/* ioctl commands */ +#define COUNTER_ADD_WATCH_IOCTL _IOW(0x3E, 0x00, struct counter_watch) +#define COUNTER_ENABLE_EVENTS_IOCTL _IO(0x3E, 0x01) +#define COUNTER_DISABLE_EVENTS_IOCTL _IO(0x3E, 0x02) + +/** +* struct counter_event - Counter event data
include/uapi/linux/counter.h enum counter_scope { · line 90
Quoted 10 lines
include/uapi/linux/counter.h +10
-0+* @timestamp: best estimate of time of event occurrence, in nanoseconds +* @value: component value +* @watch: component watch configuration +* @status: return status (system error number) +*/ +struct counter_event { +__aligned_u64 timestamp; +__aligned_u64 value; +struct counter_watch watch; +__u8 status;
Not sure if this is a _good_ idea, but if status is a signed int, then it could have negative error codes and positive values could be used for event-specific non-error status. I can't think of any practical use though. And forcing it to only be errno codes (0 - 255) probably encourages better drivers anyway.
Quoted 6 lines
include/uapi/linux/counter.h +2
-0+}; +/* Count direction values */ enum counter_count_direction {COUNTER_COUNT_DIRECTION_FORWARD ,
Jonathan Cameron
maintainerRe: [PATCH v12 10/17] counter: Add character device interface
In reply to William Breathitt GrayQuoted 73 lines · On Mon, 5 Jul 2021 17:18:58 +0900 William Breathitt Gray…
This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Cc Oleksij Rempel <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 494 +++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 44 ++- include/linux/counter.h | 45 +++ include/uapi/linux/counter.h | 77 +++++ 6 files changed, 670 insertions(+), 6 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.hdrivers/counter/Makefile +1
-1@@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.odrivers/counter/counter-chrdev.c new file +32
-0@@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Generic Counter character device interface +* Copyright (C) 2020 William Breathitt Gray +*/ + +#include <linux/cdev.h> +#include <linux/counter.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/kfifo.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/nospec.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timekeeping.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/uaccess.h> + +#include "counter-chrdev.h" + +struct counter_comp_node { +struct list_head l; +struct counter_component component; +struct counter_comp comp; +void *parent; +};
drivers/counter/counter-chrdev.c line 42
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ +static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ;
There is a lot of discussion going on buried in a rust on linux thread around the use of devm when chardevs are involved. I'd kind of forgotten that Lars-Peter Clausen did a lot of work to make IIO safe to these races some time ago. One of those elements was to make we dropped out quickly from read functions if we were on the way 'down'. Could you make sure to run some tests to ensure we are safe with driver unbinds when the cdev is still open? Another part of that was to ensure a blocking read unblocks when the device goes away (with an error of course!) Some of this stuff isn't 'necessary' for correctness, but it is desirable for device removal to occur in finite time.
Note I want to take another look at the IIO code around this as well just in case we missed anything that has come up in that discussion. I think we are fine but maybe can move to more 'standard' code patterns if those get formalised.
Anyhow, it's fiddly stuff, so make sure to test those cases.
Jonathan
Quoted 273 lines
drivers/counter/counter-chrdev.c new file +273
-0+ +do { +if (kfifo_is_empty (&counter->events)) { +if (filp->f_flags &O_NONBLOCK ) +return -EAGAIN ; + + err =wait_event_interruptible (counter->events_wait, + !kfifo_is_empty (&counter->events)); +if (err <0 ) +return err; + } + +if (mutex_lock_interruptible (&counter->events_lock)) +return -ERESTARTSYS ; + err =kfifo_to_user (&counter->events, buf, len, &copied); +mutex_unlock (&counter->events_lock); +if (err <0 ) +return err; + }while (!copied); + +return copied; +} + +static __poll_t counter_chrdev_poll (struct file *filp, +struct poll_table_struct *pollt) +{ +struct counter_device *const counter = filp->private_data; +__poll_t events =0 ; + +poll_wait (filp, &counter->events_wait, pollt); + +if (!kfifo_is_empty (&counter->events)) + events =EPOLLIN |EPOLLRDNORM ; + +return events; +} + +static void counter_events_list_free (struct list_head *const events_list) +{ +struct counter_event_node *p, *n; +struct counter_comp_node *q, *o; + +list_for_each_entry_safe (p, n, events_list, l) { +/* Free associated component nodes */ +list_for_each_entry_safe (q, o, &p->comp_list, l) { +list_del (&q->l); +kfree (q); + } + +/* Free event node */ +list_del (&p->l); +kfree (p); + } +} + +static int counter_set_event_node (struct counter_device *const counter, +struct counter_watch *const watch, +const struct counter_comp_node *const cfg) +{ +unsigned long flags; +struct counter_event_node *event_node; +int err =0 ; +struct counter_comp_node *comp_node; + +spin_lock_irqsave (&counter->events_list_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->next_events_list, l) +if (event_node->event == watch->event && + event_node->channel == watch->channel) +break ; + +/* If event is not already in the list */ +if (&event_node->l == &counter->next_events_list) { +/* Allocate new event node */ + event_node =kmalloc (sizeof (*event_node),GFP_ATOMIC ); +if (!event_node) { + err = -ENOMEM ; + goto exit_early; + } + +/* Configure event node and add to the list */ + event_node->event = watch->event; + event_node->channel = watch->channel; +INIT_LIST_HEAD (&event_node->comp_list); +list_add (&event_node->l, &counter->next_events_list); + } + +/* Check if component watch has already been set before */ +list_for_each_entry (comp_node, &event_node->comp_list, l) +if (comp_node->parent == cfg->parent && + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) { + err = -EINVAL ; + goto exit_early; + } + +/* Allocate component node */ + comp_node =kmalloc (sizeof (*comp_node),GFP_ATOMIC ); +if (!comp_node) { +/* Free event node if no one else is watching */ +if (list_empty (&event_node->comp_list)) { +list_del (&event_node->l); +kfree (event_node); + } + err = -ENOMEM ; + goto exit_early; + } + *comp_node = *cfg; + +/* Add component node to event node */ +list_add_tail (&comp_node->l, &event_node->comp_list); + +exit_early: +spin_unlock_irqrestore (&counter->events_list_lock, flags); + +return err; +} + +static int counter_disable_events (struct counter_device *const counter) +{ +unsigned long flags; +int err =0 ; + +spin_lock_irqsave (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->events_list); + +if (counter->ops->events_configure) + err = counter->ops->events_configure (counter); + +spin_unlock_irqrestore (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->next_events_list); + +return err; +} + +static int counter_add_watch (struct counter_device *const counter, +const unsigned long arg) +{ +void __user *const uwatch = (void __user *)arg; +struct counter_watch watch; +struct counter_comp_node comp_node = {}; +size_t parent, id; +struct counter_comp *ext; +size_t num_ext; +int err; + +if (copy_from_user (&watch, uwatch,sizeof (watch))) +return -EFAULT ; + +if (watch.component.type ==COUNTER_COMPONENT_NONE ) + goto no_component; + + parent = watch.component.parent; + +/* Configure parent component info for comp node */ +switch (watch.component.scope) { +case COUNTER_SCOPE_DEVICE : + ext = counter->ext; + num_ext = counter->num_ext; +break ; +case COUNTER_SCOPE_SIGNAL : +if (parent >= counter->num_signals) +return -EINVAL ; + parent =array_index_nospec (parent, counter->num_signals); + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; +break ; +case COUNTER_SCOPE_COUNT : +if (parent >= counter->num_counts) +return -EINVAL ; + parent =array_index_nospec (parent, counter->num_counts); + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; +break ; +default : +return -EINVAL ; + } + + id = watch.component.id; + +/* Configure component info for comp node */ +switch (watch.component.type) { +case COUNTER_COMPONENT_SIGNAL : +if (watch.component.scope !=COUNTER_SCOPE_SIGNAL ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SIGNAL_LEVEL ; + comp_node.comp.signal_u32_read = counter->ops->signal_read; +break ; +case COUNTER_COMPONENT_COUNT : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_U64 ; + comp_node.comp.count_u64_read = counter->ops->count_read; +break ; +case COUNTER_COMPONENT_FUNCTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_FUNCTION ; + comp_node.comp.count_u32_read = counter->ops->function_read; +break ; +case COUNTER_COMPONENT_SYNAPSE_ACTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; +if (id >= counter->counts[parent].num_synapses) +return -EINVAL ; + id =array_index_nospec (id, counter->counts[parent].num_synapses); + + comp_node.comp.type =COUNTER_COMP_SYNAPSE_ACTION ; + comp_node.comp.action_read = counter->ops->action_read; + comp_node.comp.priv = counter->counts[parent].synapses + id; +break ; +case COUNTER_COMPONENT_EXTENSION : +if (id >= num_ext) +return -EINVAL ; + id =array_index_nospec (id, num_ext); + + comp_node.comp = ext[id]; +break ; +default : +return -EINVAL ; + } +/* Check if any read callback is set; this is part of a union */ +if (!comp_node.comp.count_u8_read) +return -EOPNOTSUPP ; + +no_component: +if (counter->ops->watch_validate) { + err = counter->ops->watch_validate (counter, &watch); +if (err <0 ) +return err; + } + + comp_node.component = watch.component; + +return counter_set_event_node (counter, &watch, &comp_node); +} + +static long counter_chrdev_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; +int err =0 ; + +switch (cmd) { +case COUNTER_ADD_WATCH_IOCTL : +return counter_add_watch (counter, arg); +case COUNTER_ENABLE_EVENTS_IOCTL : +spin_lock_irqsave (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->events_list); +list_replace_init (&counter->next_events_list, + &counter->events_list); + +if (counter->ops->events_configure) + err = counter->ops->events_configure (counter); + +spin_unlock_irqrestore (&counter->events_list_lock, flags); +return err; +case COUNTER_DISABLE_EVENTS_IOCTL : +return counter_disable_events (counter); +default :
drivers/counter/counter-chrdev.c line 325
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+return -ENOIOCTLCMD ; + } +} + +static int counter_chrdev_open (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter =container_of (inode->i_cdev, +typeof (*counter), + chrdev); +
What stops multiple simultaneous openings? I'm going to assume this isn't safe to those, or at least that crazy things could happen if you had it open twice at the same time.
Quoted 50 lines
drivers/counter/counter-chrdev.c new file +50
-0+get_device (&counter->dev); + filp->private_data = counter; + +return nonseekable_open (inode, filp); +} + +static int counter_chrdev_release (struct inode *inode,struct file *filp) +{ +struct counter_device *const counter = filp->private_data; +int err; + + err =counter_disable_events (counter); +if (err <0 ) +return err; + +put_device (&counter->dev); + +return 0 ; +} + +static const struct file_operations counter_fops = { + .llseek = no_llseek, + .read = counter_chrdev_read, + .poll = counter_chrdev_poll, + .unlocked_ioctl = counter_chrdev_ioctl, + .open = counter_chrdev_open, + .release = counter_chrdev_release, +}; + +int counter_chrdev_add (struct counter_device *const counter) +{ +/* Initialize Counter events lists */ +INIT_LIST_HEAD (&counter->events_list); +INIT_LIST_HEAD (&counter->next_events_list); +spin_lock_init (&counter->events_list_lock); +init_waitqueue_head (&counter->events_wait); +mutex_init (&counter->events_lock); + +/* Initialize character device */ +cdev_init (&counter->chrdev, &counter_fops); + +/* Allocate Counter events queue */ +return kfifo_alloc (&counter->events,64 ,GFP_ATOMIC ); +} + +void counter_chrdev_remove (struct counter_device *const counter) +{ +kfifo_free (&counter->events); +} +
...
Quoted 328 lines
drivers/counter/counter-chrdev.h new file +14
-0@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Counter character device interface +* Copyright (C) 2020 William Breathitt Gray +*/ +#ifndef _COUNTER_CHRDEV_H_ +#define _COUNTER_CHRDEV_H_ + +#include <linux/counter.h> + +int counter_chrdev_add (struct counter_device *const counter); +void counter_chrdev_remove (struct counter_device *const counter); + +#endif /* _COUNTER_CHRDEV_H_ */ drivers/counter/counter-core.c +39
-5@@ -3,14 +3,20 @@* Generic Counter interface * Copyright (C) 2020 William Breathitt Gray */ +#include <linux/cdev.h> #include <linux/counter.h> #include <linux/device.h> +#include <linux/device/bus.h> #include <linux/export.h> +#include <linux/fs.h> #include <linux/gfp.h> #include <linux/idr.h> #include <linux/init.h> +#include <linux/kdev_t.h> #include <linux/module.h> +#include <linux/types.h> +#include "counter-chrdev.h" #include "counter-sysfs.h" /* Provides a unique ID for each counter device */ @@ -34,6 +40,8 @@ static struct bus_type counter_bus_type = { .dev_name = "counter", }; +static dev_t counter_devt; +/** * counter_register - register Counter to the system * @counter: pointer to Counter to register @@ -60,6 +68,7 @@ int counter_register(struct counter_device *const counter) dev->id = id; dev->type = &counter_device_type; dev->bus = &counter_bus_type; + dev->devt =MKDEV (MAJOR (counter_devt), id);if (counter->parent) { dev->parent = counter->parent; dev->of_node = counter->parent->of_node;@@ -67,18 +76,25 @@ int counter_register(struct counter_device *const counter)device_initialize (dev);dev_set_drvdata (dev, counter); +/* Add Counter character device */ + err =counter_chrdev_add (counter); +if (err <0 ) + goto err_free_id; +/* Add Counter sysfs attributes */ err =counter_sysfs_add (counter);if (err <0 ) - goto err_free_id; + goto err_remove_chrdev;/* Add device to system */ - err =device_add (dev); + err =cdev_device_add (&counter->chrdev, dev);if (err <0 ) - goto err_free_id; + goto err_remove_chrdev;return 0 ; +err_remove_chrdev: +counter_chrdev_remove (counter); err_free_id:put_device (dev);return err;@@ -96,7 +112,8 @@ void counter_unregister(struct counter_device *const counter)if (!counter)return ; -device_unregister (&counter->dev); +cdev_device_del (&counter->chrdev, &counter->dev); +put_device (&counter->dev); }EXPORT_SYMBOL_GPL (counter_unregister);@@ -130,13 +147,30 @@ int devm_counter_register(struct device *dev, }EXPORT_SYMBOL_GPL (devm_counter_register); +#define COUNTER_DEV_MAX 256 +static int __init counter_init (void ) { -return bus_register (&counter_bus_type); +int err; + + err =bus_register (&counter_bus_type); +if (err <0 ) +return err; + + err =alloc_chrdev_region (&counter_devt,0 ,COUNTER_DEV_MAX ,"counter" ); +if (err <0 ) + goto err_unregister_bus; + +return 0 ; + +err_unregister_bus: +bus_unregister (&counter_bus_type); +return err; } static void __exit counter_exit(void) { +unregister_chrdev_region (counter_devt,COUNTER_DEV_MAX );bus_unregister (&counter_bus_type); }include/linux/counter.h +45
-0@@ -6,9 +6,14 @@#ifndef _COUNTER_H_#define _COUNTER_H_ +#include <linux/cdev.h> #include <linux/device.h> #include <linux/kernel.h> +#include <linux/kfifo.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> #include <linux/types.h> +#include <linux/wait.h> #include <uapi/linux/counter.h> struct counter_device ;@@ -199,6 +204,20 @@ struct counter_count {size_t num_ext; }; +/** +* struct counter_event_node - Counter Event node +* @l: list of current watching Counter events +* @event: event that triggers +* @channel: event channel +* @comp_list: list of components to watch when event triggers +*/ +struct counter_event_node { +struct list_head l; +u8 event; +u8 channel; +struct list_head comp_list; +}; +/** * struct counter_ops - Callbacks from driver * @signal_read: read callback for Signals. The read level of the @@ -221,6 +240,13 @@ struct counter_count {* @action_write: write callback for Synapse action modes. The action mode * to write for the respective Synapse is passed in via the * action parameter. +* @events_configure: write callback to configure events. The list of struct +* counter_event_node may be accessed via the events_list +* member of the counter parameter. +* @watch_validate: callback to validate a watch. The Counter component +* watch configuration is passed in via the watch +* parameter. A return value of 0 indicates a valid Counter +* component watch configuration. */ struct counter_ops { int (*signal_read)(struct counter_device *counter,@@ -244,6 +270,9 @@ struct counter_ops {struct counter_count *count, struct counter_synapse *synapse, enum counter_synapse_action action); +int (*events_configure)(struct counter_device *counter); +int (*watch_validate)(struct counter_device *counter, +const struct counter_watch *watch); };/** @@ -259,6 +288,13 @@ struct counter_ops {* @num_ext: number of Counter device extensions specified in @ext * @priv: optional private data supplied by driver * @dev: internal device structure +* @chrdev: internal character device structure +* @events_list: list of current watching Counter events +* @events_list_lock: lock to protect Counter events list operations +* @next_events_list: list of next watching Counter events +* @events: queue of detected Counter events +* @events_wait: wait queue to allow blocking reads of Counter events +* @events_lock: lock to protect Counter events queue read operations */ struct counter_device {const char *name;@@ -277,12 +313,21 @@ struct counter_device {void *priv;struct device dev; +struct cdev chrdev; +struct list_head events_list; +spinlock_t events_list_lock; +struct list_head next_events_list; + DECLARE_KFIFO_PTR(events, struct counter_event); +wait_queue_head_t events_wait; +struct mutex events_lock; };int counter_register (struct counter_device *const counter);void counter_unregister (struct counter_device *const counter);int devm_counter_register (struct device *dev,struct counter_device *const counter); +void counter_push_event (struct counter_device *const counter,const u8 event, +const u8 channel);#define COUNTER_COMP_DEVICE_U8 (_name, _read, _write) \ { \include/uapi/linux/counter.h +77
-0@@ -6,6 +6,19 @@#ifndef _UAPI_COUNTER_H_#define _UAPI_COUNTER_H_ +#include <linux/ioctl.h> +#include <linux/types.h> + +/* Component type definitions */ +enum counter_component_type { +COUNTER_COMPONENT_NONE , +COUNTER_COMPONENT_SIGNAL , +COUNTER_COMPONENT_COUNT , +COUNTER_COMPONENT_FUNCTION , +COUNTER_COMPONENT_SYNAPSE_ACTION , +COUNTER_COMPONENT_EXTENSION , +}; +/* Component scope definitions */ enum counter_scope { COUNTER_SCOPE_DEVICE,@@ -13,6 +26,70 @@ enum counter_scope { COUNTER_SCOPE_COUNT, }; +/** +* struct counter_component - Counter component identification +* @type: component type (one of enum counter_component_type) +* @scope: component scope (one of enum counter_scope) +* @parent: parent ID (matching the ID suffix of the respective parent sysfs +* path as described by the ABI documentation file +* Documentation/ABI/testing/sysfs-bus-counter; e.g. if the component +* attribute path is /sys/bus/counter/devices/counter4/count2/count, +* the parent is count2 and thus parent ID is 2) +* @id: component ID (matching the ID provided by the respective *_component_id +* sysfs attribute of the desired component; for example, if the component +* attribute path is /sys/bus/counter/devices/counter4/count2/ceiling, the +* respective /sys/bus/counter/devices/counter4/count2/ceiling_component_id +* attribute will provide the necessary component ID) +* ) +*/ +struct counter_component { +__u8 type; +__u8 scope; +__u8 parent; +__u8 id; +}; + +/* Event type definitions */ +enum counter_event_type { +COUNTER_EVENT_OVERFLOW , +COUNTER_EVENT_UNDERFLOW , +COUNTER_EVENT_OVERFLOW_UNDERFLOW , +COUNTER_EVENT_THRESHOLD , +COUNTER_EVENT_INDEX , +}; + +/** +* struct counter_watch - Counter component watch configuration +* @component: component to watch when event triggers +* @event: event that triggers (one of enum counter_event_type) +* @channel: event channel (typically 0 unless the device supports concurrent +* events of the same type) +*/ +struct counter_watch { +struct counter_component component; +__u8 event; +__u8 channel; +}; + +/* ioctl commands */ +#define COUNTER_ADD_WATCH_IOCTL _IOW(0x3E, 0x00, struct counter_watch) +#define COUNTER_ENABLE_EVENTS_IOCTL _IO(0x3E, 0x01) +#define COUNTER_DISABLE_EVENTS_IOCTL _IO(0x3E, 0x02) + +/** +* struct counter_event - Counter event data +* @timestamp: best estimate of time of event occurrence, in nanoseconds +* @value: component value +* @watch: component watch configuration +* @status: return status (system error number) +*/ +struct counter_event { +__aligned_u64 timestamp; +__aligned_u64 value; +struct counter_watch watch; +__u8 status; +}; +/* Count direction values */ enum counter_count_direction {COUNTER_COUNT_DIRECTION_FORWARD ,
Re: [PATCH v12 10/17] counter: Add character device interface
Quoted 100 lines · Jonathan Cameron
Quoted 73 lines · On Mon, 5 Jul 2021 17:18:58 +0900 William Breathitt Gray…
This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.
Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Cc Oleksij Rempel <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 494 +++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 44 ++- include/linux/counter.h | 45 +++ include/uapi/linux/counter.h | 77 +++++ 6 files changed, 670 insertions(+), 6 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.hdrivers/counter/Makefile +1
-1@@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.odrivers/counter/counter-chrdev.c new file +32
-0@@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Generic Counter character device interface +* Copyright (C) 2020 William Breathitt Gray +*/ + +#include <linux/cdev.h> +#include <linux/counter.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/kfifo.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/nospec.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timekeeping.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/uaccess.h> + +#include "counter-chrdev.h" + +struct counter_comp_node { +struct list_head l; +struct counter_component component; +struct counter_comp comp; +void *parent; +};drivers/counter/counter-chrdev.c line 42
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10
-0+ +static ssize_t counter_chrdev_read (struct file *filp,char __user *buf, +size_t len,loff_t *f_ps) +{ +struct counter_device *const counter = filp->private_data; +int err; +unsigned int copied; + +if (len <sizeof (struct counter_event )) +return -EINVAL ;There is a lot of discussion going on buried in a rust on linux thread around the use of devm when chardevs are involved. I'd kind of forgotten that Lars-Peter Clausen did a lot of work to make IIO safe to these races some time ago. One of those elements was to make we dropped out quickly from read functions if we were on the way 'down'. Could you make sure to run some tests to ensure we are safe with driver unbinds when the cdev is still open? Another part of that was to ensure a blocking read unblocks when the device goes away (with an error of course!) Some of this stuff isn't 'necessary' for correctness, but it is desirable for device removal to occur in finite time.
Quoted 8 lines
Note I want to take another look at the IIO code around this as well just in case we missed anything that has come up in that discussion. I think we are fine but maybe can move to more 'standard' code patterns if those get formalised.
Anyhow, it's fiddly stuff, so make sure to test those cases.
Jonathan
Thank you for the heads-up. It'll be a shame if all the simplification we got by using devm_* for counter-sysfs.c has to be undone, but I'll investigate this first and see if we're all right as we are now; I also suspect we are fine given how many times we've already tested these patchset revisions, but I'll run some deliberate tests for these particular cases just to be sure.
Quoted 279 lines
Quoted 279 lines
drivers/counter/counter-chrdev.c new file +279
-0+ +do { +if (kfifo_is_empty (&counter->events)) { +if (filp->f_flags &O_NONBLOCK ) +return -EAGAIN ; + + err =wait_event_interruptible (counter->events_wait, + !kfifo_is_empty (&counter->events)); +if (err <0 ) +return err; + } + +if (mutex_lock_interruptible (&counter->events_lock)) +return -ERESTARTSYS ; + err =kfifo_to_user (&counter->events, buf, len, &copied); +mutex_unlock (&counter->events_lock); +if (err <0 ) +return err; + }while (!copied); + +return copied; +} + +static __poll_t counter_chrdev_poll (struct file *filp, +struct poll_table_struct *pollt) +{ +struct counter_device *const counter = filp->private_data; +__poll_t events =0 ; + +poll_wait (filp, &counter->events_wait, pollt); + +if (!kfifo_is_empty (&counter->events)) + events =EPOLLIN |EPOLLRDNORM ; + +return events; +} + +static void counter_events_list_free (struct list_head *const events_list) +{ +struct counter_event_node *p, *n; +struct counter_comp_node *q, *o; + +list_for_each_entry_safe (p, n, events_list, l) { +/* Free associated component nodes */ +list_for_each_entry_safe (q, o, &p->comp_list, l) { +list_del (&q->l); +kfree (q); + } + +/* Free event node */ +list_del (&p->l); +kfree (p); + } +} + +static int counter_set_event_node (struct counter_device *const counter, +struct counter_watch *const watch, +const struct counter_comp_node *const cfg) +{ +unsigned long flags; +struct counter_event_node *event_node; +int err =0 ; +struct counter_comp_node *comp_node; + +spin_lock_irqsave (&counter->events_list_lock, flags); + +/* Search for event in the list */ +list_for_each_entry (event_node, &counter->next_events_list, l) +if (event_node->event == watch->event && + event_node->channel == watch->channel) +break ; + +/* If event is not already in the list */ +if (&event_node->l == &counter->next_events_list) { +/* Allocate new event node */ + event_node =kmalloc (sizeof (*event_node),GFP_ATOMIC ); +if (!event_node) { + err = -ENOMEM ; + goto exit_early; + } + +/* Configure event node and add to the list */ + event_node->event = watch->event; + event_node->channel = watch->channel; +INIT_LIST_HEAD (&event_node->comp_list); +list_add (&event_node->l, &counter->next_events_list); + } + +/* Check if component watch has already been set before */ +list_for_each_entry (comp_node, &event_node->comp_list, l) +if (comp_node->parent == cfg->parent && + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) { + err = -EINVAL ; + goto exit_early; + } + +/* Allocate component node */ + comp_node =kmalloc (sizeof (*comp_node),GFP_ATOMIC ); +if (!comp_node) { +/* Free event node if no one else is watching */ +if (list_empty (&event_node->comp_list)) { +list_del (&event_node->l); +kfree (event_node); + } + err = -ENOMEM ; + goto exit_early; + } + *comp_node = *cfg; + +/* Add component node to event node */ +list_add_tail (&comp_node->l, &event_node->comp_list); + +exit_early: +spin_unlock_irqrestore (&counter->events_list_lock, flags); + +return err; +} + +static int counter_disable_events (struct counter_device *const counter) +{ +unsigned long flags; +int err =0 ; + +spin_lock_irqsave (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->events_list); + +if (counter->ops->events_configure) + err = counter->ops->events_configure (counter); + +spin_unlock_irqrestore (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->next_events_list); + +return err; +} + +static int counter_add_watch (struct counter_device *const counter, +const unsigned long arg) +{ +void __user *const uwatch = (void __user *)arg; +struct counter_watch watch; +struct counter_comp_node comp_node = {}; +size_t parent, id; +struct counter_comp *ext; +size_t num_ext; +int err; + +if (copy_from_user (&watch, uwatch,sizeof (watch))) +return -EFAULT ; + +if (watch.component.type ==COUNTER_COMPONENT_NONE ) + goto no_component; + + parent = watch.component.parent; + +/* Configure parent component info for comp node */ +switch (watch.component.scope) { +case COUNTER_SCOPE_DEVICE : + ext = counter->ext; + num_ext = counter->num_ext; +break ; +case COUNTER_SCOPE_SIGNAL : +if (parent >= counter->num_signals) +return -EINVAL ; + parent =array_index_nospec (parent, counter->num_signals); + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; +break ; +case COUNTER_SCOPE_COUNT : +if (parent >= counter->num_counts) +return -EINVAL ; + parent =array_index_nospec (parent, counter->num_counts); + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; +break ; +default : +return -EINVAL ; + } + + id = watch.component.id; + +/* Configure component info for comp node */ +switch (watch.component.type) { +case COUNTER_COMPONENT_SIGNAL : +if (watch.component.scope !=COUNTER_SCOPE_SIGNAL ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_SIGNAL_LEVEL ; + comp_node.comp.signal_u32_read = counter->ops->signal_read; +break ; +case COUNTER_COMPONENT_COUNT : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_U64 ; + comp_node.comp.count_u64_read = counter->ops->count_read; +break ; +case COUNTER_COMPONENT_FUNCTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; + + comp_node.comp.type =COUNTER_COMP_FUNCTION ; + comp_node.comp.count_u32_read = counter->ops->function_read; +break ; +case COUNTER_COMPONENT_SYNAPSE_ACTION : +if (watch.component.scope !=COUNTER_SCOPE_COUNT ) +return -EINVAL ; +if (id >= counter->counts[parent].num_synapses) +return -EINVAL ; + id =array_index_nospec (id, counter->counts[parent].num_synapses); + + comp_node.comp.type =COUNTER_COMP_SYNAPSE_ACTION ; + comp_node.comp.action_read = counter->ops->action_read; + comp_node.comp.priv = counter->counts[parent].synapses + id; +break ; +case COUNTER_COMPONENT_EXTENSION : +if (id >= num_ext) +return -EINVAL ; + id =array_index_nospec (id, num_ext); + + comp_node.comp = ext[id]; +break ; +default : +return -EINVAL ; + } +/* Check if any read callback is set; this is part of a union */ +if (!comp_node.comp.count_u8_read) +return -EOPNOTSUPP ; + +no_component: +if (counter->ops->watch_validate) { + err = counter->ops->watch_validate (counter, &watch); +if (err <0 ) +return err; + } + + comp_node.component = watch.component; + +return counter_set_event_node (counter, &watch, &comp_node); +} + +static long counter_chrdev_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct counter_device *const counter = filp->private_data; +unsigned long flags; +int err =0 ; + +switch (cmd) { +case COUNTER_ADD_WATCH_IOCTL : +return counter_add_watch (counter, arg); +case COUNTER_ENABLE_EVENTS_IOCTL : +spin_lock_irqsave (&counter->events_list_lock, flags); + +counter_events_list_free (&counter->events_list); +list_replace_init (&counter->next_events_list, + &counter->events_list); + +if (counter->ops->events_configure) + err = counter->ops->events_configure (counter); + +spin_unlock_irqrestore (&counter->events_list_lock, flags); +return err; +case COUNTER_DISABLE_EVENTS_IOCTL : +return counter_disable_events (counter); +default : +return -ENOIOCTLCMD ; + } +} + +static int counter_chrdev_open (struct inode *inode,struct file *filp) +{
Quoted 8 lines
drivers/counter/counter-chrdev.c line 325
Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4
-0+struct counter_device *const counter =container_of (inode->i_cdev, +typeof (*counter), + chrdev); +What stops multiple simultaneous openings? I'm going to assume this isn't safe to those, or at least that crazy things could happen if you had it open twice at the same time.
We're "safe" in the sense that there are locks protecting data access (no risk of dereferencing a NUll pointer, etc.), so I don't expect any crashes if there are multiple opens to the chrdev. However, I coded this with the expectation of a single open, so in such as scenario where we have multiple opens I suspect some data would be not appear, or maybe only appear partially, because the kfifo data is cleared when read.
In patch 15 I deliberately restrict the chrdev to a single open via counter->chrdev_lock in order to support the events_queue_size sysfs attribute. I could move it to this patch, but it may not be necessary when both patches will likely be picked up together and we have no critical issues with opening twice here.
William Breathitt Gray
PATCH v12 11/17 docs: counter: Document character device interface
In reply to William Breathitt GrayThis patch adds high-level documentation about the Counter subsystem character device interface.
--- Documentation/driver-api/generic-counter.rst | 185 ++++++++++++++---- .../userspace-api/ioctl/ioctl-number.rst | 1 + 2 files changed, 145 insertions(+), 41 deletions(-)
Documentation/driver-api/generic-counter.rst +144 -41
@@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus on the core idea of what the data and process represent (e.g. position as interpreted from quadrature encoding data). -Userspace Interface -=================== - -Several sysfs attributes are generated by the Generic Counter interface, -and reside under the /sys/bus/counter/devices/counterX directory, where -counterX refers to the respective counter device. Please see -Documentation/ABI/testing/sysfs-bus-counter for detailed -information on each Generic Counter interface sysfs attribute. - -Through these sysfs attributes, programs and scripts may interact with -the Generic Counter paradigm Counts, Signals, and Synapses of respective -counter devices. - Driver API ==========
@@ -388,16 +375,16 @@ userspace interface components:: / driver callbacks / ------------------- | - +---------------+ - | - V - +--------------------+ - | Counter sysfs | - +--------------------+ - | Translates to the | - | standard Counter | - | sysfs output | - +--------------------+ + +---------------+---------------+ + | | + V V + +--------------------+ +---------------------+ + | Counter sysfs | | Counter chrdev | + +--------------------+ +---------------------+ + | Translates to the | | Translates to the | + | standard Counter | | standard Counter | + | sysfs output | | character device | + +--------------------+ +---------------------+ Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface::
@@ -428,23 +415,30 @@ driver and Counter userspace interface:: / u64 / ---------- | - +---------------+ - | - V - +--------------------+ - | Counter sysfs | - +--------------------+ - | Translates to the | - | standard Counter | - | sysfs output | - |--------------------| - | Type: const char * | - | Value: "42" | - +--------------------+ - | - --------------- - / const char * / - --------------- + +---------------+---------------+ + | | + V V + +--------------------+ +---------------------+ + | Counter sysfs | | Counter chrdev | + +--------------------+ +---------------------+ + | Translates to the | | Translates to the | + | standard Counter | | standard Counter | + | sysfs output | | character device | + |--------------------| |---------------------| + | Type: const char * | | Type: u64 | + | Value: "42" | | Value: 42 | + +--------------------+ +---------------------+ + | | + --------------- ----------------------- + / const char * / / struct counter_event / + --------------- ----------------------- + | | + | V + | +-----------+ + | | read | + | +-----------+ + | \ Count: 42 / + | ----------- | V +--------------------------------------------------+
@@ -453,7 +447,7 @@ driver and Counter userspace interface:: \ Count: "42" / -------------------------------------------------- -There are three primary components involved: +There are four primary components involved: Counter device driver ---------------------
@@ -473,3 +467,112 @@ and vice versa. Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file for a detailed breakdown of the available Generic Counter interface sysfs attributes. + +Counter chrdev +-------------- +Translates counter data to the standard Counter character device; data +is transferred via standard character device read calls, while Counter +events are configured via ioctl calls. + +Sysfs Interface +=============== + +Several sysfs attributes are generated by the Generic Counter interface, +and reside under the ``/sys/bus/counter/devices/counterX`` directory, +where ``X`` is to the respective counter device id. Please see +``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information +on each Generic Counter interface sysfs attribute. + +Through these sysfs attributes, programs and scripts may interact with +the Generic Counter paradigm Counts, Signals, and Synapses of respective +counter devices. + +Counter Character Device +======================== + +Counter character device nodes are created under the ``/dev`` directory +as ``counterX``, where ``X`` is the respective counter device id. +Defines for the standard Counter data types are exposed via the +userspace ``include/uapi/linux/counter.h`` file. + +Counter events +-------------- +Counter device drivers can support Counter events by utilizing the +``counter_push_event`` function:: + + void counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel); + +The event id is specified by the ``event`` parameter; the event channel +id is specified by the ``channel`` parameter. When this function is +called, the Counter data associated with the respective event is +gathered, and a ``struct counter_event`` is generated for each datum and +pushed to userspace. + +Counter events can be configured by users to report various Counter +data of interest. This can be conceptualized as a list of Counter +component read calls to perform. For example:: + + +~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+ + | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX | + +~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+ + | Channel 0 | Channel 0 | + +------------------------+------------------------+ + | * Count 0 | * Signal 0 | + | * Count 1 | * Signal 0 Extension 0 | + | * Signal 3 | * Extension 4 | + | * Count 4 Extension 2 +------------------------+ + | * Signal 5 Extension 0 | Channel 1 | + | +------------------------+ + | | * Signal 4 | + | | * Signal 4 Extension 0 | + | | * Count 7 | + +------------------------+------------------------+ + +When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called +for example, it will go down the list for the ``COUNTER_EVENT_INDEX`` +event channel 1 and execute the read callbacks for Signal 4, Signal 4 +Extension 0, and Count 4 -- the data returned for each is pushed to a +kfifo as a ``struct counter_event``, which userspace can retrieve via a +standard read operation on the respective character device node. + +Userspace +--------- +Userspace applications can configure Counter events via ioctl operations +on the Counter character device node. There following ioctl codes are +supported and provided by the ``linux/counter.h`` userspace header file: + +* COUNTER_ADD_WATCH_IOCTL: + Queues a Counter watch for the specified event. The queued watches + will not be applied until ``COUNTER_ENABLE_EVENTS_IOCTL`` is called. + +* COUNTER_ENABLE_EVENTS_IOCTL: + Enables monitoring the events specified by the Counter watches that + were queued by ``COUNTER_ADD_WATCH_IOCTL``. If events are already + enabled, the new set of watches replaces the old one. Calling this + ioctl also has the effect of clearing the queue of watches added by + ``COUNTER_ADD_WATCH_IOCTL``. + +* COUNTER_DISABLE_EVENTS_IOCTL: + Stops monitoring the previously enabled events. + +To configure events to gather Counter data, users first populate a +``struct counter_watch`` with the relevant event id, event channel id, +and the information for the desired Counter component from which to +read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl +command. + +Note that an event can be watched without gathering Counter data by +setting the ``component.type`` member equal to +``COUNTER_COMPONENT_NONE``. With this configuration the Counter +character device will simply populate the event timestamps for those +respective ``struct counter_event`` elements and ignore the component +value. + +The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter +watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command +may be used to activate these Counter watches. + +Userspace applications can then execute a ``read`` operation (optionally +calling ``poll`` first) on the Counter character device node to retrieve +``struct counter_event`` elements with the desired data.
Documentation/userspace-api/ioctl/ioctl-number.rst +1 -0
@@ -88,6 +88,7 @@ Code Seq# Include File Comments <http://infiniband.sourceforge.net/> 0x20 all drivers/cdrom/cm206.h 0x22 all scsi/sg.h +0x3E 00-0F linux/counter.h <mailto:[email protected]> '!' 00-1F uapi/linux/seccomp.h '#' 00-3F IEEE 1394 Subsystem Block for the entire subsystem
Signature · 1 line
2.32.0
David Lechner
reviewerRe: [PATCH v12 11/17] docs: counter: Document character device interface
Quoted 31 lines · William Breathitt Gray
This patch adds high-level documentation about the Counter subsystem character device interface.
Signed-off-by William Breathitt Gray <[email protected]>--- Documentation/driver-api/generic-counter.rst | 185 ++++++++++++++---- .../userspace-api/ioctl/ioctl-number.rst | 1 + 2 files changed, 145 insertions(+), 41 deletions(-)Documentation/driver-api/generic-counter.rst +16
-0+ +Counter Character Device +======================== + +Counter character device nodes are created under the ``/dev`` directory +as ``counterX``, where ``X`` is the respective counter device id. +Defines for the standard Counter data types are exposed via the +userspace ``include/uapi/linux/counter.h`` file. + +Counter events +-------------- +Counter device drivers can support Counter events by utilizing the +``counter_push_event`` function:: + + void counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel);
Documentation/driver-api/generic-counter.rst and vice versa. · line 514
Quoted 10 lines
Documentation/driver-api/generic-counter.rst +10
-0+ +The event id is specified by the ``event`` parameter; the event channel +id is specified by the ``channel`` parameter. When this function is +called, the Counter data associated with the respective event is +gathered, and a ``struct counter_event`` is generated for each datum and +pushed to userspace. + +Counter events can be configured by users to report various Counter +data of interest. This can be conceptualized as a list of Counter +component read calls to perform. For example::
Won't the :: here make this appear as text instead of an HTML table?
(might need to change ~~~ to --- [top line] and === [middle line])
Quoted 33 lines
Documentation/driver-api/generic-counter.rst +33
-0+ + +~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+ + | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX | + +~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+ + | Channel 0 | Channel 0 | + +------------------------+------------------------+ + | * Count 0 | * Signal 0 | + | * Count 1 | * Signal 0 Extension 0 | + | * Signal 3 | * Extension 4 | + | * Count 4 Extension 2 +------------------------+ + | * Signal 5 Extension 0 | Channel 1 | + | +------------------------+ + | | * Signal 4 | + | | * Signal 4 Extension 0 | + | | * Count 7 | + +------------------------+------------------------+ + +When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called +for example, it will go down the list for the ``COUNTER_EVENT_INDEX`` +event channel 1 and execute the read callbacks for Signal 4, Signal 4 +Extension 0, and Count 4 -- the data returned for each is pushed to a +kfifo as a ``struct counter_event``, which userspace can retrieve via a +standard read operation on the respective character device node. + +Userspace +--------- +Userspace applications can configure Counter events via ioctl operations +on the Counter character device node. There following ioctl codes are +supported and provided by the ``linux/counter.h`` userspace header file: + +* COUNTER_ADD_WATCH_IOCTL: + Queues a Counter watch for the specified event. The queued watches + will not be applied until ``COUNTER_ENABLE_EVENTS_IOCTL`` is called.
Documentation/driver-api/generic-counter.rst and vice versa. · line 557
Quoted 10 lines
Documentation/driver-api/generic-counter.rst +10
-0+ +* COUNTER_ENABLE_EVENTS_IOCTL: + Enables monitoring the events specified by the Counter watches that + were queued by ``COUNTER_ADD_WATCH_IOCTL``. If events are already + enabled, the new set of watches replaces the old one. Calling this + ioctl also has the effect of clearing the queue of watches added by + ``COUNTER_ADD_WATCH_IOCTL``. + +* COUNTER_DISABLE_EVENTS_IOCTL: + Stops monitoring the previously enabled events.
I wouldn't mind seeing more of this documentation in the actual header file and just referenced here with :c:macro:COUNTER_ADD_WATCH_IOCTL
Documentation/driver-api/generic-counter.rst and vice versa. · line 570
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ +To configure events to gather Counter data, users first populate a +``struct counter_watch`` with the relevant event id, event channel id, +and the information for the desired Counter component from which to +read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl +command. + +Note that an event can be watched without gathering Counter data by +setting the ``component.type`` member equal to +``COUNTER_COMPONENT_NONE``. With this configuration the Counter +character device will simply populate the event timestamps for those +respective ``struct counter_event`` elements and ignore the component +value.
To make sure I am understanding this correctly, scope + parent determines this part of the path:
/sys/.../counterX/<scope><parent>/<component>
Or in the case that scope == COUNTER_SCOPE_DEVICE then parent is not applicable:
/sys/.../counterX/<component>
I suggested parent_id instead of parent earlier, but maybe scope_id would be a better name? (Or rename scope to parent_type?)
Quoted 8 lines
Documentation/driver-api/generic-counter.rst +8
-0+ +The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter +watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command +may be used to activate these Counter watches. + +Userspace applications can then execute a ``read`` operation (optionally +calling ``poll`` first) on the Counter character device node to retrieve +``struct counter_event`` elements with the desired data.
Re: [PATCH v12 11/17] docs: counter: Document character device interface
Quoted 45 lines · David Lechner
Quoted 31 lines · William Breathitt Gray
This patch adds high-level documentation about the Counter subsystem character device interface.
Signed-off-by William Breathitt Gray <[email protected]>--- Documentation/driver-api/generic-counter.rst | 185 ++++++++++++++---- .../userspace-api/ioctl/ioctl-number.rst | 1 + 2 files changed, 145 insertions(+), 41 deletions(-)Documentation/driver-api/generic-counter.rst +16
-0+ +Counter Character Device +======================== + +Counter character device nodes are created under the ``/dev`` directory +as ``counterX``, where ``X`` is the respective counter device id. +Defines for the standard Counter data types are exposed via the +userspace ``include/uapi/linux/counter.h`` file. + +Counter events +-------------- +Counter device drivers can support Counter events by utilizing the +``counter_push_event`` function:: + + void counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel);Documentation/driver-api/generic-counter.rst and vice versa. · line 514
Quoted 10 lines
Documentation/driver-api/generic-counter.rst +10
-0+ +The event id is specified by the ``event`` parameter; the event channel +id is specified by the ``channel`` parameter. When this function is +called, the Counter data associated with the respective event is +gathered, and a ``struct counter_event`` is generated for each datum and +pushed to userspace. + +Counter events can be configured by users to report various Counter +data of interest. This can be conceptualized as a list of Counter +component read calls to perform. For example::Won't the :: here make this appear as text instead of an HTML table?
Quoted 1 line
(might need to change ~~~ to --- [top line] and === [middle line])
Ack, I'll change this to an HTML table.
Quoted 38 lines
Quoted 38 lines
Documentation/driver-api/generic-counter.rst +38
-0+ + +~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+ + | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX | + +~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+ + | Channel 0 | Channel 0 | + +------------------------+------------------------+ + | * Count 0 | * Signal 0 | + | * Count 1 | * Signal 0 Extension 0 | + | * Signal 3 | * Extension 4 | + | * Count 4 Extension 2 +------------------------+ + | * Signal 5 Extension 0 | Channel 1 | + | +------------------------+ + | | * Signal 4 | + | | * Signal 4 Extension 0 | + | | * Count 7 | + +------------------------+------------------------+ + +When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called +for example, it will go down the list for the ``COUNTER_EVENT_INDEX`` +event channel 1 and execute the read callbacks for Signal 4, Signal 4 +Extension 0, and Count 4 -- the data returned for each is pushed to a +kfifo as a ``struct counter_event``, which userspace can retrieve via a +standard read operation on the respective character device node. + +Userspace +--------- +Userspace applications can configure Counter events via ioctl operations +on the Counter character device node. There following ioctl codes are +supported and provided by the ``linux/counter.h`` userspace header file: + +* COUNTER_ADD_WATCH_IOCTL: + Queues a Counter watch for the specified event. The queued watches + will not be applied until ``COUNTER_ENABLE_EVENTS_IOCTL`` is called. + +* COUNTER_ENABLE_EVENTS_IOCTL: + Enables monitoring the events specified by the Counter watches that + were queued by ``COUNTER_ADD_WATCH_IOCTL``. If events are already + enabled, the new set of watches replaces the old one. Calling this
Quoted 8 lines
Documentation/driver-api/generic-counter.rst and vice versa. · line 557
Quoted 5 lines
Documentation/driver-api/generic-counter.rst +5
-0+ ioctl also has the effect of clearing the queue of watches added by + ``COUNTER_ADD_WATCH_IOCTL``. + +* COUNTER_DISABLE_EVENTS_IOCTL: + Stops monitoring the previously enabled events.I wouldn't mind seeing more of this documentation in the actual header file and just referenced here with :c:macro:
COUNTER_ADD_WATCH_IOCTL
Ack.
Quoted 17 lines
Documentation/driver-api/generic-counter.rst and vice versa. · line 570
Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13
-0+ +To configure events to gather Counter data, users first populate a +``struct counter_watch`` with the relevant event id, event channel id, +and the information for the desired Counter component from which to +read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl +command. + +Note that an event can be watched without gathering Counter data by +setting the ``component.type`` member equal to +``COUNTER_COMPONENT_NONE``. With this configuration the Counter +character device will simply populate the event timestamps for those +respective ``struct counter_event`` elements and ignore the component +value.To make sure I am understanding this correctly, scope + parent determines this part of the path:
Quoted 6 lines
/sys/.../counterX/<scope><parent>/<component>Or in the case that scope ==
COUNTER_SCOPE_DEVICEthen parent is not applicable:/sys/.../counterX/<component>
Yes, that understanding is correct.
Quoted 2 lines
I suggested
parent_idinstead of parent earlier, but maybescope_idwould be a better name? (Or rename scope toparent_type?)
I can see the benefit of more specific naming, but perhaps the current names are clear enough when they appear in the context of user code. It becomes rather obvious in a snippet of code that component.scope refers to the scope type of a component while component.parent refers to the specific parent of the component; I don't think the more verbose scope_type/parent_type or scope_id/parent_id namings really add much benefit here.
I'm not entirely opposed to renaming this, so if other maintainers also feel this is better renamed then I'll change it. If we do change the naming, then remaing scope to scope_type and parent to parent_id might be the way to go; "scope_id" seems strange to me because I envision scope as a type rather than an identifiable component.
William Breathitt Gray
PATCH v12 12/17 tools/counter: Create Counter tools
In reply to William Breathitt GrayThis creates an example Counter program under tools/counter/* to exemplify the Counter character device interface.
--- MAINTAINERS | 1 + tools/Makefile | 13 ++--- tools/counter/Build | 1 + tools/counter/Makefile | 53 ++++++++++++++++++ tools/counter/counter_example.c | 95 +++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 tools/counter/Build create mode 100644 tools/counter/Makefile create mode 100644 tools/counter/counter_example.c
MAINTAINERS +1 -0
@@ -4688,6 +4688,7 @@ F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h F: include/uapi/linux/counter.h +F: tools/counter/ CP2615 I2C DRIVER M: Bence Csókás <[email protected]>
tools/Makefile +7 -6
@@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' bpf - misc BPF tools' @echo ' cgroup - cgroup tools' + @echo ' counter - Counter tools' @echo ' cpupower - a tool for all things x86 CPU power' @echo ' debugging - tools for debugging' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@@ -65,7 +66,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE +cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE $(call descend,$@) bpf/%: FORCE
@@ -100,7 +101,7 @@ freefall: FORCE kvm_stat: FORCE $(call descend,kvm/$@) -all: acpi cgroup cpupower gpio hv firewire liblockdep \ +all: acpi cgroup counter cpupower gpio hv firewire liblockdep \ perf selftests bootconfig spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi \
@@ -112,7 +113,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: +cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: $(call descend,$(@:_install=),install) liblockdep_install:
@@ -133,7 +134,7 @@ freefall_install: kvm_stat_install: $(call descend,kvm/$(@:_install=),install) -install: acpi_install cgroup_install cpupower_install gpio_install \ +install: acpi_install cgroup_install counter_install cpupower_install gpio_install \ hv_install firewire_install iio_install liblockdep_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \
@@ -147,7 +148,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: +cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean:
@@ -181,7 +182,7 @@ freefall_clean: build_clean: $(call descend,build,clean) -clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ +clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \ perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
tools/counter/Build new file +1 -0
@@ -0,0 +1 @@ +counter_example-y += counter_example.o
tools/counter/Makefile new file +53 -0
53 lines not on this page · raw message
tools/counter/counter_example.c new file +95 -0
95 lines not on this page · raw message
Signature · 1 line
2.32.0
David Lechner
reviewerRe: [PATCH v12 12/17] tools/counter: Create Counter tools
Quoted 11 lines · William Breathitt Gray
This creates an example Counter program under
tools/counter/*to exemplify the Counter character device interface.Cc Pavel Machek <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>---
tools/Makefile help: · line 15
Quoted 4 lines
tools/Makefile +1
-0@@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' bpf - misc BPF tools' @echo ' cgroup - cgroup tools' + @echo ' counter - Counter tools'
nit: other descriptions start with lower case letter, so to be consistent, this should too
Quoted 12 lines
tools/counter/counter_example.c new file +9
-0@@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Counter - example userspace application +* +* The userspace application opens /dev/counter0, configures the +* COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count +* 1 count, and prints out the data as it becomes available on the +* character device node. +* +* Copyright (C) 2021 William Breathitt Gray
tools/counter/counter_example.c line 19
Quoted 10 lines
tools/counter/counter_example.c new file +10
-0+*/ +#include <errno.h> +#include <fcntl.h> +#include <linux/counter.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +struct counter_watch watches[2 ] = {
nit: this can be static
Quoted 22 lines
tools/counter/counter_example.c new file +22
-0+ { +/* Component data: Count 0 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 0, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, + { +/* Component data: Count 1 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 1, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, +}; +
tools/counter/counter_example.c line 51
Quoted 10 lines
tools/counter/counter_example.c new file +10
-0+int main (void ) +{ +int fd; +int ret; +struct counter_event event_data[2 ]; + + fd =open ("/dev/counter0" ,O_RDWR ); +if (fd ==-1 ) { +perror ("Unable to open /dev/counter0" ); +return -errno;
errno is no longer valid after calling perror(). Since this is example code, we can just return 1 instead (exit codes positive number between 0 and 255 so -1 would be 255).
Quoted 31 lines
tools/counter/counter_example.c new file +31
-0+ } + + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches); +if (ret ==-1 ) { +perror ("Error adding watches[0]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches +1 ); +if (ret ==-1 ) { +perror ("Error adding watches[1]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ENABLE_EVENTS_IOCTL ); +if (ret ==-1 ) { +perror ("Error enabling events" ); +return -errno; + } + +for (;;) { + ret =read (fd, event_data,sizeof (event_data)); +if (ret ==-1 ) { +perror ("Failed to read event data" ); +return -errno; + } + +if (ret !=sizeof (event_data)) { +fprintf (stderr,"Failed to read event data\n" ); +return -EIO ; + } + + printf("Timestamp 0: %llu\tCount 0: %llu\n"
tools/counter/counter_example.c line 92
Quoted 10 lines
tools/counter/counter_example.c new file +10
-0+"Error Message 0: %s\n" +"Timestamp 1: %llu\tCount 1: %llu\n" +"Error Message 1: %s\n" , + (unsigned long long )event_data[0 ].timestamp, + (unsigned long long )event_data[0 ].value, +strerror (event_data[0 ].status), + (unsigned long long )event_data[1 ].timestamp, + (unsigned long long )event_data[1 ].value, + strerror(event_data[1].status)); + }
Aren't the Count 0 and Count 1 events independent? Why should we expect to always get both events at the same time in the same order?
Quoted 4 lines
tools/counter/counter_example.c new file +3
-0+ +return 0 ; +}
Re: [PATCH v12 12/17] tools/counter: Create Counter tools
Quoted 9 lines · David Lechner
Quoted 6 lines · William Breathitt Gray
This creates an example Counter program under
tools/counter/*to exemplify the Counter character device interface.Cc Pavel Machek <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>---
Quoted 10 lines
tools/Makefile help: · line 15
Quoted 7 lines
tools/Makefile +1
-0@@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' bpf - misc BPF tools' @echo ' cgroup - cgroup tools' + @echo ' counter - Counter tools'nit: other descriptions start with lower case letter, so to be consistent, this should too
Ack.
Quoted 16 lines
Quoted 16 lines
tools/counter/counter_example.c new file +13
-0@@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Counter - example userspace application +* +* The userspace application opens /dev/counter0, configures the +* COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count +* 1 count, and prints out the data as it becomes available on the +* character device node. +* +* Copyright (C) 2021 William Breathitt Gray +*/ +#include <errno.h> +#include <fcntl.h> +#include <linux/counter.h>
Quoted 8 lines
tools/counter/counter_example.c line 19
Quoted 6 lines
tools/counter/counter_example.c new file +6
-0+#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +struct counter_watch watches[2 ] = {nit: this can be static
Ack.
Quoted 28 lines
Quoted 28 lines
tools/counter/counter_example.c new file +28
-0+ { +/* Component data: Count 0 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 0, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, + { +/* Component data: Count 1 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 1, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, +}; + +int main (void ) +{ +int fd; +int ret; +struct counter_event event_data[2 ]; +
Quoted 8 lines
tools/counter/counter_example.c line 51
Quoted 4 lines
tools/counter/counter_example.c new file +4
-0+ fd =open ("/dev/counter0" ,O_RDWR ); +if (fd ==-1 ) { +perror ("Unable to open /dev/counter0" ); +return -errno;errno is no longer valid after calling
perror(). Since this is example code, we can just return 1 instead (exit codes positive number between 0 and 255 so -1 would be 255).
Ack.
Quoted 36 lines
Quoted 36 lines
tools/counter/counter_example.c new file +36
-0+ } + + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches); +if (ret ==-1 ) { +perror ("Error adding watches[0]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches +1 ); +if (ret ==-1 ) { +perror ("Error adding watches[1]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ENABLE_EVENTS_IOCTL ); +if (ret ==-1 ) { +perror ("Error enabling events" ); +return -errno; + } + +for (;;) { + ret =read (fd, event_data,sizeof (event_data)); +if (ret ==-1 ) { +perror ("Failed to read event data" ); +return -errno; + } + +if (ret !=sizeof (event_data)) { +fprintf (stderr,"Failed to read event data\n" ); +return -EIO ; + } + + printf("Timestamp 0: %llu\tCount 0: %llu\n" + "Error Message 0: %s\n" + "Timestamp 1: %llu\tCount 1: %llu\n" + "Error Message 1: %s\n", + (unsigned long long)event_data[0].timestamp, + (unsigned long long)event_data[0].value,
Quoted 8 lines
tools/counter/counter_example.c line 92
Quoted 5 lines
tools/counter/counter_example.c new file +5
-0+strerror (event_data[0 ].status), + (unsigned long long )event_data[1 ].timestamp, + (unsigned long long )event_data[1 ].value, + strerror(event_data[1].status)); + }Aren't the Count 0 and Count 1 events independent? Why should we expect to always get both events at the same time in the same order?
Watch 0 and Watch 1 are both triggered by the same event: a COUNTER_EVENT_INDEX event on device event channel 0. If we had set channel to 1 for Watch 1, then we would have two independent events, but in this case both Watches have their respective channel set to 0.
To make the sequence of events clearer, here's a timeline:
* The user configures the watch list via COUNTER_ADD_WATCH_IOCTL.
* The watch list consists of Watch 0 and Watch 1. Watch 0 is configured to report the Count 0 count, while Watch 1 is configured to report the Count 1 count. Both watches are configured to trigger on the same event (COUNTER_EVENT_INDEX on device event channel 0).
* The user enables Counter events via COUNTER_ENABLE_EVENTS_IOCTL.
* The user calls read() from userspace and blocks until data is available in the Counter events list kfifo; this corresponds to wait_event_interruptible() in counter_chrdev_read().
* A COUNTER_EVENT_INDEX event occurs on device event channel 0.
* All Watches in the watch list that are waiting for COUNTER_EVENT_INDEX on device event channel 0 will now trigger; both Watch 0 and Watch 1 will trigger, one after the other.
* A read operation is performed for the Count 0 count component; the data is pushed to the Counter event list.
* A read operation is performed for the Count 1 count component; the data is pushed to the Counter event list.
* Counter subsystem notifies that data is available in the Counter events list kfifo; this corresponds to the wake_up_poll() in counter_push_event().
* The userspace read() call returns the Counter event list data.
So in the counter_example.c reference code, we will always get both event data elements returned to the user at the same time (with the exception of errors which break early).
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v12 12/17] tools/counter: Create Counter tools
Quoted 87 lines · William Breathitt Gray
Quoted 30 lines · David Lechner
Quoted 28 lines · William Breathitt Gray
tools/counter/counter_example.c new file +28
-0+ { +/* Component data: Count 0 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 0, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, + { +/* Component data: Count 1 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 1, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, +}; + +int main (void ) +{ +int fd; +int ret; +struct counter_event event_data[2 ]; +Quoted 8 lines
tools/counter/counter_example.c line 51
Quoted 4 lines
tools/counter/counter_example.c new file +4
-0+ fd =open ("/dev/counter0" ,O_RDWR ); +if (fd ==-1 ) { +perror ("Unable to open /dev/counter0" ); +return -errno;errno is no longer valid after calling
perror(). Since this is example code, we can just return 1 instead (exit codes positive number between 0 and 255 so -1 would be 255).Ack.
Quoted 36 lines
Quoted 36 lines
tools/counter/counter_example.c new file +36
-0+ } + + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches); +if (ret ==-1 ) { +perror ("Error adding watches[0]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches +1 ); +if (ret ==-1 ) { +perror ("Error adding watches[1]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ENABLE_EVENTS_IOCTL ); +if (ret ==-1 ) { +perror ("Error enabling events" ); +return -errno; + } + +for (;;) { + ret =read (fd, event_data,sizeof (event_data)); +if (ret ==-1 ) { +perror ("Failed to read event data" ); +return -errno; + } + +if (ret !=sizeof (event_data)) { +fprintf (stderr,"Failed to read event data\n" ); +return -EIO ; + } + + printf("Timestamp 0: %llu\tCount 0: %llu\n" + "Error Message 0: %s\n" + "Timestamp 1: %llu\tCount 1: %llu\n" + "Error Message 1: %s\n", + (unsigned long long)event_data[0].timestamp, + (unsigned long long)event_data[0].value,Quoted 8 lines
tools/counter/counter_example.c line 92
Quoted 5 lines
tools/counter/counter_example.c new file +5
-0+strerror (event_data[0 ].status), + (unsigned long long )event_data[1 ].timestamp, + (unsigned long long )event_data[1 ].value, + strerror(event_data[1].status)); + }Aren't the Count 0 and Count 1 events independent? Why should we expect to always get both events at the same time in the same order?
Quoted 4 lines
Watch 0 and Watch 1 are both triggered by the same event: a
COUNTER_EVENT_INDEXevent on device event channel 0. If we had set channel to 1 for Watch 1, then we would have two independent events, but in this case both Watches have their respective channel set to 0.
The thing that jumped out to me is that they have different parents. But I guess I forgot that the event itself always has a scope of device and that the component just says what value to record and is otherwise independent of the event.
Jonathan Cameron
maintainerRe: [PATCH v12 12/17] tools/counter: Create Counter tools
In reply to William Breathitt GrayQuoted 242 lines · On Mon, 5 Jul 2021 17:19:00 +0900 William Breathitt Gray…
This creates an example Counter program under
tools/counter/*to exemplify the Counter character device interface.Cc Pavel Machek <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- MAINTAINERS | 1 + tools/Makefile | 13 ++--- tools/counter/Build | 1 + tools/counter/Makefile | 53 ++++++++++++++++++ tools/counter/counter_example.c | 95 +++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 tools/counter/Build create mode 100644 tools/counter/Makefile create mode 100644 tools/counter/counter_example.cMAINTAINERS +1
-0@@ -4688,6 +4688,7 @@ F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h F: include/uapi/linux/counter.h +F: tools/counter/ CP2615 I2C DRIVER M: Bence Csókás <[email protected]>tools/Makefile +7
-6@@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' bpf - misc BPF tools' @echo ' cgroup - cgroup tools' + @echo ' counter - Counter tools' @echo ' cpupower - a tool for all things x86 CPU power' @echo ' debugging - tools for debugging' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'@@ -65,7 +66,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE +cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE $(call descend,$@) bpf/%: FORCE@@ -100,7 +101,7 @@ freefall: FORCE kvm_stat: FORCE $(call descend,kvm/$@) -all: acpi cgroup cpupower gpio hv firewire liblockdep \ +all: acpi cgroup counter cpupower gpio hv firewire liblockdep \ perf selftests bootconfig spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi \@@ -112,7 +113,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: +cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: $(call descend,$(@:_install=),install) liblockdep_install:@@ -133,7 +134,7 @@ freefall_install: kvm_stat_install: $(call descend,kvm/$(@:_install=),install) -install: acpi_install cgroup_install cpupower_install gpio_install \ +install: acpi_install cgroup_install counter_install cpupower_install gpio_install \ hv_install firewire_install iio_install liblockdep_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \@@ -147,7 +148,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: +cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean:@@ -181,7 +182,7 @@ freefall_clean: build_clean: $(call descend,build,clean) -clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ +clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \ perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \tools/counter/Build new file +1
-0@@ -0,0 +1 @@ +counter_example-y += counter_example.otools/counter/Makefile new file +53
-0@@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../scripts/Makefile.include + +bindir ?= /usr/bin + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(CURDIR))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +endif + +# Do not use make's built-in rules +# (this improves performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -r + +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include + +ALL_TARGETS := counter_example +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) + +all: $(ALL_PROGRAMS) + +export srctree OUTPUT CC LD CFLAGS +include $(srctree)/tools/build/Makefile.include + +# +# We need the following to be outside of kernel tree +# +$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@ + +prepare: $(OUTPUT)include/linux/counter.h + +COUNTER_EXAMPLE := $(OUTPUT)counter_example.o +$(COUNTER_EXAMPLE): prepare FORCE + $(Q)$(MAKE) $(build)=counter_example +$(OUTPUT)counter_example: $(COUNTER_EXAMPLE) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +clean: + rm -f $(ALL_PROGRAMS) + rm -rf $(OUTPUT)include/linux/counter.h + find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + +install: $(ALL_PROGRAMS) + install -d -m 755 $(DESTDIR)$(bindir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program $(DESTDIR)$(bindir); \ + done + +FORCE: + +.PHONY: all install clean FORCE preparetools/counter/counter_example.c new file +76
-0@@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Counter - example userspace application +* +* The userspace application opens /dev/counter0, configures the +* COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count +* 1 count, and prints out the data as it becomes available on the +* character device node. +* +* Copyright (C) 2021 William Breathitt Gray +*/ +#include <errno.h> +#include <fcntl.h> +#include <linux/counter.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +struct counter_watch watches[2 ] = { + { +/* Component data: Count 0 count */ + .component.type =COUNTER_COMPONENT_COUNT , + .component.scope =COUNTER_SCOPE_COUNT , + .component.parent =0 , +/* Event type: Index */ + .event =COUNTER_EVENT_INDEX , +/* Device event channel 0 */ + .channel =0 , + }, + { +/* Component data: Count 1 count */ + .component.type =COUNTER_COMPONENT_COUNT , + .component.scope =COUNTER_SCOPE_COUNT , + .component.parent =1 , +/* Event type: Index */ + .event =COUNTER_EVENT_INDEX , +/* Device event channel 0 */ + .channel =0 , + }, +}; + +int main (void ) +{ +int fd; +int ret; +struct counter_event event_data[2 ]; + + fd =open ("/dev/counter0" ,O_RDWR ); +if (fd ==-1 ) { +perror ("Unable to open /dev/counter0" ); +return -errno; + } + + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches); +if (ret ==-1 ) { +perror ("Error adding watches[0]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ADD_WATCH_IOCTL , watches +1 ); +if (ret ==-1 ) { +perror ("Error adding watches[1]" ); +return -errno; + } + ret =ioctl (fd,COUNTER_ENABLE_EVENTS_IOCTL ); +if (ret ==-1 ) { +perror ("Error enabling events" ); +return -errno; + } + +for (;;) { + ret =read (fd, event_data,sizeof (event_data)); +if (ret ==-1 ) { +perror ("Failed to read event data" ); +return -errno; + } +
tools/counter/counter_example.c line 86
Quoted 10 lines
tools/counter/counter_example.c new file +10
-0+if (ret !=sizeof (event_data)) { +fprintf (stderr,"Failed to read event data\n" ); +return -EIO ; + } + + printf("Timestamp 0: %llu\tCount 0: %llu\n" + "Error Message 0: %s\n" + "Timestamp 1: %llu\tCount 1: %llu\n" + "Error Message 1: %s\n", + (unsigned long long)event_data[0].timestamp,
I think there should be no need to cast this one. __aligned_u64 is always going to be of type llu anyway.
Quoted 9 lines
tools/counter/counter_example.c new file +9
-0+ (unsigned long long )event_data[0 ].value, +strerror (event_data[0 ].status), + (unsigned long long )event_data[1 ].timestamp, + (unsigned long long )event_data[1 ].value, + strerror(event_data[1].status)); + } + +return 0 ; +}
PATCH v12 13/17 counter: Implement signalZ_action_component_id sysfs attribute
In reply to William Breathitt GrayThe Generic Counter chrdev interface expects users to supply component IDs in order to select Synapses for requests. In order for users to know what component ID belongs to which Synapse this information must be exposed. The signalZ_action_component_id attribute provides a way for users to discover what component ID belongs to the respective Synapse.
--- Documentation/ABI/testing/sysfs-bus-counter | 7 ++++ drivers/counter/counter-sysfs.c | 45 +++++++++++++++++++++ 2 files changed, 52 insertions(+)
Documentation/ABI/testing/sysfs-bus-counter +7 -0
@@ -203,6 +203,13 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +KernelVersion: 5.15 +Contact: [email protected] +Description: + Read-only attribute that indicates the component ID of the + respective Synapse of Count Y for Signal Z. + What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns KernelVersion: 5.14 Contact: [email protected]
drivers/counter/counter-sysfs.c +45 -0
57 lines not on this page · raw message
Signature · 1 line
2.32.0
Re: [PATCH v12 13/17] counter: Implement signalZ_action_component_id sysfs attribute
Quoted 11 lines · William Breathitt Gray
The Generic Counter chrdev interface expects users to supply component IDs in order to select Synapses for requests. In order for users to know what component ID belongs to which Synapse this information must be exposed. The
signalZ_action_component_idattribute provides a way for users to discover what component ID belongs to the respective Synapse.Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>---
I like this better than having to scrape the number from the attribute name.
drivers/counter/counter-sysfs.c static int counter_name_attr_create(struct device *const dev, · line 551
Quoted 2 lines
drivers/counter/counter-sysfs.c +2
-0+/* Allocate Counter attribute */ + counter_attr =devm_kzalloc (dev,sizeof (*counter_attr),GFP_KERNEL );
nit: Comments like this are redundant and can be omitted. All 3 words in the comment are literally (abbreviated) in the next line of code so it doesn't add any new/useful information.
PATCH v12 14/17 counter: Implement *_component_id sysfs attributes
In reply to William Breathitt GrayThe Generic Counter chrdev interface expects users to supply component IDs in order to select extensions for requests. In order for users to know what component ID belongs to which extension this information must be exposed. The *_component_id attribute provides a way for users to discover what component ID belongs to which respective extension.
--- Documentation/ABI/testing/sysfs-bus-counter | 16 ++++++++++- drivers/counter/counter-sysfs.c | 30 ++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter +15 -1
@@ -203,12 +203,26 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id +What: /sys/bus/counter/devices/counterX/countY/floor_component_id +What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id +What: /sys/bus/counter/devices/counterX/countY/direction_component_id +What: /sys/bus/counter/devices/counterX/countY/enable_component_id +What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id +What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id +What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id +What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id KernelVersion: 5.15 Contact: [email protected] Description: Read-only attribute that indicates the component ID of the - respective Synapse of Count Y for Signal Z. + respective extension or Synapse. What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns KernelVersion: 5.14
drivers/counter/counter-sysfs.c +24 -6
@@ -587,6 +587,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter,int err;struct counter_comp comp;size_t i; +struct counter_comp *ext;/* Create main Signal attribute */ comp = counter_signal_comp;
@@ -602,8 +603,13 @@ static int counter_signal_attrs_create(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < signal->num_ext; i++) { - err =counter_attr_create (dev, group, signal->ext + i, scope, - signal); + ext = signal->ext + i; + + err =counter_attr_create (dev, group, ext, scope, signal); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }
@@ -694,6 +700,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,int err;struct counter_comp comp;size_t i; +struct counter_comp *ext;/* Create main Count attribute */ comp = counter_count_comp;
@@ -718,8 +725,13 @@ static int counter_count_attrs_create(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < count->num_ext; i++) { - err =counter_attr_create (dev, group, count->ext + i, scope, - count); + ext = count->ext + i; + + err =counter_attr_create (dev, group, ext, scope, count); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }
@@ -783,6 +795,7 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,struct device *const dev = &counter->dev;int err;size_t i; +struct counter_comp *ext;/* Add Signals sysfs attributes */ err =counter_sysfs_signals_add (counter, group);
@@ -815,8 +828,13 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < counter->num_ext; i++) { - err =counter_attr_create (dev, group, counter->ext + i, scope, -NULL ); + ext = counter->ext + i; + + err =counter_attr_create (dev, group, ext, scope,NULL ); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }
Signature · 1 line
2.32.0
David Lechner
reviewerRe: [PATCH v12 14/17] counter: Implement *_component_id sysfs attributes
Quoted 28 lines · William Breathitt Gray
The Generic Counter chrdev interface expects users to supply component IDs in order to select extensions for requests. In order for users to know what component ID belongs to which extension this information must be exposed. The
*_component_idattribute provides a way for users to discover what component ID belongs to which respective extension.Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- Documentation/ABI/testing/sysfs-bus-counter | 16 ++++++++++- drivers/counter/counter-sysfs.c | 30 ++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-)Documentation/ABI/testing/sysfs-bus-counter +5
-0@@ -203,12 +203,26 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id +What: /sys/bus/counter/devices/counterX/countY/floor_component_id +What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id +What: /sys/bus/counter/devices/counterX/countY/direction_component_id +What: /sys/bus/counter/devices/counterX/countY/enable_component_id
Documentation/ABI/testing/sysfs-bus-counter Description: · line 220
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-counter +9
-0+What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id +What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id +What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id +What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id
Could we just write a single line?
What: /sys/bus/counter/devices/counterX/signalY/<component>_component_id
Quoted 46 lines
Documentation/ABI/testing/sysfs-bus-counter +1
-1KernelVersion: 5.15 Contact: [email protected] Description: Read-only attribute that indicates the component ID of the - respective Synapse of Count Y for Signal Z. + respective extension or Synapse. What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns KernelVersion: 5.14drivers/counter/counter-sysfs.c +9
-2@@ -587,6 +587,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter,int err;struct counter_comp comp;size_t i; +struct counter_comp *ext;/* Create main Signal attribute */ comp = counter_signal_comp;@@ -602,8 +603,13 @@ static int counter_signal_attrs_create(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < signal->num_ext; i++) { - err =counter_attr_create (dev, group, signal->ext + i, scope, - signal); + ext = signal->ext + i; + + err =counter_attr_create (dev, group, ext, scope, signal); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }@@ -694,6 +700,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,int err;struct counter_comp comp;size_t i; +struct counter_comp *ext;/* Create main Count attribute */ comp = counter_count_comp;
drivers/counter/counter-sysfs.c static int counter_count_attrs_create(struct counter_device *const counter, · line 728
Quoted 6 lines
drivers/counter/counter-sysfs.c +1
-2@@ -718,8 +725,13 @@ static int counter_count_attrs_create(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < count->num_ext; i++) { - err =counter_attr_create (dev, group, count->ext + i, scope, - count); + ext = count->ext + i;
ext = &count->ext[i];
Could make it more clear we are dealing with pointers rather than integers.
Quoted 18 lines
drivers/counter/counter-sysfs.c +7
-0+ + err =counter_attr_create (dev, group, ext, scope, count); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }@@ -783,6 +795,7 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,struct device *const dev = &counter->dev;int err;size_t i; +struct counter_comp *ext;/* Add Signals sysfs attributes */ err =counter_sysfs_signals_add (counter, group);
drivers/counter/counter-sysfs.c static int counter_sysfs_attr_add(struct counter_device *const counter, · line 831
Quoted 6 lines
drivers/counter/counter-sysfs.c +1
-2@@ -815,8 +828,13 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < counter->num_ext; i++) { - err =counter_attr_create (dev, group, counter->ext + i, scope, -NULL ); + ext = counter->ext + i;
ditto
Quoted 10 lines
drivers/counter/counter-sysfs.c +6
-0+ + err =counter_attr_create (dev, group, ext, scope,NULL ); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }
Jonathan Cameron
maintainerRe: [PATCH v12 14/17] counter: Implement *_component_id sysfs attributes
Quoted 42 lines · On Sat, 10 Jul 2021 12:06:53 -0500 David Lechner <[email protected]> wrote:
Quoted 28 lines · William Breathitt Gray
The Generic Counter chrdev interface expects users to supply component IDs in order to select extensions for requests. In order for users to know what component ID belongs to which extension this information must be exposed. The
*_component_idattribute provides a way for users to discover what component ID belongs to which respective extension.Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- Documentation/ABI/testing/sysfs-bus-counter | 16 ++++++++++- drivers/counter/counter-sysfs.c | 30 ++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-)Documentation/ABI/testing/sysfs-bus-counter +5
-0@@ -203,12 +203,26 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id +What: /sys/bus/counter/devices/counterX/countY/floor_component_id +What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id +What: /sys/bus/counter/devices/counterX/countY/direction_component_id +What: /sys/bus/counter/devices/counterX/countY/enable_component_idDocumentation/ABI/testing/sysfs-bus-counter Description: · line 220
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-counter +9
-0+What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id +What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id +What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id +What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_idCould we just write a single line?
Quoted 1 line
What: /sys/bus/counter/devices/counterX/signalY/<component>_component_id
Not nice for grepping so I think it's better to call them out explicitly.
There has been a proposal to check this ABI doc against running kernels, and if we have too many wild cards that becomes very difficult to do.
Jonathan
Quoted 96 lines
Quoted 47 lines
Documentation/ABI/testing/sysfs-bus-counter +1
-1KernelVersion: 5.15 Contact: [email protected] Description: Read-only attribute that indicates the component ID of the - respective Synapse of Count Y for Signal Z. + respective extension or Synapse. What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns KernelVersion: 5.14drivers/counter/counter-sysfs.c +9
-2@@ -587,6 +587,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter,int err;struct counter_comp comp;size_t i; +struct counter_comp *ext;/* Create main Signal attribute */ comp = counter_signal_comp;@@ -602,8 +603,13 @@ static int counter_signal_attrs_create(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < signal->num_ext; i++) { - err =counter_attr_create (dev, group, signal->ext + i, scope, - signal); + ext = signal->ext + i; + + err =counter_attr_create (dev, group, ext, scope, signal); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }@@ -694,6 +700,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,int err;struct counter_comp comp;size_t i; +struct counter_comp *ext;/* Create main Count attribute */ comp = counter_count_comp;drivers/counter/counter-sysfs.c static int counter_count_attrs_create(struct counter_device *const counter, · line 728
Quoted 6 lines
drivers/counter/counter-sysfs.c +1
-2@@ -718,8 +725,13 @@ static int counter_count_attrs_create(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < count->num_ext; i++) { - err =counter_attr_create (dev, group, count->ext + i, scope, - count); + ext = count->ext + i;ext = &count->ext[i];Could make it more clear we are dealing with pointers rather than integers.
Quoted 18 lines
drivers/counter/counter-sysfs.c +7
-0+ + err =counter_attr_create (dev, group, ext, scope, count); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }@@ -783,6 +795,7 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,struct device *const dev = &counter->dev;int err;size_t i; +struct counter_comp *ext;/* Add Signals sysfs attributes */ err =counter_sysfs_signals_add (counter, group);drivers/counter/counter-sysfs.c static int counter_sysfs_attr_add(struct counter_device *const counter, · line 831
Quoted 6 lines
drivers/counter/counter-sysfs.c +1
-2@@ -815,8 +828,13 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,/* Create an attribute for each extension */ for (i =0 ; i < counter->num_ext; i++) { - err =counter_attr_create (dev, group, counter->ext + i, scope, -NULL ); + ext = counter->ext + i;ditto
Quoted 10 lines
drivers/counter/counter-sysfs.c +6
-0+ + err =counter_attr_create (dev, group, ext, scope,NULL ); +if (err <0 ) +return err; + + err =counter_comp_id_attr_create (dev, group, ext->name, i);if (err <0 )return err; }
David Lechner
reviewerRe: [PATCH v12 14/17] counter: Implement *_component_id sysfs attributes
Quoted 45 lines · Jonathan Cameron
Quoted 42 lines · On Sat, 10 Jul 2021 12:06:53 -0500 David Lechner <[email protected]> wrote:
Quoted 28 lines · William Breathitt Gray
The Generic Counter chrdev interface expects users to supply component IDs in order to select extensions for requests. In order for users to know what component ID belongs to which extension this information must be exposed. The
*_component_idattribute provides a way for users to discover what component ID belongs to which respective extension.Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- Documentation/ABI/testing/sysfs-bus-counter | 16 ++++++++++- drivers/counter/counter-sysfs.c | 30 ++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-)Documentation/ABI/testing/sysfs-bus-counter +5
-0@@ -203,12 +203,26 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id +What: /sys/bus/counter/devices/counterX/countY/floor_component_id +What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id +What: /sys/bus/counter/devices/counterX/countY/direction_component_id +What: /sys/bus/counter/devices/counterX/countY/enable_component_idDocumentation/ABI/testing/sysfs-bus-counter Description: · line 220
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-counter +9
-0+What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id +What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id +What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id +What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_idCould we just write a single line?
Quoted 11 lines
Quoted 1 line
What: /sys/bus/counter/devices/counterX/signalY/<component>_component_id
Not nice for grepping so I think it's better to call them out explicitly.
There has been a proposal to check this ABI doc against running kernels, and if we have too many wild cards that becomes very difficult to do.
Jonathan
Quoted 2 lines
Quoted 2 lines
Documentation/ABI/testing/sysfs-bus-counter +0
-0KernelVersion: 5.15
Makes sense. Do we start a new group of similar names with the same description for each kernel release that includes new attributes then?
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Documentation/ABI/testing/sysfs-bus-counter +1
-1Contact: [email protected] Description: Read-only attribute that indicates the component ID of the - respective Synapse of Count Y for Signal Z. + respective extension or Synapse.
Re: [PATCH v12 14/17] counter: Implement *_component_id sysfs attributes
Quoted 53 lines · On Sun, 11 Jul 2021 09:08:29 -0500 David Lechner <[email protected]> wrote:
Quoted 52 lines · Jonathan Cameron
Quoted 42 lines · On Sat, 10 Jul 2021 12:06:53 -0500 David Lechner <[email protected]> wrote:
Quoted 28 lines · William Breathitt Gray
The Generic Counter chrdev interface expects users to supply component IDs in order to select extensions for requests. In order for users to know what component ID belongs to which extension this information must be exposed. The
*_component_idattribute provides a way for users to discover what component ID belongs to which respective extension.Cc David Lechner <[email protected]>Cc Gwendal Grignou <[email protected]>Cc Dan Carpenter <[email protected]>Signed-off-by William Breathitt Gray <[email protected]>--- Documentation/ABI/testing/sysfs-bus-counter | 16 ++++++++++- drivers/counter/counter-sysfs.c | 30 ++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-)Documentation/ABI/testing/sysfs-bus-counter +5
-0@@ -203,12 +203,26 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id +What: /sys/bus/counter/devices/counterX/countY/floor_component_id +What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id +What: /sys/bus/counter/devices/counterX/countY/direction_component_id +What: /sys/bus/counter/devices/counterX/countY/enable_component_idDocumentation/ABI/testing/sysfs-bus-counter Description: · line 220
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-counter +9
-0+What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id +What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id +What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id +What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_idCould we just write a single line?
Quoted 1 line
What: /sys/bus/counter/devices/counterX/signalY/<component>_component_id
Not nice for grepping so I think it's better to call them out explicitly.
There has been a proposal to check this ABI doc against running kernels, and if we have too many wild cards that becomes very difficult to do.
Quoted 7 lines
Quoted 4 lines
Jonathan
Quoted 2 lines
Quoted 2 lines
Documentation/ABI/testing/sysfs-bus-counter +0
-0KernelVersion: 5.15Makes sense. Do we start a new group of similar names with the same description for each kernel release that includes new attributes then?
You've spotted one of the short comings of current format. The scripts that produce the html docs don't cope with multiple version numbers.
Mostly for IIO we've just been cynical and not had the correct kernel version for new ABI when it's added. It's not ideal though.
The alternative, as you mention is to have a new block. Perhaps we can have that refer back to the existing one if the docs cover the new entries as well.
@Mauro: Any suggestions for this?
Thanks
Jonathan
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Documentation/ABI/testing/sysfs-bus-counter +1
-1Contact: [email protected] Description: Read-only attribute that indicates the component ID of the - respective Synapse of Count Y for Signal Z. + respective extension or Synapse.
PATCH v12 15/17 counter: Implement events_queue_size sysfs attribute
In reply to William Breathitt GrayThe events_queue_size sysfs attribute provides a way for users to dynamically configure the Counter events queue size for the Counter character device interface. The size is in number of struct counter_event data structures. The number of elements will be rounded-up to a power of 2 due to a requirement of the kfifo_alloc function called during reallocation of the queue.
--- Documentation/ABI/testing/sysfs-bus-counter | 8 ++++ drivers/counter/counter-chrdev.c | 4 ++ drivers/counter/counter-sysfs.c | 44 +++++++++++++++++++++ include/linux/counter.h | 2 + 4 files changed, 58 insertions(+)
Documentation/ABI/testing/sysfs-bus-counter +8 -0
@@ -233,6 +233,14 @@ Description: shorter or equal to configured value are ignored. Value 0 means filter is disabled. +What: /sys/bus/counter/devices/counterX/events_queue_size +KernelVersion: 5.15 +Contact: [email protected] +Description: + Size of the Counter events queue in number of struct + counter_event data structures. The number of elements will be + rounded-up to a power of 2. + What: /sys/bus/counter/devices/counterX/name KernelVersion: 5.2 Contact: [email protected]
drivers/counter/counter-chrdev.c +4 -0
@@ -323,6 +323,9 @@ static int counter_chrdev_open(struct inode *inode, struct file *filp)typeof (*counter), chrdev); +if (!mutex_trylock (&counter->chrdev_lock)) +return -EBUSY ; +get_device (&counter->dev); filp->private_data = counter;
@@ -339,6 +342,7 @@ static int counter_chrdev_release(struct inode *inode, struct file *filp)return err;put_device (&counter->dev); +mutex_unlock (&counter->chrdev_lock);return 0 ; }
drivers/counter/counter-sysfs.c +44 -0
69 lines not on this page · raw message
include/linux/counter.h +2 -0
@@ -289,6 +289,7 @@ struct counter_ops {* @priv: optional private data supplied by driver * @dev: internal device structure * @chrdev: internal character device structure +* @chrdev_lock: lock to limit chrdev to a single open at a time * @events_list: list of current watching Counter events * @events_list_lock: lock to protect Counter events list operations * @next_events_list: list of next watching Counter events
@@ -314,6 +315,7 @@ struct counter_device {struct device dev;struct cdev chrdev; +struct mutex chrdev_lock;struct list_head events_list;spinlock_t events_list_lock;struct list_head next_events_list;
Signature · 1 line
2.32.0
Dan Carpenter
build botRe: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
CC: kbuild-all(a)lists.01.org In-Reply-To: <e298043c880b350a42bdc40452376a3708bf533b.1625471640.git.vilhelm.gray@gmail.com> References…
CC: kbuild-all(a)lists.01.org
In-Reply-To: <e298043c880b350a42bdc40452376a3708bf533b.1625471640.git.vilhelm.gray@gmail.com>
References: <e298043c880b350a42bdc40452376a3708bf533b.1625471640.git.vilhelm.gray@gmail.com>
TO: William Breathitt Gray <[email protected]>
TO: jic23(a)kernel.org
CC: linux-stm32(a)st-md-mailman.stormreply.com
CC: kernel(a)pengutronix.de
CC: a.fatoum(a)pengutronix.de
CC: kamel.bouhara(a)bootlin.com
CC: gwendal(a)chromium.org
CC: alexandre.belloni(a)bootlin.com
CC: david(a)lechnology.com
CC: linux-iio(a)vger.kernel.org
CC: linux-kernel(a)vger.kernel.org
Hi William,
I love your patch! Perhaps something to improve:
[auto build test WARNING on 6cbb3aa0f9d5d23221df787cf36f74d3866fdb78]
url: https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20210705-162324 base: 6cbb3aa0f9d5d23221df787cf36f74d3866fdb78
:::::: branch date: 8 hours ago
:::::: commit date: 8 hours ago
config: parisc-randconfig-m031-20210705 (attached as .config)
compiler: hppa-linux-gcc (GCC) 9.3.0
If you fix the issue, kindly add following tag as appropriate
smatch warnings:drivers/counter/counter-sysfs.c:815 counter_events_queue_size_write() warn: inconsistent returns '&counter->chrdev_lock'.
vim +815 drivers/counter/counter-sysfs.c
d95f8a2e83a813 William Breathitt Gray 2021-07-05 793
d95f8a2e83a813 William Breathitt Gray 2021-07-05 794 static int counter_events_queue_size_write(struct counter_device *counter,
d95f8a2e83a813 William Breathitt Gray 2021-07-05 795 u64 val)
d95f8a2e83a813 William Breathitt Gray 2021-07-05 796 {
d95f8a2e83a813 William Breathitt Gray 2021-07-05 797 int err;
d95f8a2e83a813 William Breathitt Gray 2021-07-05 798 DECLARE_KFIFO_PTR(events, struct counter_event);
d95f8a2e83a813 William Breathitt Gray 2021-07-05 799
d95f8a2e83a813 William Breathitt Gray 2021-07-05 800 /* Verify chrdev is not currently being used */
d95f8a2e83a813 William Breathitt Gray 2021-07-05 801 if (!mutex_trylock(&counter->chrdev_lock))
d95f8a2e83a813 William Breathitt Gray 2021-07-05 802 return -EBUSY;
d95f8a2e83a813 William Breathitt Gray 2021-07-05 803
d95f8a2e83a813 William Breathitt Gray 2021-07-05 804 /* Allocate new events queue */
d95f8a2e83a813 William Breathitt Gray 2021-07-05 805 err = kfifo_alloc(&events, val, GFP_ATOMIC);
d95f8a2e83a813 William Breathitt Gray 2021-07-05 806 if (err)
d95f8a2e83a813 William Breathitt Gray 2021-07-05 807 return err;
d95f8a2e83a813 William Breathitt Gray 2021-07-05 808
d95f8a2e83a813 William Breathitt Gray 2021-07-05 809 /* Swap in new events queue */
d95f8a2e83a813 William Breathitt Gray 2021-07-05 810 kfifo_free(&counter->events);
d95f8a2e83a813 William Breathitt Gray 2021-07-05 811 counter->events.kfifo = events.kfifo;
d95f8a2e83a813 William Breathitt Gray 2021-07-05 812
d95f8a2e83a813 William Breathitt Gray 2021-07-05 813 mutex_unlock(&counter->chrdev_lock);
d95f8a2e83a813 William Breathitt Gray 2021-07-05 814 d95f8a2e83a813 William Breathitt Gray 2021-07-05 @815 return 0;d95f8a2e83a813 William Breathitt Gray 2021-07-05 816 } d95f8a2e83a813 William Breathitt Gray 2021-07-05 817
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
Re: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
In reply to William Breathitt GrayHi
Quoted 7 lines · William Breathitt Gray
The
events_queue_sizesysfs attribute provides a way for users to dynamically configure the Counter events queue size for the Counter character device interface. The size is in number of structcounter_eventdata structures. The number of elements will be rounded-up to a power of 2 due to a requirement of thekfifo_allocfunction called during reallocation of the queue.
...
Quoted 15 lines
drivers/counter/counter-chrdev.c +3
-0@@ -323,6 +323,9 @@ static int counter_chrdev_open(struct inode *inode, struct file *filp)typeof (*counter), chrdev); +if (!mutex_trylock (&counter->chrdev_lock)) +return -EBUSY ; +get_device (&counter->dev); filp->private_data = counter;
drivers/counter/counter-chrdev.c static int counter_chrdev_release(struct inode *inode, struct file *filp) · line 348
Quoted 7 lines
drivers/counter/counter-chrdev.c +1
-0@@ -339,6 +342,7 @@ static int counter_chrdev_release(struct inode *inode, struct file *filp)return err;put_device (&counter->dev); +mutex_unlock (&counter->chrdev_lock);return 0 ; }
I got two separate mutex warnings from counter_chrdev_open() by doing blind "cat /dev/counter0". First one due mutex being uninitialized:
[ 441.057342] DEBUG_LOCKS_WARN_ON(lock->magic != lock) [ 441.057355] WARNING: CPU: 2 PID: 366 at kernel/locking/mutex.c:1416 mutex_trylock+0xf2/0x130 ... [ 441.217331] Call Trace: [ 441.220062] counter_chrdev_open+0x21/0x60 [counter]
...
which I fixed trivially by (please be free to use it)
drivers/counter/counter-chrdev.c +1 -0
@@ -364,6 +364,7 @@ int counter_chrdev_add(struct counter_device *const counter)spin_lock_init (&counter->events_list_lock);init_waitqueue_head (&counter->events_wait);mutex_init (&counter->events_lock); +mutex_init (&counter->chrdev_lock);/* Initialize character device */ cdev_init (&counter->chrdev, &counter_fops);
and after that
[ 16.564403] ================================================
[ 16.570725] WARNING: lock held when returning to user space!
[ 16.577044] 5.13.0-next-20210706+ #4 Not tainted
[ 16.582198] ------------------------------------------------
[ 16.588507] cat/331 is leaving the kernel with locks still held!
[ 16.595214] 1 lock held by cat/331:
[ 16.599103] #0: ffff888102bb3630 (&counter->chrdev_lock){+.+.}-{3:3}, at: counter_chrdev_open+0x21/0x60 [counter]Jarkko
Re: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
Quoted 57 lines · Jarkko Nikula
Hi
Quoted 7 lines · William Breathitt Gray
The
events_queue_sizesysfs attribute provides a way for users to dynamically configure the Counter events queue size for the Counter character device interface. The size is in number of structcounter_eventdata structures. The number of elements will be rounded-up to a power of 2 due to a requirement of thekfifo_allocfunction called during reallocation of the queue....
Quoted 15 lines
drivers/counter/counter-chrdev.c +3
-0@@ -323,6 +323,9 @@ static int counter_chrdev_open(struct inode *inode, struct file *filp)typeof (*counter), chrdev); +if (!mutex_trylock (&counter->chrdev_lock)) +return -EBUSY ; +get_device (&counter->dev); filp->private_data = counter;drivers/counter/counter-chrdev.c static int counter_chrdev_release(struct inode *inode, struct file *filp) · line 348
Quoted 7 lines
drivers/counter/counter-chrdev.c +1
-0@@ -339,6 +342,7 @@ static int counter_chrdev_release(struct inode *inode, struct file *filp)return err;put_device (&counter->dev); +mutex_unlock (&counter->chrdev_lock);return 0 ; }I got two separate mutex warnings from
counter_chrdev_open()by doing blind "cat/dev/counter0". First one due mutex being uninitialized:[ 441.057342] DEBUG_LOCKS_WARN_ON(lock->magic != lock) [ 441.057355] WARNING: CPU: 2 PID: 366 at kernel/locking/mutex.c:1416 mutex_trylock+0xf2/0x130 ... [ 441.217331] Call Trace: [ 441.220062] counter_chrdev_open+0x21/0x60 [counter]...
which I fixed trivially by (please be free to use it)
drivers/counter/counter-chrdev.c +1
-0@@ -364,6 +364,7 @@ int counter_chrdev_add(struct counter_device *const counter) spin_lock_init(&counter->events_list_lock);init_waitqueue_head (&counter->events_wait);mutex_init (&counter->events_lock); +mutex_init (&counter->chrdev_lock);/* Initialize character device */
Quoted 1 line
cdev_init(&counter->chrdev, &counter_fops);
Thanks, I'll add this line in.
Quoted 9 lines
and after that
[ 16.564403] ================================================ [ 16.570725] WARNING: lock held when returning to user space! [ 16.577044] 5.13.0-next-20210706+ #4 Not tainted [ 16.582198] ------------------------------------------------ [ 16.588507] cat/331 is leaving the kernel with locks still held! [ 16.595214] 1 lock held by cat/331: [ 16.599103] #0: ffff888102bb3630
Quoted 4 lines
(&counter->chrdev_lock){+.+.}-{3:3}, at:
counter_chrdev_open+0x21/0x60[counter]Jarkko
I'm not sure how to resolve this warning. The purpose of this lock is to limit chrdev to a single open at a time. To accomplish this I grab this lock in counter_chrdev_open() and hold it until counter_chrdev_release() is called. Is there a better way to accomplish this?
William Breathitt Gray
David Lechner
reviewerRe: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
Quoted 12 lines · William Breathitt Gray
Quoted 12 lines
and after that
[ 16.564403] ================================================ [ 16.570725] WARNING: lock held when returning to user space! [ 16.577044] 5.13.0-next-20210706+ #4 Not tainted [ 16.582198] ------------------------------------------------ [ 16.588507] cat/331 is leaving the kernel with locks still held! [ 16.595214] 1 lock held by cat/331: [ 16.599103] #0: ffff888102bb3630(&counter->chrdev_lock){+.+.}-{3:3}, at:
counter_chrdev_open+0x21/0x60[counter]
Quoted 5 lines
Quoted 1 line
Jarkko
I'm not sure how to resolve this warning. The purpose of this lock is to limit chrdev to a single open at a time. To accomplish this I grab this lock in
counter_chrdev_open()and hold it untilcounter_chrdev_release()is called. Is there a better way to accomplish this?
How about using an atomic flag, e.g test_and_set_bit()?
David Lechner
reviewerRe: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
Quoted 13 lines · David Lechner
Quoted 12 lines · William Breathitt Gray
Quoted 12 lines
and after that
[ 16.564403] ================================================ [ 16.570725] WARNING: lock held when returning to user space! [ 16.577044] 5.13.0-next-20210706+ #4 Not tainted
[ 16.582198] ------------------------------------------------ [ 16.588507] cat/331 is leaving the kernel with locks still held! [ 16.595214] 1 lock held by cat/331:
[ 16.599103] #0:ffff888102bb3630(&counter->chrdev_lock){+.+.}-{3:3}, at:counter_chrdev_open+0x21/0x60[counter]
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
Jarkko
I'm not sure how to resolve this warning. The purpose of this lock is to limit chrdev to a single open at a time. To accomplish this I grab this lock in
counter_chrdev_open()and hold it untilcounter_chrdev_release()is called. Is there a better way to accomplish this?How about using an atomic flag, e.g
test_and_set_bit()?
Another option could be to rethink it at a higher level and avoid the need for a lock (and sysfs attribute) altogether. For example, would it work to (re)allocate the kfifo buffer in the COUNTER_ENABLE_EVENTS_IOCTL callback and add a parameter to that ioctl to specify the buffer size (with units of events rather than bytes)?
David Lechner
reviewerRe: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
Quoted 20 lines · David Lechner
Quoted 19 lines · David Lechner
Quoted 17 lines · William Breathitt Gray
Quoted 12 lines
and after that
[ 16.564403] ================================================ [ 16.570725] WARNING: lock held when returning to user space! [ 16.577044] 5.13.0-next-20210706+ #4 Not tainted
[ 16.582198] ------------------------------------------------ [ 16.588507] cat/331 is leaving the kernel with locks still held! [ 16.595214] 1 lock held by cat/331:
[ 16.599103] #0:ffff888102bb3630(&counter->chrdev_lock){+.+.}-{3:3}, at:counter_chrdev_open+0x21/0x60[counter]Quoted 1 line
Jarkko
I'm not sure how to resolve this warning. The purpose of this lock is to limit chrdev to a single open at a time. To accomplish this I grab this lock in
counter_chrdev_open()and hold it untilcounter_chrdev_release()is called. Is there a better way to accomplish this?
Quoted 8 lines
Quoted 1 line
How about using an atomic flag, e.g
test_and_set_bit()?Another option could be to rethink it at a higher level and avoid the need for a lock (and sysfs attribute) altogether. For example, would it work to (re)allocate the kfifo buffer in the
COUNTER_ENABLE_EVENTS_IOCTLcallback and add a parameter to that ioctl to specify the buffer size (with units of events rather than bytes)?
Thinking about it a bit more, this probably isn't a good suggestion. It would create issues with being able to start reading from the chrdev before enabling events.
Re: [PATCH v12 15/17] counter: Implement events_queue_size sysfs attribute
In reply to David LechnerQuoted 13 lines · David Lechner
Quoted 12 lines · William Breathitt Gray
Quoted 12 lines
and after that
[ 16.564403] ================================================ [ 16.570725] WARNING: lock held when returning to user space! [ 16.577044] 5.13.0-next-20210706+ #4 Not tainted [ 16.582198] ------------------------------------------------ [ 16.588507] cat/331 is leaving the kernel with locks still held! [ 16.595214] 1 lock held by cat/331: [ 16.599103] #0: ffff888102bb3630(&counter->chrdev_lock){+.+.}-{3:3}, at:
counter_chrdev_open+0x21/0x60[counter]
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
Jarkko
I'm not sure how to resolve this warning. The purpose of this lock is to limit chrdev to a single open at a time. To accomplish this I grab this lock in
counter_chrdev_open()and hold it untilcounter_chrdev_release()is called. Is there a better way to accomplish this?How about using an atomic flag, e.g
test_and_set_bit()?
Yes, I think this might work: atomically test and set the bit to lock and atomically clear it to unlock. I'll replace the mutex with an atomic flag.
By the way, what is the difference between test_and_set_bit() and test_and_set_bit_lock()?
William Breathitt Gray
PATCH v12 16/17 counter: 104-quad-8: Replace mutex with spinlock
In reply to William Breathitt GrayThis patch replaces the mutex I/O lock with a spinlock. This is in preparation for a subsequent patch adding IRQ support for 104-QUAD-8 devices; we can't sleep in an interrupt context, so we'll need to use a spinlock instead.
--- drivers/counter/104-quad-8.c | 90 +++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 37 deletions(-)
drivers/counter/104-quad-8.c +53 -37
@@ -16,6 +16,7 @@#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> +#include <linux/spinlock.h> #define QUAD8_EXTENT 32
@@ -28,6 +29,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");/** * struct quad8 - device private data structure +* @lock: lock to prevent clobbering device states during R/W ops * @counter: instance of the counter_device * @fck_prescaler: array of filter clock prescaler configurations * @preset: array of preset values
@@ -42,7 +44,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");* @base: base port address of the device */ struct quad8 { -struct mutex lock; +spinlock_t lock;struct counter_device counter;unsigned int fck_prescaler[QUAD8_NUM_COUNTERS ];unsigned int preset[QUAD8_NUM_COUNTERS ];
@@ -123,6 +125,7 @@ static int quad8_count_read(struct counter_device *counter,unsigned int flags;unsigned int borrow;unsigned int carry; +unsigned long irqflags;int i; flags =inb (base_offset +1 );
@@ -132,7 +135,7 @@ static int quad8_count_read(struct counter_device *counter,/* Borrow XOR Carry effectively doubles count range */ *val = (unsigned long )(borrow ^ carry) <<24 ; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);/* Reset Byte Pointer; transfer Counter to Output Latch */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -141,7 +144,7 @@ static int quad8_count_read(struct counter_device *counter,for (i =0 ; i <3 ; i++) *val |= (unsigned long )inb (base_offset) << (8 * i); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -151,13 +154,14 @@ static int quad8_count_write(struct counter_device *counter, {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id; +unsigned long irqflags;int i;/* Only 24-bit values are supported */ if (val >0xFFFFFF )return -ERANGE ; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);/* Reset Byte Pointer */ outb (QUAD8_CTR_RLD |QUAD8_RLD_RESET_BP , base_offset +1 );
@@ -182,7 +186,7 @@ static int quad8_count_write(struct counter_device *counter,/* Reset Error flag */ outb (QUAD8_CTR_RLD |QUAD8_RLD_RESET_E , base_offset +1 ); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -200,8 +204,9 @@ static int quad8_function_read(struct counter_device *counter, {struct quad8 *const priv = counter->priv;const int id = count->id; +unsigned long irqflags; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);if (priv->quadrature_mode[id])switch (priv->quadrature_scale[id]) {
@@ -218,7 +223,7 @@ static int quad8_function_read(struct counter_device *counter,else *function =COUNTER_FUNCTION_PULSE_DIRECTION ; -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -233,10 +238,11 @@ static int quad8_function_write(struct counter_device *counter,unsigned int *const scale = priv->quadrature_scale + id;unsigned int *const synchronous_mode = priv->synchronous_mode + id;const int base_offset = priv->base +2 * id +1 ; +unsigned long irqflags;unsigned int mode_cfg;unsigned int idr_cfg; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); mode_cfg = priv->count_mode[id] <<1 ; idr_cfg = priv->index_polarity[id] <<1 ;
@@ -271,7 +277,7 @@ static int quad8_function_write(struct counter_device *counter,break ;default :/* should never reach this path */ -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return -EINVAL ; } }
@@ -279,7 +285,7 @@ static int quad8_function_write(struct counter_device *counter,/* Load mode configuration to Counter Mode Register */ outb (QUAD8_CTR_CMR | mode_cfg, base_offset); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -405,9 +411,10 @@ static int quad8_index_polarity_set(struct counter_device *counter,struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id -16 ;const int base_offset = priv->base +2 * channel_id +1 ; +unsigned long irqflags;unsigned int idr_cfg = index_polarity <<1 ; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); idr_cfg |= priv->synchronous_mode[channel_id];
@@ -416,7 +423,7 @@ static int quad8_index_polarity_set(struct counter_device *counter,/* Load Index Control configuration to Index Control Register */ outb (QUAD8_CTR_IDR | idr_cfg, base_offset); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -445,15 +452,16 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id -16 ;const int base_offset = priv->base +2 * channel_id +1 ; +unsigned long irqflags;unsigned int idr_cfg = synchronous_mode; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); idr_cfg |= priv->index_polarity[channel_id] <<1 ;/* Index function must be non-synchronous in non-quadrature mode */ if (synchronous_mode && !priv->quadrature_mode[channel_id]) { -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return -EINVAL ; }
@@ -462,7 +470,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,/* Load Index Control configuration to Index Control Register */ outb (QUAD8_CTR_IDR | idr_cfg, base_offset); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -509,6 +517,7 @@ static int quad8_count_mode_write(struct counter_device *counter,unsigned int count_mode;unsigned int mode_cfg;const int base_offset = priv->base +2 * count->id +1 ; +unsigned long irqflags;/* Map Generic Counter count mode to 104-QUAD-8 count mode */ switch (cnt_mode) {
@@ -529,7 +538,7 @@ static int quad8_count_mode_write(struct counter_device *counter,return -EINVAL ; } -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); priv->count_mode[count->id] = count_mode;
@@ -543,7 +552,7 @@ static int quad8_count_mode_write(struct counter_device *counter,/* Load mode configuration to Counter Mode Register */ outb (QUAD8_CTR_CMR | mode_cfg, base_offset); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -563,9 +572,10 @@ static int quad8_count_enable_write(struct counter_device *counter, {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id; +unsigned long irqflags;unsigned int ior_cfg; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); priv->ab_enable[count->id] = enable;
@@ -574,7 +584,7 @@ static int quad8_count_enable_write(struct counter_device *counter,/* Load I/O control configuration */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset +1 ); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -625,16 +635,17 @@ static int quad8_count_preset_write(struct counter_device *counter, struct counter_count *count, u64 preset) {struct quad8 *const priv = counter->priv; +unsigned long irqflags;/* Only 24-bit values are supported */ if (preset >0xFFFFFF )return -ERANGE ; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);quad8_preset_register_set (priv, count->id, preset); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -643,8 +654,9 @@ static int quad8_count_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { struct quad8 *const priv = counter->priv; +unsigned long irqflags; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);/* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) {
@@ -658,7 +670,7 @@ static int quad8_count_ceiling_read(struct counter_device *counter,break ; } -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -667,23 +679,24 @@ static int quad8_count_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) {struct quad8 *const priv = counter->priv; +unsigned long irqflags;/* Only 24-bit values are supported */ if (ceiling >0xFFFFFF )return -ERANGE ; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);/* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) {case 1 :case 3 :quad8_preset_register_set (priv, count->id, ceiling); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; } -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return -EINVAL ; }
@@ -705,12 +718,13 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, {struct quad8 *const priv = counter->priv;const int base_offset = priv->base +2 * count->id +1 ; +unsigned long irqflags;unsigned int ior_cfg;/* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); priv->preset_enable[count->id] = preset_enable;
@@ -719,7 +733,7 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,/* Load I/O control configuration to Input / Output Control Register */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -730,22 +744,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter, {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ; +unsigned long irqflags;bool disabled;unsigned int status; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); disabled = !(priv->cable_fault_enable &BIT (channel_id));if (disabled) { -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return -EINVAL ; }/* Logic 0 = cable fault */ status =inb (priv->base +QUAD8_DIFF_ENCODER_CABLE_STATUS ); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);/* Mask respective channel and invert logic */ *cable_fault = !(status &BIT (channel_id));
@@ -771,9 +786,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, {struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ; +unsigned long irqflags;unsigned int cable_fault_enable; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags);if (enable) priv->cable_fault_enable |=BIT (channel_id);
@@ -785,7 +801,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,outb (cable_fault_enable, priv->base +QUAD8_DIFF_ENCODER_CABLE_STATUS ); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -808,8 +824,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,struct quad8 *const priv = counter->priv;const size_t channel_id = signal->id /2 ;const int base_offset = priv->base +2 * channel_id; +unsigned long irqflags; -mutex_lock (&priv->lock); +spin_lock_irqsave (&priv->lock, irqflags); priv->fck_prescaler[channel_id] = prescaler;
@@ -821,7 +838,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,outb (QUAD8_CTR_RLD |QUAD8_RLD_RESET_BP |QUAD8_RLD_PRESET_PSC , base_offset +1 ); -mutex_unlock (&priv->lock); +spin_unlock_irqrestore (&priv->lock, irqflags);return 0 ; }
@@ -990,8 +1007,7 @@ static int quad8_probe(struct device *dev, unsigned int id) priv->counter.priv = priv; priv->base = base[id]; -/* Initialize mutex */ -mutex_init (&priv->lock); +spin_lock_init (&priv->lock);/* Reset all counters and disable interrupt function */ outb (QUAD8_CHAN_OP_RESET_COUNTERS , base[id] +QUAD8_REG_CHAN_OP );
Signature · 1 line
2.32.0
PATCH v12 17/17 counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
In reply to William Breathitt GrayThe LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index.
Carry:
Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows.
Compare:
Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter.
Carry-Borrow:
Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows.
Index:
Interrupt generated on active high Index signal.
These four functions correspond respectivefly to the following four Counter event types: COUNTER_EVENT_OVERFLOW, COUNTER_EVENT_THRESHOLD, COUNTER_EVENT_OVERFLOW_UNDERFLOW, and COUNTER_EVENT_INDEX. Interrupts push Counter events to event channel X, where 'X' is the respective channel whose FLG1 activated.
This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter.
--- drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++-- drivers/counter/Kconfig | 6 +- 2 files changed, 164 insertions(+), 9 deletions(-)
drivers/counter/104-quad-8.c +161 -6
@@ -11,6 +11,7 @@#include <linux/errno.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/interrupt.h> #include <linux/isa.h> #include <linux/kernel.h> #include <linux/module.h>
@@ -25,6 +26,10 @@ static unsigned int num_quad8;module_param_hw_array (base, uint, ioport, &num_quad8,0 );MODULE_PARM_DESC (base,"ACCES 104-QUAD-8 base addresses" ); +static unsigned int irq[max_num_isa_dev (QUAD8_EXTENT )]; +module_param_hw_array (irq, uint, irq,NULL ,0 ); +MODULE_PARM_DESC (irq,"ACCES 104-QUAD-8 interrupt line numbers" ); +#define QUAD8_NUM_COUNTERS 8/**
@@ -38,6 +43,8 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");* @quadrature_scale: array of quadrature mode scale configurations * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations +* @irq_trigger: array of current IRQ trigger function configurations +* @next_irq_trigger: array of next IRQ trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations
@@ -53,13 +60,17 @@ struct quad8 {unsigned int quadrature_scale[QUAD8_NUM_COUNTERS ];unsigned int ab_enable[QUAD8_NUM_COUNTERS ];unsigned int preset_enable[QUAD8_NUM_COUNTERS ]; +unsigned int irq_trigger[QUAD8_NUM_COUNTERS ]; +unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS ];unsigned int synchronous_mode[QUAD8_NUM_COUNTERS ];unsigned int index_polarity[QUAD8_NUM_COUNTERS ];unsigned int cable_fault_enable;unsigned int base; }; +#define QUAD8_REG_INTERRUPT_STATUS 0x10#define QUAD8_REG_CHAN_OP 0x11 +#define QUAD8_REG_INDEX_INTERRUPT 0x12#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17/* Borrow Toggle flip-flop */
@@ -92,8 +103,8 @@ struct quad8 {#define QUAD8_RLD_CNTR_OUT 0x10/* Transfer Preset Register LSB to FCK Prescaler */ #define QUAD8_RLD_PRESET_PSC 0x18 -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04#define QUAD8_CMR_QUADRATURE_X1 0x08#define QUAD8_CMR_QUADRATURE_X2 0x10#define QUAD8_CMR_QUADRATURE_X4 0x18
@@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter, } } +enum { +QUAD8_EVENT_NONE =-1 , +QUAD8_EVENT_CARRY =0 , +QUAD8_EVENT_COMPARE =1 , +QUAD8_EVENT_CARRY_BORROW =2 , +QUAD8_EVENT_INDEX =3 , +}; + +static int quad8_events_configure (struct counter_device *counter) +{ +struct quad8 *const priv = counter->priv; +unsigned long irq_enabled =0 ; +unsigned long irqflags; +size_t channel; +unsigned long ior_cfg; +unsigned long base_offset; + +spin_lock_irqsave (&priv->lock, irqflags); + +/* Enable interrupts for the requested channels, disable for the rest */ +for (channel =0 ; channel <QUAD8_NUM_COUNTERS ; channel++) { +if (priv->next_irq_trigger[channel] ==QUAD8_EVENT_NONE ) +continue ; + +if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) { +/* Save new IRQ function configuration */ + priv->irq_trigger[channel] = priv->next_irq_trigger[channel]; + +/* Load configuration to I/O Control Register */ + ior_cfg = priv->ab_enable[channel] | + priv->preset_enable[channel] <<1 | + priv->irq_trigger[channel] <<3 ; + base_offset = priv->base +2 * channel +1 ; +outb (QUAD8_CTR_IOR | ior_cfg, base_offset); + } + +/* Reset next IRQ trigger function configuration */ + priv->next_irq_trigger[channel] =QUAD8_EVENT_NONE ; + +/* Enable IRQ line */ + irq_enabled |=BIT (channel); + } + +outb (irq_enabled, priv->base +QUAD8_REG_INDEX_INTERRUPT ); + +spin_unlock_irqrestore (&priv->lock, irqflags); + +return 0 ; +} + +static int quad8_watch_validate (struct counter_device *counter, +const struct counter_watch *watch) +{ +struct quad8 *const priv = counter->priv; + +if (watch->channel >QUAD8_NUM_COUNTERS -1 ) +return -EINVAL ; + +switch (watch->event) { +case COUNTER_EVENT_OVERFLOW : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_CARRY ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_CARRY ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_THRESHOLD : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_COMPARE ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_COMPARE ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_OVERFLOW_UNDERFLOW : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_CARRY_BORROW ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_CARRY_BORROW ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_INDEX : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_INDEX ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_INDEX ) +return -EINVAL ; +return 0 ; +default : +return -EINVAL ; + } +} +static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, .function_read = quad8_function_read, .function_write = quad8_function_write, - .action_read = quad8_action_read + .action_read = quad8_action_read, + .events_configure = quad8_events_configure, + .watch_validate = quad8_watch_validate, };static const char *const quad8_index_polarity_modes[] = {
@@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter, priv->ab_enable[count->id] = enable; - ior_cfg = enable | priv->preset_enable[count->id] <<1 ; + ior_cfg = enable | priv->preset_enable[count->id] <<1 | + priv->irq_trigger[count->id] <<3 ;/* Load I/O control configuration */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset +1 );
@@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 ; + ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 | + priv->irq_trigger[count->id] <<3 ;/* Load I/O control configuration to Input / Output Control Register */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset);
@@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = {QUAD8_COUNT (7 ,"Channel 8 Count" ) }; +static irqreturn_t quad8_irq_handler (int irq,void *private) +{ +struct quad8 *const priv = private; +const unsigned long base = priv->base; +unsigned long irq_status; +unsigned long channel; +u8 event; + + irq_status =inb (base +QUAD8_REG_INTERRUPT_STATUS ); +if (!irq_status) +return IRQ_NONE ; + +for_each_set_bit (channel, &irq_status,QUAD8_NUM_COUNTERS ) { +switch (priv->irq_trigger[channel]) { +case QUAD8_EVENT_CARRY : + event =COUNTER_EVENT_OVERFLOW ; +break ; +case QUAD8_EVENT_COMPARE : + event =COUNTER_EVENT_THRESHOLD ; +break ; +case QUAD8_EVENT_CARRY_BORROW : + event =COUNTER_EVENT_OVERFLOW_UNDERFLOW ; +break ; +case QUAD8_EVENT_INDEX : + event =COUNTER_EVENT_INDEX ; +break ; +default : +/* should never reach this path */ +WARN_ONCE (true,"invalid interrupt trigger function %u configured for channel %lu\n" , + priv->irq_trigger[channel], channel); +continue ; + } + +counter_push_event (&priv->counter, event, channel); + } + +/* Clear pending interrupts on device */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base +QUAD8_REG_CHAN_OP ); + +return IRQ_HANDLED ; +} +static int quad8_probe (struct device *dev,unsigned int id) {struct quad8 *priv;int i, j;unsigned int base_offset; +int err;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",
@@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id)spin_lock_init (&priv->lock); +/* Reset Index/Interrupt Register */ +outb (0x00 , base[id] +QUAD8_REG_INDEX_INTERRUPT );/* Reset all counters and disable interrupt function */ outb (QUAD8_CHAN_OP_RESET_COUNTERS , base[id] +QUAD8_REG_CHAN_OP );/* Set initial configuration for all counters */
@@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id)outb (QUAD8_CTR_IOR , base_offset +1 );/* Disable index function; negative index polarity */ outb (QUAD8_CTR_IDR , base_offset +1 ); +/* Initialize next IRQ trigger function configuration */ + priv->next_irq_trigger[i] =QUAD8_EVENT_NONE ; }/* Disable Differential Encoder Cable Status for all channels */ outb (0xFF , base[id] +QUAD8_DIFF_ENCODER_CABLE_STATUS ); -/* Enable all counters */ -outb (QUAD8_CHAN_OP_ENABLE_COUNTERS , base[id] +QUAD8_REG_CHAN_OP ); +/* Enable all counters and enable interrupt function */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base[id] +QUAD8_REG_CHAN_OP ); + + err =devm_request_irq (dev, irq[id], quad8_irq_handler,IRQF_SHARED , + priv->counter.name, priv); +if (err) +return err;return devm_counter_register (dev, &priv->counter); }
drivers/counter/Kconfig +3 -3
@@ -23,11 +23,11 @@ config 104_QUAD_8 A counter's respective error flag may be cleared by performing a write operation on the respective count value attribute. Although the 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be - set, either directly or via the counter's preset attribute. Interrupts - are not supported by this driver. + set, either directly or via the counter's preset attribute. The base port addresses for the devices may be configured via the base - array module parameter. + array module parameter. The interrupt line numbers for the devices may + be configured via the irq array module parameter. config INTERRUPT_CNT tristate "Interrupt counter driver"
Signature · 1 line
2.32.0
Jonathan Cameron
maintainerRe: [PATCH v12 17/17] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
Quoted 32 lines · On Mon, 5 Jul 2021 17:19:05 +0900 William Breathitt Gray…
The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index.
Carry:
Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows.Compare:
Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter.Carry-Borrow:
Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows.Index:
Interrupt generated on active high Index signal.These four functions correspond respectivefly to the following four Counter event types:
COUNTER_EVENT_OVERFLOW,COUNTER_EVENT_THRESHOLD,COUNTER_EVENT_OVERFLOW_UNDERFLOW, andCOUNTER_EVENT_INDEX. Interrupts push Counter events to event channel X, where 'X' is the respective channel whose FLG1 activated.
Quoted 6 lines
This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter.
Signed-off-by William Breathitt Gray <[email protected]>
Trivial comment inline.
Quoted 91 lines
--- drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++-- drivers/counter/Kconfig | 6 +- 2 files changed, 164 insertions(+), 9 deletions(-)drivers/counter/104-quad-8.c +34
-1@@ -11,6 +11,7 @@#include <linux/errno.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/interrupt.h> #include <linux/isa.h> #include <linux/kernel.h> #include <linux/module.h> @@ -25,6 +26,10 @@ static unsigned int num_quad8;module_param_hw_array (base, uint, ioport, &num_quad8,0 );MODULE_PARM_DESC (base,"ACCES 104-QUAD-8 base addresses" ); +static unsigned int irq[max_num_isa_dev (QUAD8_EXTENT )]; +module_param_hw_array (irq, uint, irq,NULL ,0 ); +MODULE_PARM_DESC (irq,"ACCES 104-QUAD-8 interrupt line numbers" ); +#define QUAD8_NUM_COUNTERS 8/** @@ -38,6 +43,8 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");* @quadrature_scale: array of quadrature mode scale configurations * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations +* @irq_trigger: array of current IRQ trigger function configurations +* @next_irq_trigger: array of next IRQ trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations @@ -53,13 +60,17 @@ struct quad8 {unsigned int quadrature_scale[QUAD8_NUM_COUNTERS ];unsigned int ab_enable[QUAD8_NUM_COUNTERS ];unsigned int preset_enable[QUAD8_NUM_COUNTERS ]; +unsigned int irq_trigger[QUAD8_NUM_COUNTERS ]; +unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS ];unsigned int synchronous_mode[QUAD8_NUM_COUNTERS ];unsigned int index_polarity[QUAD8_NUM_COUNTERS ];unsigned int cable_fault_enable;unsigned int base; }; +#define QUAD8_REG_INTERRUPT_STATUS 0x10#define QUAD8_REG_CHAN_OP 0x11 +#define QUAD8_REG_INDEX_INTERRUPT 0x12#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17/* Borrow Toggle flip-flop */ @@ -92,8 +103,8 @@ struct quad8 {#define QUAD8_RLD_CNTR_OUT 0x10/* Transfer Preset Register LSB to FCK Prescaler */ #define QUAD8_RLD_PRESET_PSC 0x18 -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04#define QUAD8_CMR_QUADRATURE_X1 0x08#define QUAD8_CMR_QUADRATURE_X2 0x10#define QUAD8_CMR_QUADRATURE_X4 0x18@@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter, } } +enum { +QUAD8_EVENT_NONE =-1 , +QUAD8_EVENT_CARRY =0 , +QUAD8_EVENT_COMPARE =1 , +QUAD8_EVENT_CARRY_BORROW =2 , +QUAD8_EVENT_INDEX =3 , +}; + +static int quad8_events_configure (struct counter_device *counter) +{ +struct quad8 *const priv = counter->priv; +unsigned long irq_enabled =0 ; +unsigned long irqflags; +size_t channel; +unsigned long ior_cfg; +unsigned long base_offset; + +spin_lock_irqsave (&priv->lock, irqflags); + +/* Enable interrupts for the requested channels, disable for the rest */ +for (channel =0 ; channel <QUAD8_NUM_COUNTERS ; channel++) { +if (priv->next_irq_trigger[channel] ==QUAD8_EVENT_NONE )
drivers/counter/104-quad-8.c static int quad8_action_read(struct counter_device *counter, · line 423
Quoted 10 lines
drivers/counter/104-quad-8.c +10
-0+continue ; + +if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) { +/* Save new IRQ function configuration */ + priv->irq_trigger[channel] = priv->next_irq_trigger[channel]; + +/* Load configuration to I/O Control Register */ + ior_cfg = priv->ab_enable[channel] | + priv->preset_enable[channel] <<1 | + priv->irq_trigger[channel] <<3 ;
Nicer to define masks and use FIELD_PREP etc for these rather than hiding shifts down here in the code.
Quoted 193 lines
drivers/counter/104-quad-8.c +117
-5+ base_offset = priv->base +2 * channel +1 ; +outb (QUAD8_CTR_IOR | ior_cfg, base_offset); + } + +/* Reset next IRQ trigger function configuration */ + priv->next_irq_trigger[channel] =QUAD8_EVENT_NONE ; + +/* Enable IRQ line */ + irq_enabled |=BIT (channel); + } + +outb (irq_enabled, priv->base +QUAD8_REG_INDEX_INTERRUPT ); + +spin_unlock_irqrestore (&priv->lock, irqflags); + +return 0 ; +} + +static int quad8_watch_validate (struct counter_device *counter, +const struct counter_watch *watch) +{ +struct quad8 *const priv = counter->priv; + +if (watch->channel >QUAD8_NUM_COUNTERS -1 ) +return -EINVAL ; + +switch (watch->event) { +case COUNTER_EVENT_OVERFLOW : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_CARRY ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_CARRY ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_THRESHOLD : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_COMPARE ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_COMPARE ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_OVERFLOW_UNDERFLOW : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_CARRY_BORROW ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_CARRY_BORROW ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_INDEX : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_INDEX ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_INDEX ) +return -EINVAL ; +return 0 ; +default : +return -EINVAL ; + } +} +static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, .function_read = quad8_function_read, .function_write = quad8_function_write, - .action_read = quad8_action_read + .action_read = quad8_action_read, + .events_configure = quad8_events_configure, + .watch_validate = quad8_watch_validate, };static const char *const quad8_index_polarity_modes[] = {@@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter, priv->ab_enable[count->id] = enable; - ior_cfg = enable | priv->preset_enable[count->id] <<1 ; + ior_cfg = enable | priv->preset_enable[count->id] <<1 | + priv->irq_trigger[count->id] <<3 ;/* Load I/O control configuration */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset +1 );@@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 ; + ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 | + priv->irq_trigger[count->id] <<3 ;/* Load I/O control configuration to Input / Output Control Register */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset);@@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = {QUAD8_COUNT (7 ,"Channel 8 Count" ) }; +static irqreturn_t quad8_irq_handler (int irq,void *private) +{ +struct quad8 *const priv = private; +const unsigned long base = priv->base; +unsigned long irq_status; +unsigned long channel; +u8 event; + + irq_status =inb (base +QUAD8_REG_INTERRUPT_STATUS ); +if (!irq_status) +return IRQ_NONE ; + +for_each_set_bit (channel, &irq_status,QUAD8_NUM_COUNTERS ) { +switch (priv->irq_trigger[channel]) { +case QUAD8_EVENT_CARRY : + event =COUNTER_EVENT_OVERFLOW ; +break ; +case QUAD8_EVENT_COMPARE : + event =COUNTER_EVENT_THRESHOLD ; +break ; +case QUAD8_EVENT_CARRY_BORROW : + event =COUNTER_EVENT_OVERFLOW_UNDERFLOW ; +break ; +case QUAD8_EVENT_INDEX : + event =COUNTER_EVENT_INDEX ; +break ; +default : +/* should never reach this path */ +WARN_ONCE (true,"invalid interrupt trigger function %u configured for channel %lu\n" , + priv->irq_trigger[channel], channel); +continue ; + } + +counter_push_event (&priv->counter, event, channel); + } + +/* Clear pending interrupts on device */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base +QUAD8_REG_CHAN_OP ); + +return IRQ_HANDLED ; +} +static int quad8_probe (struct device *dev,unsigned int id) {struct quad8 *priv;int i, j;unsigned int base_offset; +int err;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",@@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id)spin_lock_init (&priv->lock); +/* Reset Index/Interrupt Register */ +outb (0x00 , base[id] +QUAD8_REG_INDEX_INTERRUPT );/* Reset all counters and disable interrupt function */ outb (QUAD8_CHAN_OP_RESET_COUNTERS , base[id] +QUAD8_REG_CHAN_OP );/* Set initial configuration for all counters */ @@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id)outb (QUAD8_CTR_IOR , base_offset +1 );/* Disable index function; negative index polarity */ outb (QUAD8_CTR_IDR , base_offset +1 ); +/* Initialize next IRQ trigger function configuration */ + priv->next_irq_trigger[i] =QUAD8_EVENT_NONE ; }/* Disable Differential Encoder Cable Status for all channels */ outb (0xFF , base[id] +QUAD8_DIFF_ENCODER_CABLE_STATUS ); -/* Enable all counters */ -outb (QUAD8_CHAN_OP_ENABLE_COUNTERS , base[id] +QUAD8_REG_CHAN_OP ); +/* Enable all counters and enable interrupt function */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base[id] +QUAD8_REG_CHAN_OP ); + + err =devm_request_irq (dev, irq[id], quad8_irq_handler,IRQF_SHARED , + priv->counter.name, priv); +if (err) +return err;return devm_counter_register (dev, &priv->counter); }drivers/counter/Kconfig +3
-3@@ -23,11 +23,11 @@ config 104_QUAD_8 A counter's respective error flag may be cleared by performing a write operation on the respective count value attribute. Although the 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be - set, either directly or via the counter's preset attribute. Interrupts - are not supported by this driver. + set, either directly or via the counter's preset attribute. The base port addresses for the devices may be configured via the base - array module parameter. + array module parameter. The interrupt line numbers for the devices may + be configured via the irq array module parameter. config INTERRUPT_CNT tristate "Interrupt counter driver"
Re: [PATCH v12 17/17] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
Quoted 35 lines · Jonathan Cameron
Quoted 32 lines · On Mon, 5 Jul 2021 17:19:05 +0900 William Breathitt Gray…
The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index.
Carry:
Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows.Compare:
Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter.Carry-Borrow:
Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows.Index:
Interrupt generated on active high Index signal.These four functions correspond respectivefly to the following four Counter event types:
COUNTER_EVENT_OVERFLOW,COUNTER_EVENT_THRESHOLD,COUNTER_EVENT_OVERFLOW_UNDERFLOW, andCOUNTER_EVENT_INDEX. Interrupts push Counter events to event channel X, where 'X' is the respective channel whose FLG1 activated.
Quoted 8 lines
Quoted 6 lines
This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter.
Signed-off-by William Breathitt Gray <[email protected]>Trivial comment inline.
I agree with the change because it'll make the code for the 104-QUAD-8 driver much clearer to read, but I'd prefer to postpone it until after this patchset is merged so that we don't introduce too many new changes now that we've got this pretty stable.
Syed, would you take a look at this and see if we can use the FIELD_PREP macro to simplify all the shifting we're doing in the 104-quad-8 driver?
Thanks,
William Breathitt Gray
Quoted 299 lines
Quoted 91 lines
--- drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++-- drivers/counter/Kconfig | 6 +- 2 files changed, 164 insertions(+), 9 deletions(-)drivers/counter/104-quad-8.c +34
-1@@ -11,6 +11,7 @@#include <linux/errno.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/interrupt.h> #include <linux/isa.h> #include <linux/kernel.h> #include <linux/module.h> @@ -25,6 +26,10 @@ static unsigned int num_quad8;module_param_hw_array (base, uint, ioport, &num_quad8,0 );MODULE_PARM_DESC (base,"ACCES 104-QUAD-8 base addresses" ); +static unsigned int irq[max_num_isa_dev (QUAD8_EXTENT )]; +module_param_hw_array (irq, uint, irq,NULL ,0 ); +MODULE_PARM_DESC (irq,"ACCES 104-QUAD-8 interrupt line numbers" ); +#define QUAD8_NUM_COUNTERS 8/** @@ -38,6 +43,8 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");* @quadrature_scale: array of quadrature mode scale configurations * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations +* @irq_trigger: array of current IRQ trigger function configurations +* @next_irq_trigger: array of next IRQ trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations @@ -53,13 +60,17 @@ struct quad8 {unsigned int quadrature_scale[QUAD8_NUM_COUNTERS ];unsigned int ab_enable[QUAD8_NUM_COUNTERS ];unsigned int preset_enable[QUAD8_NUM_COUNTERS ]; +unsigned int irq_trigger[QUAD8_NUM_COUNTERS ]; +unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS ];unsigned int synchronous_mode[QUAD8_NUM_COUNTERS ];unsigned int index_polarity[QUAD8_NUM_COUNTERS ];unsigned int cable_fault_enable;unsigned int base; }; +#define QUAD8_REG_INTERRUPT_STATUS 0x10#define QUAD8_REG_CHAN_OP 0x11 +#define QUAD8_REG_INDEX_INTERRUPT 0x12#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17/* Borrow Toggle flip-flop */ @@ -92,8 +103,8 @@ struct quad8 {#define QUAD8_RLD_CNTR_OUT 0x10/* Transfer Preset Register LSB to FCK Prescaler */ #define QUAD8_RLD_PRESET_PSC 0x18 -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04#define QUAD8_CMR_QUADRATURE_X1 0x08#define QUAD8_CMR_QUADRATURE_X2 0x10#define QUAD8_CMR_QUADRATURE_X4 0x18@@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter, } } +enum { +QUAD8_EVENT_NONE =-1 , +QUAD8_EVENT_CARRY =0 , +QUAD8_EVENT_COMPARE =1 , +QUAD8_EVENT_CARRY_BORROW =2 , +QUAD8_EVENT_INDEX =3 , +}; + +static int quad8_events_configure (struct counter_device *counter) +{ +struct quad8 *const priv = counter->priv; +unsigned long irq_enabled =0 ; +unsigned long irqflags; +size_t channel; +unsigned long ior_cfg; +unsigned long base_offset; + +spin_lock_irqsave (&priv->lock, irqflags); + +/* Enable interrupts for the requested channels, disable for the rest */ +for (channel =0 ; channel <QUAD8_NUM_COUNTERS ; channel++) { +if (priv->next_irq_trigger[channel] ==QUAD8_EVENT_NONE )drivers/counter/104-quad-8.c static int quad8_action_read(struct counter_device *counter, · line 423
Quoted 10 lines
drivers/counter/104-quad-8.c +10
-0+continue ; + +if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) { +/* Save new IRQ function configuration */ + priv->irq_trigger[channel] = priv->next_irq_trigger[channel]; + +/* Load configuration to I/O Control Register */ + ior_cfg = priv->ab_enable[channel] | + priv->preset_enable[channel] <<1 | + priv->irq_trigger[channel] <<3 ;Nicer to define masks and use
FIELD_PREPetc for these rather than hiding shifts down here in the code.Quoted 193 lines
drivers/counter/104-quad-8.c +117
-5+ base_offset = priv->base +2 * channel +1 ; +outb (QUAD8_CTR_IOR | ior_cfg, base_offset); + } + +/* Reset next IRQ trigger function configuration */ + priv->next_irq_trigger[channel] =QUAD8_EVENT_NONE ; + +/* Enable IRQ line */ + irq_enabled |=BIT (channel); + } + +outb (irq_enabled, priv->base +QUAD8_REG_INDEX_INTERRUPT ); + +spin_unlock_irqrestore (&priv->lock, irqflags); + +return 0 ; +} + +static int quad8_watch_validate (struct counter_device *counter, +const struct counter_watch *watch) +{ +struct quad8 *const priv = counter->priv; + +if (watch->channel >QUAD8_NUM_COUNTERS -1 ) +return -EINVAL ; + +switch (watch->event) { +case COUNTER_EVENT_OVERFLOW : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_CARRY ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_CARRY ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_THRESHOLD : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_COMPARE ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_COMPARE ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_OVERFLOW_UNDERFLOW : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_CARRY_BORROW ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_CARRY_BORROW ) +return -EINVAL ; +return 0 ; +case COUNTER_EVENT_INDEX : +if (priv->next_irq_trigger[watch->channel] ==QUAD8_EVENT_NONE ) + priv->next_irq_trigger[watch->channel] =QUAD8_EVENT_INDEX ; +else if (priv->next_irq_trigger[watch->channel] !=QUAD8_EVENT_INDEX ) +return -EINVAL ; +return 0 ; +default : +return -EINVAL ; + } +} +static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, .function_read = quad8_function_read, .function_write = quad8_function_write, - .action_read = quad8_action_read + .action_read = quad8_action_read, + .events_configure = quad8_events_configure, + .watch_validate = quad8_watch_validate, };static const char *const quad8_index_polarity_modes[] = {@@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter, priv->ab_enable[count->id] = enable; - ior_cfg = enable | priv->preset_enable[count->id] <<1 ; + ior_cfg = enable | priv->preset_enable[count->id] <<1 | + priv->irq_trigger[count->id] <<3 ;/* Load I/O control configuration */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset +1 );@@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 ; + ior_cfg = priv->ab_enable[count->id] | preset_enable <<1 | + priv->irq_trigger[count->id] <<3 ;/* Load I/O control configuration to Input / Output Control Register */ outb (QUAD8_CTR_IOR | ior_cfg, base_offset);@@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = {QUAD8_COUNT (7 ,"Channel 8 Count" ) }; +static irqreturn_t quad8_irq_handler (int irq,void *private) +{ +struct quad8 *const priv = private; +const unsigned long base = priv->base; +unsigned long irq_status; +unsigned long channel; +u8 event; + + irq_status =inb (base +QUAD8_REG_INTERRUPT_STATUS ); +if (!irq_status) +return IRQ_NONE ; + +for_each_set_bit (channel, &irq_status,QUAD8_NUM_COUNTERS ) { +switch (priv->irq_trigger[channel]) { +case QUAD8_EVENT_CARRY : + event =COUNTER_EVENT_OVERFLOW ; +break ; +case QUAD8_EVENT_COMPARE : + event =COUNTER_EVENT_THRESHOLD ; +break ; +case QUAD8_EVENT_CARRY_BORROW : + event =COUNTER_EVENT_OVERFLOW_UNDERFLOW ; +break ; +case QUAD8_EVENT_INDEX : + event =COUNTER_EVENT_INDEX ; +break ; +default : +/* should never reach this path */ +WARN_ONCE (true,"invalid interrupt trigger function %u configured for channel %lu\n" , + priv->irq_trigger[channel], channel); +continue ; + } + +counter_push_event (&priv->counter, event, channel); + } + +/* Clear pending interrupts on device */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base +QUAD8_REG_CHAN_OP ); + +return IRQ_HANDLED ; +} +static int quad8_probe (struct device *dev,unsigned int id) {struct quad8 *priv;int i, j;unsigned int base_offset; +int err;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",@@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id)spin_lock_init (&priv->lock); +/* Reset Index/Interrupt Register */ +outb (0x00 , base[id] +QUAD8_REG_INDEX_INTERRUPT );/* Reset all counters and disable interrupt function */ outb (QUAD8_CHAN_OP_RESET_COUNTERS , base[id] +QUAD8_REG_CHAN_OP );/* Set initial configuration for all counters */ @@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id)outb (QUAD8_CTR_IOR , base_offset +1 );/* Disable index function; negative index polarity */ outb (QUAD8_CTR_IDR , base_offset +1 ); +/* Initialize next IRQ trigger function configuration */ + priv->next_irq_trigger[i] =QUAD8_EVENT_NONE ; }/* Disable Differential Encoder Cable Status for all channels */ outb (0xFF , base[id] +QUAD8_DIFF_ENCODER_CABLE_STATUS ); -/* Enable all counters */ -outb (QUAD8_CHAN_OP_ENABLE_COUNTERS , base[id] +QUAD8_REG_CHAN_OP ); +/* Enable all counters and enable interrupt function */ +outb (QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC , base[id] +QUAD8_REG_CHAN_OP ); + + err =devm_request_irq (dev, irq[id], quad8_irq_handler,IRQF_SHARED , + priv->counter.name, priv); +if (err) +return err;return devm_counter_register (dev, &priv->counter); }drivers/counter/Kconfig +3
-3@@ -23,11 +23,11 @@ config 104_QUAD_8 A counter's respective error flag may be cleared by performing a write operation on the respective count value attribute. Although the 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be - set, either directly or via the counter's preset attribute. Interrupts - are not supported by this driver. + set, either directly or via the counter's preset attribute. The base port addresses for the devices may be configured via the base - array module parameter. + array module parameter. The interrupt line numbers for the devices may + be configured via the irq array module parameter. config INTERRUPT_CNT tristate "Interrupt counter driver"
Hi
Quoted 8 lines · William Breathitt Gray
To summarize the main points of this patchset: there are no changes to the existing Counter sysfs userspace interface; a Counter character device interface is introduced that allows Counter events and associated data to be
read()by userspace; theevents_configure()andwatch_validate()driver callbacks are introduced to support Counter events; and IRQ support is added to the 104-QUAD-8 driver, serving as an example of how to support the new Counter events functionality.
I quickly tested this set on top of linux-next next-20210706 and didn't see any issue with intel-qep.c. However I noticed two mutex issues in patch 15/17. Perhaps visible due the CONFIG_DEBUG_MUTEXES=y etc. I have on.
I'll be out of office next few weeks so cannot test if you have a new version meanwhile but wanted to share no show stopper from intel-qep.c and those two mutex warnings.
Jarkko
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Quoted 15 lines · Jarkko Nikula
Hi
Quoted 8 lines · William Breathitt Gray
To summarize the main points of this patchset: there are no changes to the existing Counter sysfs userspace interface; a Counter character device interface is introduced that allows Counter events and associated data to be
read()by userspace; theevents_configure()andwatch_validate()driver callbacks are introduced to support Counter events; and IRQ support is added to the 104-QUAD-8 driver, serving as an example of how to support the new Counter events functionality.I quickly tested this set on top of linux-next next-20210706 and didn't see any issue with
intel-qep.c. However I noticed two mutex issues in patch 15/17. Perhaps visible due theCONFIG_DEBUG_MUTEXES=yetc. I have on.
Quoted 5 lines
I'll be out of office next few weeks so cannot test if you have a new version meanwhile but wanted to share no show stopper from
intel-qep.cand those two mutex warnings.Jarkko
Thanks, these seem pretty straightforward to fix so I'll wait a week or so to see if any other issues pop up before making a v13 submission.
William Breathitt Gray
Jonathan Cameron
maintainerQuoted 28 lines · On Mon, 5 Jul 2021 17:18:48 +0900 William Breathitt Gray…
Changes in v12:
- Move unlock to after register set inquad8_count_ceiling_write()
- Add locking protection tocounter_set_event_node()
- Fix sparse warning by using {} instead of {0}
- Clean up and organize comments for clarity
- Reduce boilerplate by utilizingdevm_add_action_or_reset()
- Use switch statements inti_eqep_action_read()to make possible cases more obviousI pulled out a lot of bits and pieces to their own patches; hopefully that makes reviewing this patchset much simpler than before. This patchset is also available on my personal git repo for convenience:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v12The patches preceding "counter: Internalize sysfs interface code" are primarily cleanup and fixes that can be picked up and applied now to the IIO tree if so desired. The "counter: Internalize sysfs interface code" patch as well may be considered for pickup because it is relatively safe and makes no changes to the userspace interface.
To summarize the main points of this patchset: there are no changes to the existing Counter sysfs userspace interface; a Counter character device interface is introduced that allows Counter events and associated data to be
read()by userspace; theevents_configure()andwatch_validate()driver callbacks are introduced to support Counter events; and IRQ support is added to the 104-QUAD-8 driver, serving as an example of how to support the new Counter events functionality.
Quoted 4 lines
Something that should still be discussed: should the struct
counter_event"status" member be 8 bits or 32 bits wide? This member will provide the return status (system error number) of an event operation.
Hi william,
I've looked through the lot and where I haven't commented, I had nothing much to add to David's comments.
I'm not planning to go through the whole thing again unless major changes occur. Fingers crossed for v13.
If it looks like there are still some unresolved issues after that, perhaps applying up to patch 8 or so makes sense to reduced the volume of code you are carrying. Let me know if you'd like me to do that.
Thanks,
Jonathan
Quoted 63 lines
William Breathitt Gray (17):
counter: 104-quad-8: Return error when invalid mode during
ceiling_write
counter: Return error code on invalid modes
counter: Standardize to ERANGE for limit exceeded errors
counter: Renamecounter_signal_valuetocounter_signal_level
counter: Renamecounter_count_functiontocounter_function
counter: Internalize sysfs interface code
counter: Updatecounter.hcomments to reflect sysfs internalization
docs: counter: Update to reflect sysfs internalization
counter: Move counter enums to uapi header
counter: Add character device interface
docs: counter: Document character device interfacetools/counter: Create Counter tools
counter: ImplementsignalZ_action_component_idsysfs attribute
counter: Implement*_component_idsysfs attributes
counter: Implementevents_queue_sizesysfs attribute
counter: 104-quad-8: Replace mutex with spinlock
counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8Documentation/ABI/testing/sysfs-bus-counter | 38 +- Documentation/driver-api/generic-counter.rst | 366 +++- .../userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 3 +- drivers/counter/104-quad-8.c | 728 ++++---- drivers/counter/Kconfig | 6 +- drivers/counter/Makefile | 1 + drivers/counter/counter-chrdev.c | 498 ++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 182 ++ drivers/counter/counter-sysfs.c | 953 +++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------- drivers/counter/ftm-quaddec.c | 59 +- drivers/counter/intel-qep.c | 150 +- drivers/counter/interrupt-cnt.c | 73 +- drivers/counter/microchip-tcb-capture.c | 103 +- drivers/counter/stm32-lptimer-cnt.c | 176 +- drivers/counter/stm32-timer-cnt.c | 147 +- drivers/counter/ti-eqep.c | 205 ++- include/linux/counter.h | 716 ++++---- include/linux/counter_enum.h | 45 - include/uapi/linux/counter.h | 133 ++ tools/Makefile | 13 +- tools/counter/Build | 1 + tools/counter/Makefile | 53 + tools/counter/counter_example.c | 95 ++ 27 files changed, 3501 insertions(+), 2767 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h create mode 100644 include/uapi/linux/counter.h create mode 100644 tools/counter/Build create mode 100644 tools/counter/Makefile create mode 100644 tools/counter/counter_example.cbase-commit:
6cbb3aa0f9d5d23221df787cf36f74d3866fdb78
Quoted 44 lines · Jonathan Cameron
Quoted 28 lines · On Mon, 5 Jul 2021 17:18:48 +0900 William Breathitt Gray…
Changes in v12:
- Move unlock to after register set inquad8_count_ceiling_write()
- Add locking protection tocounter_set_event_node()
- Fix sparse warning by using {} instead of {0}
- Clean up and organize comments for clarity
- Reduce boilerplate by utilizingdevm_add_action_or_reset()
- Use switch statements inti_eqep_action_read()to make possible cases more obviousI pulled out a lot of bits and pieces to their own patches; hopefully that makes reviewing this patchset much simpler than before. This patchset is also available on my personal git repo for convenience:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v12The patches preceding "counter: Internalize sysfs interface code" are primarily cleanup and fixes that can be picked up and applied now to the IIO tree if so desired. The "counter: Internalize sysfs interface code" patch as well may be considered for pickup because it is relatively safe and makes no changes to the userspace interface.
To summarize the main points of this patchset: there are no changes to the existing Counter sysfs userspace interface; a Counter character device interface is introduced that allows Counter events and associated data to be
read()by userspace; theevents_configure()andwatch_validate()driver callbacks are introduced to support Counter events; and IRQ support is added to the 104-QUAD-8 driver, serving as an example of how to support the new Counter events functionality.Quoted 4 lines
Something that should still be discussed: should the struct
counter_event"status" member be 8 bits or 32 bits wide? This member will provide the return status (system error number) of an event operation.Hi william,
I've looked through the lot and where I haven't commented, I had nothing much to add to David's comments.
I'm not planning to go through the whole thing again unless major changes occur. Fingers crossed for v13.
Quoted 7 lines
If it looks like there are still some unresolved issues after that, perhaps applying up to patch 8 or so makes sense to reduced the volume of code you are carrying. Let me know if you'd like me to do that.
Thanks,
Jonathan
Yes, much of the code has remained stable for some months now so I think we're pretty close. If we do need a v14, then applying up to patch 8 would help me a lot (most of the merge conflicts I encounter when I rebase are due to the large subsystem refactor in patch 06).
William Breathitt Gray
Quoted 64 lines
Quoted 63 lines
William Breathitt Gray (17):
counter: 104-quad-8: Return error when invalid mode during
ceiling_write
counter: Return error code on invalid modes
counter: Standardize to ERANGE for limit exceeded errors
counter: Renamecounter_signal_valuetocounter_signal_level
counter: Renamecounter_count_functiontocounter_function
counter: Internalize sysfs interface code
counter: Updatecounter.hcomments to reflect sysfs internalization
docs: counter: Update to reflect sysfs internalization
counter: Move counter enums to uapi header
counter: Add character device interface
docs: counter: Document character device interfacetools/counter: Create Counter tools
counter: ImplementsignalZ_action_component_idsysfs attribute
counter: Implement*_component_idsysfs attributes
counter: Implementevents_queue_sizesysfs attribute
counter: 104-quad-8: Replace mutex with spinlock
counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8Documentation/ABI/testing/sysfs-bus-counter | 38 +- Documentation/driver-api/generic-counter.rst | 366 +++- .../userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 3 +- drivers/counter/104-quad-8.c | 728 ++++---- drivers/counter/Kconfig | 6 +- drivers/counter/Makefile | 1 + drivers/counter/counter-chrdev.c | 498 ++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 182 ++ drivers/counter/counter-sysfs.c | 953 +++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ----------------- drivers/counter/ftm-quaddec.c | 59 +- drivers/counter/intel-qep.c | 150 +- drivers/counter/interrupt-cnt.c | 73 +- drivers/counter/microchip-tcb-capture.c | 103 +- drivers/counter/stm32-lptimer-cnt.c | 176 +- drivers/counter/stm32-timer-cnt.c | 147 +- drivers/counter/ti-eqep.c | 205 ++- include/linux/counter.h | 716 ++++---- include/linux/counter_enum.h | 45 - include/uapi/linux/counter.h | 133 ++ tools/Makefile | 13 +- tools/counter/Build | 1 + tools/counter/Makefile | 53 + tools/counter/counter_example.c | 95 ++ 27 files changed, 3501 insertions(+), 2767 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h create mode 100644 include/uapi/linux/counter.h create mode 100644 tools/counter/Build create mode 100644 tools/counter/Makefile create mode 100644 tools/counter/counter_example.cbase-commit:
6cbb3aa0f9d5d23221df787cf36f74d3866fdb78