요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=========================
Generic Counter Interface
=========================
Introduction
============
Counter devices are prevalent among a diverse spectrum of industries.
The ubiquitous presence of these devices necessitates a common interface
and standard of interaction and exposure. This driver API attempts to
resolve the issue of duplicate code found among existing counter device
drivers by introducing a generic counter interface for consumption. The
Generic Counter interface enables drivers to support and expose a common
set of components and functionality present in counter devices.
Theory
======
Counter devices can vary greatly in design, but regardless of whether
some devices are quadrature encoder counters or tally counters, all
counter devices consist of a core set of components. This core set of
components, shared by all counter devices, is what forms the essence of
the Generic Counter interface.
There are three core components to a counter:
* 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 is
evaluated by the counter to determine the count data; e.g. a quadrature
signal output line of a rotary encoder. Not all counter devices provide
user access to the Signal data, so exposure is optional for drivers.
When the Signal data is available for user access, the Generic Counter
interface 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. Signal
data affects respective Count data, and the Synapse represents this
relationship.
The Synapse action mode specifies the Signal data condition that
triggers the respective Count's count function evaluation to update the
count data. The Generic Counter interface provides the following
available action modes:
* None:
Signal does not trigger the count function. In Pulse-Direction count
function 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 count
data that are generated by the evaluation of the state of the associated
input signals as defined by the respective count functions. Within the
context of the Generic Counter interface, a counter consists of Counts
each associated with a set of Signals, whose respective Synapse
instances represent the count function update conditions for the
associated Counts.
A Synapse associates one Signal with one Count.
COUNT
-----
A Count represents the accumulation of the effects of connected
Synapses; i.e. the count data for a set of Signals. The Generic
Counter interface represents the count data as a natural number.
A Count has a count function mode which represents the update behavior
for the count data. The Generic Counter interface provides the following
available 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 level
of signal B determines direction.
* Quadrature:
A pair of quadrature encoding signals are evaluated to determine
position and direction. The following Quadrature modes are available:
- x1 A:
If direction is forward, rising edges on quadrature pair signal A
updates the respective count; if the direction is backward, falling
edges 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 B
updates the respective count; if the direction is backward, falling
edges 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 the
respective count. Quadrature encoding determines the direction.
- x2 B:
Any state transition on quadrature pair signal B updates the
respective count. Quadrature encoding determines the direction.
- x4:
Any state transition on either quadrature pair signals updates the
respective 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 Count
associated with a single Signal via a single Synapse. Take for example
a counter device which simply accumulates a count of rising edges on a
source input line::
Count Synapse Signal
----- ------- ------
+---------------------+
| Data: Count | Rising Edge ________
| Function: Increase | <------------- / Source \
| | ____________
+---------------------+
In this example, the Signal is a source input line with a pulsing
voltage, while the Count is a persistent count value which is repeatedly
incremented. The Signal is associated with the respective Count via a
Synapse. The increase function is triggered by the Signal data condition
specified by the Synapse -- in this case a rising edge condition on the
voltage input line. In summary, the counter device existence and
behavior is aptly represented by respective Count, Signal, and Synapse
components: a rising edge condition triggers an increase function on an
accumulating count datum.
A counter device is not limited to a single Signal; in fact, in theory
many Signals may be associated with even a single Count. For example, a
quadrature encoder counter device can keep track of position based on
the 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) are
associated with a single Count: a rising or falling edge on either A or
B triggers the "Quadrature x4" function which determines the direction
of movement and updates the respective position data. The "Quadrature
x4" function is likely implemented in the hardware of the quadrature
encoder counter device; the Count, Signals, and Synapses simply
represent this hardware behavior and functionality.
Signals associated with the same Count can have differing Synapse action
mode conditions. For example, a quadrature encoder counter device
operating in a non-quadrature Pulse-Direction mode could have one input
line dedicated for movement and a second input line dedicated for
direction::
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 the
instantaneous state of Signal B is still required in order to know the
direction so that the position data may be properly updated. Ultimately,
both Signals are associated with the same Count via two respective
Synapses, but only one Synapse has an active action mode condition which
triggers the respective count function while the other is left with a
"None" condition action mode to indicate its respective Signal's
availability for state evaluation despite its non-triggering mode.
Keep in mind that the Signal, Synapse, and Count are abstract
representations which do not need to be closely married to their
respective physical sources. This allows the user of a counter to
divorce themselves from the nuances of physical components (such as
whether an input line is differential or single-ended) and instead focus
on the core idea of what the data and process represent (e.g. position
as interpreted from quadrature encoding data).
Driver API
==========
Driver authors may utilize the Generic Counter interface in their code
by including the include/linux/counter.h header file. This header file
provides several core data structures, function prototypes, and macros
for defining a counter device.
.. kernel-doc:: include/linux/counter.h
:internal:
.. kernel-doc:: drivers/counter/counter-core.c
:export:
.. kernel-doc:: drivers/counter/counter-chrdev.c
:export:
Driver Implementation
=====================
To support a counter device, a driver must first allocate the available
Counter Signals via counter_signal structures. These Signals should
be stored as an array and set to the signals array member of an
allocated counter_device structure before the Counter is registered to
the system.
Counter Counts may be allocated via counter_count structures, and
respective Counter Signal associations (Synapses) made via
counter_synapse structures. Associated counter_synapse structures are
stored as an array and set to the synapses array member of the
respective counter_count structure. These counter_count structures are
set to the counts array member of an allocated counter_device structure
before the Counter is registered to the system.
Driver callbacks must be provided to the counter_device structure in
order to communicate with the device: to read and write various Signals
and Counts, and to set and get the "action mode" and "function mode" for
various Synapses and Counts respectively.
A counter_device structure is allocated using counter_alloc() and then
registered to the system by passing it to the counter_add() function, and
unregistered by passing it to the counter_unregister function. There are
device managed variants of these functions: devm_counter_alloc() and
devm_counter_add().
The struct counter_comp structure is used to define counter extensions
for 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 with
callbacks to handle that data using native C data types (i.e. u8, u64,
etc.).
Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for
use by driver authors. In particular, driver authors are expected to use
the provided macros for standard Counter subsystem attributes in order
to maintain a consistent interface for userspace. For example, a counter
device 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 are
supported by this driver ("direction", "enable", and "ceiling") and to
maintain this code without getting lost in a web of struct braces.
Callbacks must match the function type expected for the respective
component or extension. These function types are defined in the struct
counter_comp structure as the "``*_read``" and "``*_write``" union
members.
The corresponding callback prototypes for the extensions mentioned in
the 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/control
specific to a Signal. These types of attributes will exist under a
Signal's directory in sysfs.
For example, if you have an invert feature for a Signal, you can have
a Signal extension called "invert" that toggles that feature:
/sys/bus/counter/devices/counterX/signalY/invert
* Count extensions are attributes that expose information/control
specific to a Count. These type of attributes will exist under a
Count's directory in sysfs.
For example, if you want to pause/unpause a Count from updating, you
can have a Count extension called "enable" that toggles such:
/sys/bus/counter/devices/counterX/countY/enable
* Device extensions are attributes that expose information/control
non-specific to a particular Count or Signal. This is where you would
put your global features or other miscellaneous functionality.
For example, if your device has an overtemp sensor, you can report the
chip 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 sysfs
interface. This guarantees a standard userspace interface for all
counter drivers, and enables a Generic Counter chrdev interface via a
generalized device driver ABI.
A high-level view of how a count value is passed down from a counter
driver is exemplified by the following. The driver callbacks are first
registered to the Counter core component for use by the Counter
userspace 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 device
driver 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. counter
drivers for quadrature encoders, timers, etc.
Counter core
------------
Registers the counter device driver to the system so that the respective
callbacks are called during userspace interaction.
Counter sysfs
-------------
Translates counter data to the standard Counter sysfs interface format
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 events 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 7 -- 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:
* :c:macro:`COUNTER_ADD_WATCH_IOCTL`
* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL`
* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL`
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.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Generic Counter Interface 소개
1-17이 문서는 `GPL-2.0` SPDX license를 사용하며 제목은 `Generic Counter Interface`입니다.
Counter device는 다양한 산업 영역에서 널리 사용되므로 공통 interface와 표준화된 interaction·exposure 방식이 필요합니다. 이 driver API는 기존 counter driver 사이의 중복 code를 줄이기 위해 generic counter interface를 제공합니다.
Generic Counter interface를 사용하면 driver가 counter device에 공통적으로 존재하는 component와 기능 집합을 지원하고 노출할 수 있습니다.
Counter의 세 핵심 component
18-37Counter device는 quadrature encoder counter나 tally counter 등 설계가 크게 다를 수 있지만 모두 공통 핵심 component를 가집니다. 이 공통 집합이 Generic Counter interface의 본질입니다.
- `Signal`: counter가 평가할 data stream
- `Synapse`: Signal과 Count를 연결하고 평가 trigger를 지정하는 association
- `Count`: 연결된 Synapse의 효과를 누적한 값
Signal condition이 Synapse를 통해 Count update function을 trigger합니다.
SIGNAL
38-55Signal은 counter가 count data를 결정하기 위해 평가하는 input data stream입니다. 예를 들면 rotary encoder의 quadrature signal output line입니다. 모든 counter device가 Signal data에 대한 user access를 제공하지는 않으므로 driver의 Signal 노출은 선택 사항입니다.
Signal data를 user에게 제공할 때 Generic Counter interface는 두 값을 정의합니다. `SIGNAL_LOW`는 signal line이 low state임을, `SIGNAL_HIGH`는 high state임을 뜻합니다.
한 Signal은 하나 이상의 Count와 연결될 수 있습니다.
User access가 가능한 Signal line의 논리 상태입니다.
SYNAPSE
56-89Synapse는 Signal과 Count의 연결을 나타냅니다. Signal data가 해당 Count data에 미치는 관계를 표현합니다.
Synapse action mode는 연결된 Count의 count function 평가를 trigger해 count data를 갱신할 Signal condition을 지정합니다.
`None`에서는 Signal이 count function을 trigger하지 않습니다. 다만 Pulse-Direction count function mode에서는 이 Signal을 Direction으로 평가합니다. `Rising Edge`는 low에서 high로, `Falling Edge`는 high에서 low로 바뀌는 전이이며, `Both Edges`는 모든 state transition입니다.
Counter는 연결된 input Signal의 state를 각 count function 정의에 따라 평가해 생성한 Count data의 집합입니다. 각 Count에 연결된 Signal과 Synapse instance가 count function update condition을 나타냅니다. 하나의 Synapse는 정확히 하나의 Signal과 하나의 Count를 연결합니다.
Count function을 실행시키는 Signal condition입니다.
COUNT
90-139Count는 연결된 Synapse 효과의 누적, 즉 Signal 집합에 대한 count data를 나타냅니다. Generic Counter interface는 count data를 natural number로 표현합니다.
Count의 count function mode는 count data를 갱신하는 동작을 정의합니다. `Increase`는 누적 count를 증가시키고 `Decrease`는 감소시킵니다.
`Pulse-Direction`에서는 Signal A의 rising edge가 Count를 갱신하고 Signal B의 input level이 방향을 결정합니다.
`Quadrature`는 quadrature encoding Signal pair를 평가해 position과 direction을 결정합니다. `x1 A`와 `x1 B`는 진행 방향에 따라 해당 Signal의 rising 또는 falling edge에서 갱신합니다. `x2 A`와 `x2 B`는 해당 Signal의 모든 transition에서 갱신하며, `x4`는 pair의 어느 Signal에서든 발생한 모든 transition에서 갱신합니다. 모든 mode에서 quadrature encoding이 방향을 결정합니다.
하나의 Count에는 하나 이상의 Synapse 집합이 연결됩니다.
Count data update 방식과 사용하는 Signal edge입니다.
Counter paradigm 예제
140-225가장 기본적인 counter device는 하나의 Synapse를 통해 하나의 Signal에 연결된 하나의 Count로 표현할 수 있습니다. Source input line의 rising edge를 누적하는 경우, pulsing voltage Signal의 rising edge가 Synapse condition을 만족하면 `Increase` function이 persistent Count를 증가시킵니다.
원문의 단일 Signal·Synapse·Count ASCII 그림을 구조화했습니다.
Counter device는 하나의 Signal에 제한되지 않습니다. Quadrature encoder에서는 Signal A와 B가 하나의 position Count에 연결되고, 어느 Signal의 rising 또는 falling edge든 `Quadrature x4` function을 trigger해 이동 방향을 결정하고 position을 갱신합니다. 이 function은 보통 hardware에 구현되며 Count·Signal·Synapse는 그 동작을 추상화합니다.
원문의 두 Signal quadrature ASCII 그림을 표로 재구성했습니다.
같은 Count에 연결된 Signal은 서로 다른 Synapse action condition을 가질 수 있습니다. Non-quadrature Pulse-Direction mode에서는 Signal A가 movement pulse를 담당해 rising edge로 update function을 trigger하고, Signal B는 direction state만 제공합니다. B의 action mode는 `None`이지만 방향 평가를 위해 state를 읽을 수 있습니다.
원문의 movement·direction Signal ASCII 그림을 표로 재구성했습니다.
Signal, Synapse, Count는 물리 source와 반드시 밀접하게 대응할 필요가 없는 추상 표현입니다. 사용자는 input line이 differential인지 single-ended인지 같은 물리 세부 사항에서 벗어나 quadrature data에서 해석한 position처럼 data와 process의 핵심 의미에 집중할 수 있습니다.
Driver API
226-242Driver 작성자는 `include/linux/counter.h`를 include해 Generic Counter interface를 사용할 수 있습니다. 이 header는 counter device 정의에 필요한 핵심 data structure, function prototype과 macro를 제공합니다.
.. kernel-doc:: include/linux/counter.h
:internal:
Core registration function 문서는 `drivers/counter/counter-core.c`에서 export된 항목을 가져옵니다.
.. kernel-doc:: drivers/counter/counter-core.c
:export:
Character device function 문서는 `drivers/counter/counter-chrdev.c`에서 export된 항목을 가져옵니다.
.. kernel-doc:: drivers/counter/counter-chrdev.c
:export:
Counter driver 구현
243-315Counter device를 지원하려면 먼저 사용 가능한 Signal을 `counter_signal` structure로 할당해 array에 저장하고, system 등록 전에 할당한 `counter_device`의 `signals` array member에 설정합니다.
Count는 `counter_count` structure로 할당합니다. Signal과 Count의 association인 Synapse는 `counter_synapse` structure로 만들고 array에 저장해 해당 `counter_count`의 `synapses` member에 설정합니다. 완성한 `counter_count` array는 등록 전에 `counter_device.counts`에 연결합니다.
`counter_device`에는 device와 통신하는 callback을 제공해야 합니다. Callback은 Signal·Count를 읽고 쓰며, Synapse의 action mode와 Count의 function mode를 설정하고 조회합니다.
`counter_alloc()`으로 `counter_device`를 할당하고 `counter_add()`로 등록하며 `counter_unregister()`로 해제합니다. Device-managed variant는 `devm_counter_alloc()`과 `devm_counter_add()`입니다.
`struct counter_comp`는 Signal, Synapse, Count의 extension을 정의합니다. `type` member는 `BOOL`, `COUNT_DIRECTION` 같은 high-level data type을 지정하며, `*_read`와 `*_write` callback은 `u8`, `u64` 같은 native C type으로 data를 처리합니다.
Driver 작성자는 userspace interface의 일관성을 위해 `COUNTER_COMP_COUNT_U64` 같은 convenience macro와 표준 Counter attribute macro를 사용해야 합니다. 다음 예제는 `direction`, `enable`, `ceiling` attribute를 정의합니다.
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),
};
이 방식은 복잡한 structure brace에 묻히지 않고 driver가 지원하는 attribute를 확인·추가·수정하기 쉽게 합니다. Callback은 `struct counter_comp`의 `*_read`, `*_write` union member가 요구하는 function type과 일치해야 합니다.
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);
Signal·Synapse·Count를 조립해 counter_device를 등록하는 순서입니다.
Counter extension scope
316-341어떤 extension type을 만들지는 attribute의 scope로 결정합니다.
Signal extension은 특정 Signal의 정보나 제어를 노출하며 해당 Signal sysfs directory 아래에 존재합니다. 예를 들어 Signal invert 기능은 `/sys/bus/counter/devices/counterX/signalY/invert`로 제공할 수 있습니다.
Count extension은 특정 Count의 정보나 제어를 노출하며 Count directory 아래에 존재합니다. Count update를 pause·unpause하는 `enable`은 `/sys/bus/counter/devices/counterX/countY/enable`로 제공할 수 있습니다.
Device extension은 특정 Count나 Signal에 종속되지 않은 global 또는 miscellaneous 기능을 노출합니다. Overtemperature sensor 상태는 `/sys/bus/counter/devices/counterX/error_overtemp` 같은 device extension으로 보고할 수 있습니다.
Extension의 소유 component와 sysfs 위치 예제입니다.
Subsystem architecture
342-452Counter driver는 `u8`, `u64` 같은 native type으로 data를 주고받고 shared counter module이 sysfs interface 형식으로 변환합니다. 이 구조는 모든 counter driver에 표준 userspace interface를 보장하고 일반화된 device driver ABI를 통해 Generic Counter chrdev interface를 제공합니다.
먼저 driver callback을 Counter core에 등록하면 core가 userspace interface component인 Counter sysfs와 Counter chrdev로 callback을 route합니다.
원문의 callback registration ASCII 그림을 구조화했습니다.
그 뒤 data는 Counter device driver와 userspace interface 사이에서 직접 전달됩니다. 예제에서 hardware count register `0x28`의 raw value를 driver가 `u64` 값 `42`로 처리합니다. Sysfs는 이를 `const char *` 문자열 `"42"`로 변환해 `/sys/bus/counter/devices/counterX/countY/count`에 제공하고, chrdev는 `u64` 42를 `struct counter_event`로 전달해 `read`에서 반환합니다.
원문의 count data request ASCII 흐름을 interface별 data type으로 정리했습니다.
Subsystem의 네 component
453-479Counter subsystem에는 네 주요 component가 관여합니다.
Counter device driver는 quadrature encoder나 timer 같은 hardware device와 통신해 data를 읽고 씁니다. Counter core는 driver를 system에 등록하고 userspace interaction 중 적절한 callback을 호출합니다.
Counter sysfs는 counter data를 표준 Counter sysfs interface 형식으로 상호 변환합니다. 사용 가능한 attribute의 자세한 설명은 `Documentation/ABI/testing/sysfs-bus-counter`를 참조합니다.
Counter chrdev는 Counter event를 표준 Counter character device로 변환합니다. Data는 일반 character device `read` call로 전달하고 event는 `ioctl` call로 구성합니다.
Hardware에서 userspace까지 각 component의 책임입니다.
Sysfs interface
480-492Generic Counter interface가 생성하는 sysfs attribute는 `/sys/bus/counter/devices/counterX` 아래에 위치하며 `X`는 counter device id입니다. 각 attribute의 상세 정보는 `Documentation/ABI/testing/sysfs-bus-counter`를 참조합니다.
Program과 script는 이 sysfs attribute를 통해 각 counter device의 Count, Signal, Synapse와 상호 작용할 수 있습니다.
Counter character device와 event
493-541Counter character device node는 `/dev/counterX`로 생성되며 `X`는 counter device id입니다. 표준 Counter data type define은 userspace header `include/uapi/linux/counter.h`에서 제공합니다.
Counter device driver는 `counter_push_event` function으로 Counter event를 지원할 수 있습니다.
void counter_push_event(struct counter_device *const counter, const u8 event,
const u8 channel);
`event` parameter는 event id, `channel` parameter는 event channel id입니다. Function을 호출하면 해당 event와 연결된 Counter data를 수집해 각 datum마다 `struct counter_event`를 만들고 userspace로 push합니다.
User는 관심 있는 Counter data를 보고하도록 event를 구성할 수 있습니다. 이는 실행할 Counter component read call 목록으로 볼 수 있습니다.
원문의 COUNTER_EVENT_OVERFLOW·INDEX channel 표를 구조화했습니다.
예를 들어 `counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`을 호출하면 INDEX event channel 1 목록을 따라 Signal 4, Signal 4 Extension 0, Count 7의 read callback을 실행합니다. 각 결과는 `struct counter_event`로 `kfifo`에 push되며 userspace가 해당 character device node의 일반 `read` operation으로 가져옵니다.
Userspace event 구성
542-573Userspace application은 Counter character device node의 `ioctl` operation으로 event를 구성합니다. `linux/counter.h` userspace header는 `COUNTER_ADD_WATCH_IOCTL`, `COUNTER_ENABLE_EVENTS_IOCTL`, `COUNTER_DISABLE_EVENTS_IOCTL`을 제공합니다.
Counter data를 수집할 event를 구성하려면 `struct counter_watch`에 event id, event channel id, 읽을 Counter component 정보를 채운 뒤 `COUNTER_ADD_WATCH_IOCTL`로 전달합니다.
`component.type`을 `COUNTER_COMPONENT_NONE`으로 설정하면 Counter data를 수집하지 않고 event만 watch할 수 있습니다. 이 경우 character device는 해당 `struct counter_event` element의 timestamp만 채우고 component value는 무시합니다.
`COUNTER_ADD_WATCH_IOCTL`은 watch를 buffer합니다. 준비가 끝나면 `COUNTER_ENABLE_EVENTS_IOCTL`로 활성화합니다. 그 후 application은 필요하면 먼저 `poll`을 호출하고 character device node에서 `read`를 실행해 원하는 data가 담긴 `struct counter_event` element를 가져옵니다.
Watch 등록부터 event data read까지의 ioctl·I/O 순서입니다.
요약과 해설
generic-counter.rst:1-573Generic Counter는 다양한 counter hardware를 Signal, Synapse, Count라는 공통 model로 표현합니다. Driver는 native C type callback과 표준 extension macro를 등록하고, core는 이를 sysfs와 character-device event ABI로 변환합니다.
Event watch는 ioctl로 등록·활성화하며 driver의 `counter_push_event()`가 수집한 component data를 `struct counter_event` 형태로 userspace에 전달합니다.