Generic Counter Interface

Introduction

Counter devices are prevalent among a diverse spectrum of industries.The ubiquitous presence of these devices necessitates a common interfaceand standard of interaction and exposure. This driver API attempts toresolve the issue of duplicate code found among existing counter devicedrivers by introducing a generic counter interface for consumption. TheGeneric Counter interface enables drivers to support and expose a commonset of components and functionality present in counter devices.

Theory

Counter devices can vary greatly in design, but regardless of whethersome devices are quadrature encoder counters or tally counters, allcounter devices consist of a core set of components. This core set ofcomponents, shared by all counter devices, is what forms the essence ofthe Generic Counter interface.

There are three core components to a counter:

  • Signal:Stream of data to be evaluated by the counter.

  • Synapse:Association of a Signal, and evaluation trigger, with a Count.

  • Count:Accumulation of the effects of connected Synapses.

SIGNAL

A Signal represents a stream of data. This is the input data that isevaluated by the counter to determine the count data; e.g. a quadraturesignal output line of a rotary encoder. Not all counter devices provideuser access to the Signal data, so exposure is optional for drivers.

When the Signal data is available for user access, the Generic Counterinterface provides the following available signal values:

  • SIGNAL_LOW:Signal line is in a low state.

  • SIGNAL_HIGH:Signal line is in a high state.

A Signal may be associated with one or more Counts.

SYNAPSE

A Synapse represents the association of a Signal with a Count. Signaldata affects respective Count data, and the Synapse represents thisrelationship.

The Synapse action mode specifies the Signal data condition thattriggers the respective Count’s count function evaluation to update thecount data. The Generic Counter interface provides the followingavailable action modes:

  • None:Signal does not trigger the count function. In Pulse-Direction countfunction mode, this Signal is evaluated as Direction.

  • Rising Edge:Low state transitions to high state.

  • Falling Edge:High state transitions to low state.

  • Both Edges:Any state transition.

A counter is defined as a set of input signals associated with countdata that are generated by the evaluation of the state of the associatedinput signals as defined by the respective count functions. Within thecontext of the Generic Counter interface, a counter consists of Countseach associated with a set of Signals, whose respective Synapseinstances represent the count function update conditions for theassociated Counts.

A Synapse associates one Signal with one Count.

COUNT

A Count represents the accumulation of the effects of connectedSynapses; i.e. the count data for a set of Signals. The GenericCounter interface represents the count data as a natural number.

A Count has a count function mode which represents the update behaviorfor the count data. The Generic Counter interface provides the followingavailable count function modes:

  • Increase:Accumulated count is incremented.

  • Decrease:Accumulated count is decremented.

  • Pulse-Direction:Rising edges on signal A updates the respective count. The input levelof signal B determines direction.

  • Quadrature:A pair of quadrature encoding signals are evaluated to determineposition and direction. The following Quadrature modes are available:

    • x1 A:If direction is forward, rising edges on quadrature pair signal Aupdates the respective count; if the direction is backward, fallingedges on quadrature pair signal A updates the respective count.Quadrature encoding determines the direction.

    • x1 B:If direction is forward, rising edges on quadrature pair signal Bupdates the respective count; if the direction is backward, fallingedges on quadrature pair signal B updates the respective count.Quadrature encoding determines the direction.

    • x2 A:Any state transition on quadrature pair signal A updates therespective count. Quadrature encoding determines the direction.

    • x2 B:Any state transition on quadrature pair signal B updates therespective count. Quadrature encoding determines the direction.

    • x4:Any state transition on either quadrature pair signals updates therespective count. Quadrature encoding determines the direction.

A Count has a set of one or more associated Synapses.

Paradigm

The most basic counter device may be expressed as a single Countassociated with a single Signal via a single Synapse. Take for examplea counter device which simply accumulates a count of rising edges on asource input line:

        Count                Synapse        Signal        -----                -------        ------+---------------------+| Data: Count         |    Rising Edge     ________| Function: Increase  |  <-------------   / Source \|                     |                  ____________+---------------------+

In this example, the Signal is a source input line with a pulsingvoltage, while the Count is a persistent count value which is repeatedlyincremented. The Signal is associated with the respective Count via aSynapse. The increase function is triggered by the Signal data conditionspecified by the Synapse -- in this case a rising edge condition on thevoltage input line. In summary, the counter device existence andbehavior is aptly represented by respective Count, Signal, and Synapsecomponents: a rising edge condition triggers an increase function on anaccumulating count datum.

A counter device is not limited to a single Signal; in fact, in theorymany Signals may be associated with even a single Count. For example, aquadrature encoder counter device can keep track of position based onthe states of two input lines:

           Count                 Synapse     Signal           -----                 -------     ------+-------------------------+| Data: Position          |    Both Edges     ___| Function: Quadrature x4 |  <------------   / A \|                         |                 _______|                         ||                         |    Both Edges     ___|                         |  <------------   / B \|                         |                 _______+-------------------------+

In this example, two Signals (quadrature encoder lines A and B) areassociated with a single Count: a rising or falling edge on either A orB triggers the “Quadrature x4” function which determines the directionof movement and updates the respective position data. The “Quadraturex4” function is likely implemented in the hardware of the quadratureencoder counter device; the Count, Signals, and Synapses simplyrepresent this hardware behavior and functionality.

Signals associated with the same Count can have differing Synapse actionmode conditions. For example, a quadrature encoder counter deviceoperating in a non-quadrature Pulse-Direction mode could have one inputline dedicated for movement and a second input line dedicated fordirection:

           Count                   Synapse      Signal           -----                   -------      ------+---------------------------+| Data: Position            |    Rising Edge     ___| Function: Pulse-Direction |  <-------------   / A \ (Movement)|                           |                  _______|                           ||                           |       None         ___|                           |  <-------------   / B \ (Direction)|                           |                  _______+---------------------------+

Only Signal A triggers the “Pulse-Direction” update function, but theinstantaneous state of Signal B is still required in order to know thedirection so that the position data may be properly updated. Ultimately,both Signals are associated with the same Count via two respectiveSynapses, but only one Synapse has an active action mode condition whichtriggers the respective count function while the other is left with a“None” condition action mode to indicate its respective Signal’savailability for state evaluation despite its non-triggering mode.

Keep in mind that the Signal, Synapse, and Count are abstractrepresentations which do not need to be closely married to theirrespective physical sources. This allows the user of a counter todivorce themselves from the nuances of physical components (such aswhether an input line is differential or single-ended) and instead focuson the core idea of what the data and process represent (e.g. positionas interpreted from quadrature encoding data).

Driver API

Driver authors may utilize the Generic Counter interface in their codeby including the include/linux/counter.h header file. This header fileprovides several core data structures, function prototypes, and macrosfor defining a counter device.

structcounter_comp

Counter component node

Definition:

struct counter_comp {    enum counter_comp_type type;    const char *name;    void *priv;    union {        int (*action_read)(struct counter_device *counter, struct counter_count *count, struct counter_synapse *synapse, enum counter_synapse_action *action);        int (*device_u8_read)(struct counter_device *counter, u8 *val);        int (*count_u8_read)(struct counter_device *counter, struct counter_count *count, u8 *val);        int (*signal_u8_read)(struct counter_device *counter, struct counter_signal *signal, u8 *val);        int (*device_u32_read)(struct counter_device *counter, u32 *val);        int (*count_u32_read)(struct counter_device *counter, struct counter_count *count, u32 *val);        int (*signal_u32_read)(struct counter_device *counter, struct counter_signal *signal, u32 *val);        int (*device_u64_read)(struct counter_device *counter, u64 *val);        int (*count_u64_read)(struct counter_device *counter, struct counter_count *count, u64 *val);        int (*signal_u64_read)(struct counter_device *counter, struct counter_signal *signal, u64 *val);        int (*signal_array_u32_read)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u32 *val);        int (*device_array_u64_read)(struct counter_device *counter, size_t idx, u64 *val);        int (*count_array_u64_read)(struct counter_device *counter, struct counter_count *count, size_t idx, u64 *val);        int (*signal_array_u64_read)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u64 *val);    };    union {        int (*action_write)(struct counter_device *counter, struct counter_count *count, struct counter_synapse *synapse, enum counter_synapse_action action);        int (*device_u8_write)(struct counter_device *counter, u8 val);        int (*count_u8_write)(struct counter_device *counter, struct counter_count *count, u8 val);        int (*signal_u8_write)(struct counter_device *counter, struct counter_signal *signal, u8 val);        int (*device_u32_write)(struct counter_device *counter, u32 val);        int (*count_u32_write)(struct counter_device *counter, struct counter_count *count, u32 val);        int (*signal_u32_write)(struct counter_device *counter, struct counter_signal *signal, u32 val);        int (*device_u64_write)(struct counter_device *counter, u64 val);        int (*count_u64_write)(struct counter_device *counter, struct counter_count *count, u64 val);        int (*signal_u64_write)(struct counter_device *counter, struct counter_signal *signal, u64 val);        int (*signal_array_u32_write)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u32 val);        int (*device_array_u64_write)(struct counter_device *counter, size_t idx, u64 val);        int (*count_array_u64_write)(struct counter_device *counter, struct counter_count *count, size_t idx, u64 val);        int (*signal_array_u64_write)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u64 val);    };};

Members

type

Counter component data type

name

device-specific component name

priv

component-relevant data

{unnamed_union}

anonymous

action_read

Synapse action mode read callback. The read value of therespective Synapse action mode should be passed back viathe action parameter.

device_u8_read

Device u8 component read callback. The read value of therespective Device u8 component should be passed back viathe val parameter.

count_u8_read

Count u8 component read callback. The read value of therespective Count u8 component should be passed back viathe val parameter.

signal_u8_read

Signal u8 component read callback. The read value of therespective Signal u8 component should be passed back viathe val parameter.

device_u32_read

Device u32 component read callback. The read value ofthe respective Device u32 component should be passedback via the val parameter.

count_u32_read

Count u32 component read callback. The read value of therespective Count u32 component should be passed back viathe val parameter.

signal_u32_read

Signal u32 component read callback. The read value ofthe respective Signal u32 component should be passedback via the val parameter.

device_u64_read

Device u64 component read callback. The read value ofthe respective Device u64 component should be passedback via the val parameter.

count_u64_read

Count u64 component read callback. The read value of therespective Count u64 component should be passed back viathe val parameter.

signal_u64_read

Signal u64 component read callback. The read value ofthe respective Signal u64 component should be passedback via the val parameter.

signal_array_u32_read

Signal u32 array component read callback. Theindex of the respective Count u32 arraycomponent element is passed via the idxparameter. The read value of the respectiveCount u32 array component element should bepassed back via the val parameter.

device_array_u64_read

Device u64 array component read callback. Theindex of the respective Device u64 arraycomponent element is passed via the idxparameter. The read value of the respectiveDevice u64 array component element should bepassed back via the val parameter.

count_array_u64_read

Count u64 array component read callback. Theindex of the respective Count u64 arraycomponent element is passed via the idxparameter. The read value of the respectiveCount u64 array component element should bepassed back via the val parameter.

signal_array_u64_read

Signal u64 array component read callback. Theindex of the respective Count u64 arraycomponent element is passed via the idxparameter. The read value of the respectiveCount u64 array component element should bepassed back via the val parameter.

{unnamed_union}

anonymous

action_write

Synapse action mode write callback. The write value ofthe respective Synapse action mode is passed via theaction parameter.

device_u8_write

Device u8 component write callback. The write value ofthe respective Device u8 component is passed via the valparameter.

count_u8_write

Count u8 component write callback. The write value ofthe respective Count u8 component is passed via the valparameter.

signal_u8_write

Signal u8 component write callback. The write value ofthe respective Signal u8 component is passed via the valparameter.

device_u32_write

Device u32 component write callback. The write value ofthe respective Device u32 component is passed via theval parameter.

count_u32_write

Count u32 component write callback. The write value ofthe respective Count u32 component is passed via the valparameter.

signal_u32_write

Signal u32 component write callback. The write value ofthe respective Signal u32 component is passed via theval parameter.

device_u64_write

Device u64 component write callback. The write value ofthe respective Device u64 component is passed via theval parameter.

count_u64_write

Count u64 component write callback. The write value ofthe respective Count u64 component is passed via the valparameter.

signal_u64_write

Signal u64 component write callback. The write value ofthe respective Signal u64 component is passed via theval parameter.

signal_array_u32_write

Signal u32 array component write callback. Theindex of the respective Signal u32 arraycomponent element is passed via the idxparameter. The write value of the respectiveSignal u32 array component element is passed viathe val parameter.

device_array_u64_write

Device u64 array component write callback. Theindex of the respective Device u64 arraycomponent element is passed via the idxparameter. The write value of the respectiveDevice u64 array component element is passed viathe val parameter.

count_array_u64_write

Count u64 array component write callback. Theindex of the respective Count u64 arraycomponent element is passed via the idxparameter. The write value of the respectiveCount u64 array component element is passed viathe val parameter.

signal_array_u64_write

Signal u64 array component write callback. Theindex of the respective Signal u64 arraycomponent element is passed via the idxparameter. The write value of the respectiveSignal u64 array component element is passed viathe val parameter.

structcounter_signal

Counter Signal node

Definition:

struct counter_signal {    int id;    const char *name;    struct counter_comp *ext;    size_t num_ext;};

Members

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 inext

structcounter_synapse

Counter Synapse node

Definition:

struct counter_synapse {    const enum counter_synapse_action *actions_list;    size_t num_actions;    struct counter_signal *signal;};

Members

actions_list

array of available action modes

num_actions

number of action modes specified inactions_list

signal

pointer to the associated Signal

structcounter_count

Counter Count node

Definition:

struct counter_count {    int id;    const char *name;    const enum counter_function *functions_list;    size_t num_functions;    struct counter_synapse *synapses;    size_t num_synapses;    struct counter_comp *ext;    size_t num_ext;};

Members

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 infunctions_list

synapses

array of Synapses for initialization

num_synapses

number of Synapses specified insynapses

ext

optional array of Count extensions

num_ext

number of Count extensions specified inext

structcounter_event_node

Counter Event node

Definition:

struct counter_event_node {    struct list_head l;    u8 event;    u8 channel;    struct list_head comp_list;};

Members

l

list of current watching Counter events

event

event that triggers

channel

event channel

comp_list

list of components to watch when event triggers

structcounter_ops

Callbacks from driver

Definition:

struct counter_ops {    int (*signal_read)(struct counter_device *counter, struct counter_signal *signal, enum counter_signal_level *level);    int (*count_read)(struct counter_device *counter, struct counter_count *count, u64 *value);    int (*count_write)(struct counter_device *counter, struct counter_count *count, u64 value);    int (*function_read)(struct counter_device *counter, struct counter_count *count, enum counter_function *function);    int (*function_write)(struct counter_device *counter, struct counter_count *count, enum counter_function function);    int (*action_read)(struct counter_device *counter, struct counter_count *count, struct counter_synapse *synapse, enum counter_synapse_action *action);    int (*action_write)(struct counter_device *counter, 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);};

Members

signal_read

optional read callback for Signals. The read level ofthe respective Signal should be passed back via thelevel parameter.

count_read

read callback for Counts. The read value of therespective Count should be passed back via the valueparameter.

count_write

optional write callback for Counts. The write value forthe respective Count is passed in via the valueparameter.

function_read

read callback the Count function modes. The readfunction mode of the respective Count should be passedback via the function parameter.

function_write

optional write callback for Count function modes. Thefunction mode to write for the respective Count ispassed in via the function parameter.

action_read

optional read callback the Synapse action modes. Theread action mode of the respective Synapse should bepassed back via the action parameter.

action_write

optional write callback for Synapse action modes. Theaction mode to write for the respective Synapse ispassed in via the action parameter.

events_configure

optional write callback to configure events. The list ofstructcounter_event_node may be accessed via theevents_list member of the counter parameter.

watch_validate

optional callback to validate a watch. The Countercomponent watch configuration is passed in via the watchparameter. A return value of 0 indicates a valid Countercomponent watch configuration.

structcounter_device

Counter data structure

Definition:

struct counter_device {    const char *name;    struct device *parent;    const struct counter_ops *ops;    struct counter_signal *signals;    size_t num_signals;    struct counter_count *counts;    size_t num_counts;    struct counter_comp *ext;    size_t num_ext;    struct device dev;    struct cdev chrdev;    struct list_head events_list;    spinlock_t events_list_lock;    struct list_head next_events_list;    struct mutex n_events_list_lock;    struct counter_event *events;    wait_queue_head_t events_wait;    spinlock_t events_in_lock;    struct mutex events_out_lock;    struct mutex ops_exist_lock;};

Members

name

name of the device

parent

optional parent device providing the counters

ops

callbacks from driver

signals

array of Signals

num_signals

number of Signals specified insignals

counts

array of Counts

num_counts

number of Counts specified incounts

ext

optional array of Counter device extensions

num_ext

number of Counter device extensions specified inext

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

n_events_list_lock

lock to protect Counter next events list operations

events

queue of detected Counter events

events_wait

wait queue to allow blocking reads of Counter events

events_in_lock

lock to protect Counter events queue in operations

events_out_lock

lock to protect Counter events queue out operations

ops_exist_lock

lock to prevent use during removal

void*counter_priv(conststructcounter_device*constcounter)

access counter device private data

Parameters

conststructcounter_device*constcounter

counter device

Description

Get the counter device private data

structcounter_device*counter_alloc(size_tsizeof_priv)

allocate a counter_device

Parameters

size_tsizeof_priv

size of the driver private data

Description

This is part one of counter registration. The structure is allocateddynamically to ensure the right lifetime for the embeddedstructdevice.

If this succeeds, callcounter_put() to get rid of the counter_device again.

intcounter_add(structcounter_device*counter)

complete registration of a counter

Parameters

structcounter_device*counter

the counter to add

Description

This is part two of counter registration.

If this succeeds, callcounter_unregister() to get rid of the counter_device again.

voidcounter_unregister(structcounter_device*constcounter)

unregister Counter from the system

Parameters

structcounter_device*constcounter

pointer to Counter to unregister

Description

The Counter is unregistered from the system.

structcounter_device*devm_counter_alloc(structdevice*dev,size_tsizeof_priv)

allocate a counter_device

Parameters

structdevice*dev

the device to register the release callback for

size_tsizeof_priv

size of the driver private data

Description

This is the device managed version ofcounter_add(). It registers a cleanupcallback to care for callingcounter_put().

intdevm_counter_add(structdevice*dev,structcounter_device*constcounter)

complete registration of a counter

Parameters

structdevice*dev

the device to register the release callback for

structcounter_device*constcounter

the counter to add

Description

This is the device managed version ofcounter_add(). It registers a cleanupcallback to care for callingcounter_unregister().

voidcounter_push_event(structcounter_device*constcounter,constu8event,constu8channel)

queue event for userspace reading

Parameters

structcounter_device*constcounter

pointer to Counter structure

constu8event

triggered event

constu8channel

event channel

Note

If no one is watching for the respective event, it is silentlydiscarded.

Driver Implementation

To support a counter device, a driver must first allocate the availableCounter Signals via counter_signal structures. These Signals shouldbe stored as an array and set to the signals array member of anallocated counter_device structure before the Counter is registered tothe system.

Counter Counts may be allocated via counter_count structures, andrespective Counter Signal associations (Synapses) made viacounter_synapse structures. Associated counter_synapse structures arestored as an array and set to the synapses array member of therespective counter_count structure. These counter_count structures areset to the counts array member of an allocated counter_device structurebefore the Counter is registered to the system.

Driver callbacks must be provided to the counter_device structure inorder to communicate with the device: to read and write various Signalsand Counts, and to set and get the “action mode” and “function mode” forvarious Synapses and Counts respectively.

A counter_device structure is allocated usingcounter_alloc() and thenregistered to the system by passing it to thecounter_add() function, andunregistered by passing it to the counter_unregister function. There aredevice managed variants of these functions:devm_counter_alloc() anddevm_counter_add().

Thestructcounter_comp structure is used to define counter extensionsfor Signals, Synapses, and Counts.

The “type” member specifies the type of high-level data (e.g. BOOL,COUNT_DIRECTION, etc.) handled by this extension. The “*_read” and“*_write” members can then be set by the counter device driver withcallbacks to handle that data using native C data types (i.e. u8, u64,etc.).

Convenience macros such asCOUNTER_COMP_COUNT_U64 are provided foruse by driver authors. In particular, driver authors are expected to usethe provided macros for standard Counter subsystem attributes in orderto maintain a consistent interface for userspace. For example, a counterdevice driver may define several standard attributes like so:

struct counter_comp count_ext[] = {        COUNTER_COMP_DIRECTION(count_direction_read),        COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),        COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),};

This makes it simple to see, add, and modify the attributes that aresupported by this driver (“direction”, “enable”, and “ceiling”) and tomaintain this code without getting lost in a web ofstructbraces.

Callbacks must match the function type expected for the respectivecomponent or extension. These function types are defined in thestructcounter_comp structure as the “*_read” and “*_writeunionmembers.

The corresponding callback prototypes for the extensions mentioned inthe previous example above would be:

int count_direction_read(struct counter_device *counter,                         struct counter_count *count,                         enum counter_count_direction *direction);int count_enable_read(struct counter_device *counter,                      struct counter_count *count, u8 *enable);int count_enable_write(struct counter_device *counter,                       struct counter_count *count, u8 enable);int count_ceiling_read(struct counter_device *counter,                       struct counter_count *count, u64 *ceiling);int count_ceiling_write(struct counter_device *counter,                        struct counter_count *count, u64 ceiling);

Determining the type of extension to create is a matter of scope.

  • Signal extensions are attributes that expose information/controlspecific to a Signal. These types of attributes will exist under aSignal’s directory in sysfs.

    For example, if you have an invert feature for a Signal, you can havea Signal extension called “invert” that toggles that feature:/sys/bus/counter/devices/counterX/signalY/invert

  • Count extensions are attributes that expose information/controlspecific to a Count. These type of attributes will exist under aCount’s directory in sysfs.

    For example, if you want to pause/unpause a Count from updating, youcan have a Count extension called “enable” that toggles such:/sys/bus/counter/devices/counterX/countY/enable

  • Device extensions are attributes that expose information/controlnon-specific to a particular Count or Signal. This is where you wouldput your global features or other miscellaneous functionality.

    For example, if your device has an overtemp sensor, you can report thechip overheated via a device extension called “error_overtemp”:/sys/bus/counter/devices/counterX/error_overtemp

Subsystem Architecture

Counter drivers pass and take data natively (i.e.u8,u64, etc.)and the shared counter module handles the translation between the sysfsinterface. This guarantees a standard userspace interface for allcounter drivers, and enables a Generic Counter chrdev interface via ageneralized device driver ABI.

A high-level view of how a count value is passed down from a counterdriver is exemplified by the following. The driver callbacks are firstregistered to the Counter core component for use by the Counteruserspace interface components:

Driver callbacks registration:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                +----------------------------+                | Counter device driver      |                +----------------------------+                | Processes data from device |                +----------------------------+                        |                 -------------------                / driver callbacks /                -------------------                        |                        V                +----------------------+                | Counter core         |                +----------------------+                | Routes device driver |                | callbacks to the     |                | userspace interfaces |                +----------------------+                        |                 -------------------                / driver callbacks /                -------------------                        |        +---------------+---------------+        |                               |        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 devicedriver and Counter userspace interface:

Count data request:~~~~~~~~~~~~~~~~~~~                 ----------------------                / Counter device       \                +----------------------+                | Count register: 0x28 |                +----------------------+                        |                 -----------------                / raw count data /                -----------------                        |                        V                +----------------------------+                | Counter device driver      |                +----------------------------+                | Processes data from device |                |----------------------------|                | Type: u64                  |                | Value: 42                  |                +----------------------------+                        |                 ----------                / u64     /                ----------                        |        +---------------+---------------+        |                               |        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+--------------------------------------------------+| `/sys/bus/counter/devices/counterX/countY/count` |+--------------------------------------------------+\ Count: "42"                                      / --------------------------------------------------

There are four primary components involved:

Counter device driver

Communicates with the hardware device to read/write data; e.g. counterdrivers for quadrature encoders, timers, etc.

Counter core

Registers the counter device driver to the system so that the respectivecallbacks are called during userspace interaction.

Counter sysfs

Translates counter data to the standard Counter sysfs interface formatand vice versa.

Please refer to theABI file testing/sysfs-bus-counter filefor a detailed breakdown of the available Generic Counter interfacesysfs attributes.

Counter chrdev

Translates Counter events to the standard Counter character device; datais transferred via standard character device read calls, while Counterevents 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,whereX is to the respective counter device id. Please seeABI file testing/sysfs-bus-counter for detailed informationon each Generic Counter interface sysfs attribute.

Through these sysfs attributes, programs and scripts may interact withthe Generic Counter paradigm Counts, Signals, and Synapses of respectivecounter devices.

Counter Character Device

Counter character device nodes are created under the/dev directoryascounterX, whereX is the respective counter device id.Defines for the standard Counter data types are exposed via theuserspaceinclude/uapi/linux/counter.h file.

Counter events

Counter device drivers can support Counter events by utilizing thecounter_push_event function:

void counter_push_event(struct counter_device *const counter, const u8 event,                        const u8 channel);

The event id is specified by theevent parameter; the event channelid is specified by thechannel parameter. When this function iscalled, the Counter data associated with the respective event isgathered, and astructcounter_event is generated for each datum andpushed to userspace.

Counter events can be configured by users to report various Counterdata of interest. This can be conceptualized as a list of Countercomponent read calls to perform. For example:

COUNTER_EVENT_OVERFLOW

COUNTER_EVENT_INDEX

Channel 0

Channel 0

  • Count 0

  • Count 1

  • Signal 3

  • Count 4 Extension 2

  • Signal 5 Extension 0

  • Signal 0

  • Signal 0 Extension 0

  • Extension 4

Channel 1

  • Signal 4

  • Signal 4 Extension 0

  • Count 7

Whencounter_push_event(counter,COUNTER_EVENT_INDEX,1) is calledfor example, it will go down the list for theCOUNTER_EVENT_INDEXevent channel 1 and execute the read callbacks for Signal 4, Signal 4Extension 0, and Count 7 -- the data returned for each is pushed to akfifo as astructcounter_event, which userspace can retrieve via astandard read operation on the respective character device node.

Userspace

Userspace applications can configure Counter events via ioctl operationson the Counter character device node. There following ioctl codes aresupported and provided by thelinux/counter.h userspace header file:

  • COUNTER_ADD_WATCH_IOCTL

  • COUNTER_ENABLE_EVENTS_IOCTL

  • COUNTER_DISABLE_EVENTS_IOCTL

To configure events to gather Counter data, users first populate astructcounter_watch with the relevant event id, event channel id,and the information for the desired Counter component from which toread, and then pass it via theCOUNTER_ADD_WATCH_IOCTL ioctlcommand.

Note that an event can be watched without gathering Counter data bysetting thecomponent.type member equal toCOUNTER_COMPONENT_NONE. With this configuration the Countercharacter device will simply populate the event timestamps for thoserespectivestructcounter_event elements and ignore the componentvalue.

TheCOUNTER_ADD_WATCH_IOCTL command will buffer these Counterwatches. When ready, theCOUNTER_ENABLE_EVENTS_IOCTL ioctl commandmay be used to activate these Counter watches.

Userspace applications can then execute aread operation (optionallycallingpoll first) on the Counter character device node to retrievestructcounter_event elements with the desired data.