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
typeCounter component data type
namedevice-specific component name
privcomponent-relevant data
{unnamed_union}anonymous
action_readSynapse action mode read callback. The read value of therespective Synapse action mode should be passed back viathe action parameter.
device_u8_readDevice u8 component read callback. The read value of therespective Device u8 component should be passed back viathe val parameter.
count_u8_readCount u8 component read callback. The read value of therespective Count u8 component should be passed back viathe val parameter.
signal_u8_readSignal u8 component read callback. The read value of therespective Signal u8 component should be passed back viathe val parameter.
device_u32_readDevice u32 component read callback. The read value ofthe respective Device u32 component should be passedback via the val parameter.
count_u32_readCount u32 component read callback. The read value of therespective Count u32 component should be passed back viathe val parameter.
signal_u32_readSignal u32 component read callback. The read value ofthe respective Signal u32 component should be passedback via the val parameter.
device_u64_readDevice u64 component read callback. The read value ofthe respective Device u64 component should be passedback via the val parameter.
count_u64_readCount u64 component read callback. The read value of therespective Count u64 component should be passed back viathe val parameter.
signal_u64_readSignal u64 component read callback. The read value ofthe respective Signal u64 component should be passedback via the val parameter.
signal_array_u32_readSignal 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_readDevice 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_readCount 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_readSignal 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_writeSynapse action mode write callback. The write value ofthe respective Synapse action mode is passed via theaction parameter.
device_u8_writeDevice u8 component write callback. The write value ofthe respective Device u8 component is passed via the valparameter.
count_u8_writeCount u8 component write callback. The write value ofthe respective Count u8 component is passed via the valparameter.
signal_u8_writeSignal u8 component write callback. The write value ofthe respective Signal u8 component is passed via the valparameter.
device_u32_writeDevice u32 component write callback. The write value ofthe respective Device u32 component is passed via theval parameter.
count_u32_writeCount u32 component write callback. The write value ofthe respective Count u32 component is passed via the valparameter.
signal_u32_writeSignal u32 component write callback. The write value ofthe respective Signal u32 component is passed via theval parameter.
device_u64_writeDevice u64 component write callback. The write value ofthe respective Device u64 component is passed via theval parameter.
count_u64_writeCount u64 component write callback. The write value ofthe respective Count u64 component is passed via the valparameter.
signal_u64_writeSignal u64 component write callback. The write value ofthe respective Signal u64 component is passed via theval parameter.
signal_array_u32_writeSignal 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_writeDevice 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_writeCount 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_writeSignal 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
idunique ID used to identify the Signal
namedevice-specific Signal name
extoptional array of Signal extensions
num_extnumber 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_listarray of available action modes
num_actionsnumber of action modes specified inactions_list
signalpointer 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
idunique ID used to identify the Count
namedevice-specific Count name
functions_listarray of available function modes
num_functionsnumber of function modes specified infunctions_list
synapsesarray of Synapses for initialization
num_synapsesnumber of Synapses specified insynapses
extoptional array of Count extensions
num_extnumber 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
llist of current watching Counter events
eventevent that triggers
channelevent channel
comp_listlist 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_readoptional read callback for Signals. The read level ofthe respective Signal should be passed back via thelevel parameter.
count_readread callback for Counts. The read value of therespective Count should be passed back via the valueparameter.
count_writeoptional write callback for Counts. The write value forthe respective Count is passed in via the valueparameter.
function_readread callback the Count function modes. The readfunction mode of the respective Count should be passedback via the function parameter.
function_writeoptional write callback for Count function modes. Thefunction mode to write for the respective Count ispassed in via the function parameter.
action_readoptional read callback the Synapse action modes. Theread action mode of the respective Synapse should bepassed back via the action parameter.
action_writeoptional write callback for Synapse action modes. Theaction mode to write for the respective Synapse ispassed in via the action parameter.
events_configureoptional write callback to configure events. The list of
structcounter_event_nodemay be accessed via theevents_list member of the counter parameter.watch_validateoptional 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
namename of the device
parentoptional parent device providing the counters
opscallbacks from driver
signalsarray of Signals
num_signalsnumber of Signals specified insignals
countsarray of Counts
num_countsnumber of Counts specified incounts
extoptional array of Counter device extensions
num_extnumber of Counter device extensions specified inext
devinternal device structure
chrdevinternal character device structure
events_listlist of current watching Counter events
events_list_locklock to protect Counter events list operations
next_events_listlist of next watching Counter events
n_events_list_locklock to protect Counter next events list operations
eventsqueue of detected Counter events
events_waitwait queue to allow blocking reads of Counter events
events_in_locklock to protect Counter events queue in operations
events_out_locklock to protect Counter events queue out operations
ops_exist_locklock to prevent use during removal
- void*counter_priv(conststructcounter_device*constcounter)¶
access counter device private data
Parameters
conststructcounter_device*constcountercounter device
Description
Get the counter device private data
- structcounter_device*counter_alloc(size_tsizeof_priv)¶
allocate a counter_device
Parameters
size_tsizeof_privsize 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*counterthe 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*constcounterpointer 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*devthe device to register the release callback for
size_tsizeof_privsize 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*devthe device to register the release callback for
structcounter_device*constcounterthe 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*constcounterpointer to Counter structure
constu8eventtriggered event
constu8channelevent 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 “*_write”unionmembers.
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_IOCTLCOUNTER_ENABLE_EVENTS_IOCTLCOUNTER_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.