This patch introduces a character device interface for the Counter
subsystem. Device data is exposed through standard character device read
operations. Device data is gathered when a Counter event is pushed by
the respective Counter device driver. Configuration is handled via ioctl
operations on the respective Counter character device node.
A high-level view of how a count value is passed down from a counter
driver is exemplified by the following:
----------------------
/ Counter device \
+----------------------+
| Count register: 0x28 |
+----------------------+
|
-----------------
/ raw count data /
-----------------
|
V
+----------------------------+
| Counter device driver |----------+
+----------------------------+ |
| Processes data from device | -------------------
|----------------------------| / driver callbacks /
| Type: u64 | -------------------
| Value: 42 | |
+----------------------------+ |
| |
---------- |
/ u64 / |
---------- |
| |
| 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 |
|--------------------| |---------------------+
| 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" /
--------------------------------------------------
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:
int counter_push_event(struct counter_device *const counter,
const u8 event);
The event id is specified by the `event` 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:
+------------------------+------------------------+
| Event 0 | Event 1 |
+------------------------+------------------------+
| * Count 0 | * Signal 0 |
| * Count 1 | * Signal 0 Extension 0 |
| * Signal 3 | * Extension 4 |
| * Count 4 Extension 2 | |
| * Signal 5 Extension 0 | |
+------------------------+------------------------+
When `counter_push_event(counter, 1)` is called for example, it will go
down the list for Event 1 and execute the read callbacks for Signal 0,
Signal 0 Extension 0, and Extension 4 -- the data returned for each is
pushed to a kfifo as a `struct counter_event`, which userspace can
retrieve via a standard read operation on the respective character
device node.
Userspace
---------
Userspace applications can configure Counter events via ioctl operations
on the Counter character device node. There following ioctl codes are
supported and provided by the `linux/counter.h` userspace header file:
* COUNTER_CLEAR_WATCHES_IOCTL:
Clear all Counter watches from all events
* COUNTER_SET_WATCH_IOCTL:
Set a Counter watch on the specified event
To configure events to gather Counter data, users first populate a
`struct counter_watch` with the relevant event id and the information
for the desired Counter component from which to read, and then pass it
via the `COUNTER_SET_WATCH_IOCTL` ioctl command.
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.
For example, the following userspace code opens `/dev/counter0`,
configures Event 0 to gather Count 0 and Count 1, and prints out the
data as it becomes available on the character device node:
#include <fcntl.h>
#include <linux/counter.h>
#include <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
struct counter_watch watches[2] = {
{
.event = 0,
.component.owner_type = COUNTER_OWNER_TYPE_COUNT,
.component.owner_id = 0,
.component.type = COUNTER_COMPONENT_TYPE_COUNT,
},
{
.event = 0,
.component.owner_type = COUNTER_OWNER_TYPE_COUNT,
.component.owner_id = 1,
.component.type = COUNTER_COMPONENT_TYPE_COUNT,
},
};
int main(void)
{
struct pollfd pfd = { .events = POLLIN };
struct counter_event event_data[2];
pfd.fd = open("/dev/counter0", O_RDWR);
ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
for (;;) {
poll(&pfd, 1, -1);
read(pfd.fd, event_data, sizeof(event_data));
printf("Timestamp 0: %llu\nCount 0: %llu\n"
"Timestamp 1: %llu\nCount 1: %llu\n",
(unsigned long long)event_data[0].timestamp,
(unsigned long long)event_data[0].value_u64,
(unsigned long long)event_data[1].timestamp,
(unsigned long long)event_data[1].value_u64);
}
return 0;
}
Cc: David Lechner <david@xxxxxxxxxxxxxx>
Cc: Gwendal Grignou <gwendal@xxxxxxxxxxxx>
Signed-off-by: William Breathitt Gray <vilhelm.gray@xxxxxxxxx>
---