* [PATCH v3 2/4] docs: counter: Update to reflect sysfs internalization
2020-06-17 1:39 [PATCH v3 0/4] Introduce the Counter character device interface William Breathitt Gray
@ 2020-06-17 1:40 ` William Breathitt Gray
2020-06-17 1:40 ` [PATCH v3 3/4] counter: Add character device interface William Breathitt Gray
2020-06-17 1:40 ` [PATCH v3 4/4] docs: counter: Document " William Breathitt Gray
2 siblings, 0 replies; 9+ messages in thread
From: William Breathitt Gray @ 2020-06-17 1:40 UTC (permalink / raw)
To: jic23
Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
alexandre.torgue, William Breathitt Gray
The Counter subsystem architecture and driver implementations have
changed in order to handle Counter sysfs interactions in a more
consistent way. This patch updates the Generic Counter interface
documentation to reflect the changes.
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
Documentation/driver-api/generic-counter.rst | 216 +++++++++++++------
1 file changed, 150 insertions(+), 66 deletions(-)
diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index e622f8f6e56a..8aaa6cd37fd4 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -250,8 +250,8 @@ for defining a counter device.
.. kernel-doc:: drivers/counter/counter.c
:export:
-Implementation
-==============
+Driver Implementation
+=====================
To support a counter device, a driver must first allocate the available
Counter Signals via counter_signal structures. These Signals should
@@ -267,25 +267,59 @@ 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 should be provided to the counter_device structure via
-a constant counter_ops 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.
+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 defined counter_device structure may be registered to the system by
passing it to the counter_register function, and unregistered by passing
it to the counter_unregister function. Similarly, the
-devm_counter_register and devm_counter_unregister functions may be used
-if device memory-managed registration is desired.
-
-Extension sysfs attributes can be created for auxiliary functionality
-and data by passing in defined counter_device_ext, counter_count_ext,
-and counter_signal_ext structures. In these cases, the
-counter_device_ext structure is used for global/miscellaneous exposure
-and configuration of the respective Counter device, while the
-counter_count_ext and counter_signal_ext structures allow for auxiliary
-exposure and configuration of a specific Count or Signal respectively.
+devm_counter_register function may be used if device memory-managed
+registration is desired.
+
+The struct counter_data 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_DATA_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_data count_ext[] = {
+ COUNTER_DATA_DIRECTION(count_direction_read),
+ COUNTER_DATA_ENABLE(count_enable_read, count_enable_write),
+ COUNTER_DATA_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_data 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, u8 *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.
@@ -313,52 +347,102 @@ Determining the type of extension to create is a matter of scope.
chip overheated via a device extension called "error_overtemp":
/sys/bus/counter/devices/counterX/error_overtemp
-Architecture
-============
-
-When the Generic Counter interface counter module is loaded, the
-counter_init function is called which registers a bus_type named
-"counter" to the system. Subsequently, when the module is unloaded, the
-counter_exit function is called which unregisters the bus_type named
-"counter" from the system.
-
-Counter devices are registered to the system via the counter_register
-function, and later removed via the counter_unregister function. The
-counter_register function establishes a unique ID for the Counter
-device and creates a respective sysfs directory, where X is the
-mentioned unique ID:
-
- /sys/bus/counter/devices/counterX
-
-Sysfs attributes are created within the counterX directory to expose
-functionality, configurations, and data relating to the Counts, Signals,
-and Synapses of the Counter device, as well as options and information
-for the Counter device itself.
-
-Each Signal has a directory created to house its relevant sysfs
-attributes, where Y is the unique ID of the respective Signal:
-
- /sys/bus/counter/devices/counterX/signalY
-
-Similarly, each Count has a directory created to house its relevant
-sysfs attributes, where Y is the unique ID of the respective Count:
-
- /sys/bus/counter/devices/counterX/countY
-
-For a more detailed breakdown of the available Generic Counter interface
-sysfs attributes, please refer to the
-Documentation/ABI/testing/sysfs-bus-counter file.
-
-The Signals and Counts associated with the Counter device are registered
-to the system as well by the counter_register function. The
-signal_read/signal_write driver callbacks are associated with their
-respective Signal attributes, while the count_read/count_write and
-function_get/function_set driver callbacks are associated with their
-respective Count attributes; similarly, the same is true for the
-action_get/action_set driver callbacks and their respective Synapse
-attributes. If a driver callback is left undefined, then the respective
-read/write permission is left disabled for the relevant attributes.
-
-Similarly, extension sysfs attributes are created for the defined
-counter_device_ext, counter_count_ext, and counter_signal_ext
-structures that are passed in.
+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 gurantees a standard userspace interface for all counter
+drivers, and helps generalize the Generic Counter driver ABI in order to
+support the Generic Counter chrdev interface without significant changes
+to the existing counter drivers.
+
+A high-level view of how a count value is passed down from a counter
+driver is exemplified by the following::
+
+ Count data request:
+ ~~~~~~~~~~~~~~~~~~~
+ ----------------------
+ / Counter device \
+ +----------------------+
+ | Count register: 0x28 |
+ +----------------------+
+ |
+ -----------------
+ / raw count data /
+ -----------------
+ |
+ V
+ +----------------------------+
+ | Counter device driver |----------+
+ +----------------------------+ |
+ | Processes data from device | -------------------
+ |----------------------------| / driver callbacks /
+ | Type: unsigned long | -------------------
+ | Value: 42 | |
+ +----------------------------+ |
+ | |
+ ---------------- |
+ / unsigned long / |
+ ---------------- |
+ | |
+ | V
+ | +----------------------+
+ | | Counter core |
+ | +----------------------+
+ | | Routes device driver |
+ | | callbacks to the |
+ | | userspace interfaces |
+ | +----------------------+
+ | |
+ | -------------------
+ | / driver callbacks /
+ | -------------------
+ | |
+ +-------+ |
+ | |
+ | +---------------+
+ | |
+ V |
+ +--------------------+ |
+ | Counter sysfs |<-+
+ +--------------------+
+ | Translates to the |
+ | standard Counter |
+ | sysfs output |
+ |--------------------|
+ | Type: const char * |
+ | Value: "42" |
+ +--------------------+
+ |
+ ---------------
+ / const char * /
+ ---------------
+ |
+ V
+ +--------------------------------------------------+
+ | `/sys/bus/counter/devices/counterX/countY/count` |
+ +--------------------------------------------------+
+ \ Count: "42" /
+ --------------------------------------------------
+
+There are three 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.
--
2.26.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 3/4] counter: Add character device interface
2020-06-17 1:39 [PATCH v3 0/4] Introduce the Counter character device interface William Breathitt Gray
2020-06-17 1:40 ` [PATCH v3 2/4] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
@ 2020-06-17 1:40 ` William Breathitt Gray
2020-06-20 18:50 ` David Lechner
2020-06-17 1:40 ` [PATCH v3 4/4] docs: counter: Document " William Breathitt Gray
2 siblings, 1 reply; 9+ messages in thread
From: William Breathitt Gray @ 2020-06-17 1:40 UTC (permalink / raw)
To: jic23
Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
alexandre.torgue, William Breathitt Gray
This patch introduces a character device interface for the Counter
subsystem. Device control is exposed through standard character device
read and write operations.
A /sys/bus/counter/devices/counterX/chrdev_format sysfs attribute is
introduced to expose the character device data format:
* Users may write to this sysfs attribute to select the components they
want to interface -- the layout can be determined as well from the
order. For example:
# echo "C0 C3 C2" > /sys/bus/counter/devices/counter0/chrdev_format
This would select Counts 0, 3, and 2 (in that order) to be available
in the /dev/counter0 node as a contiguous memory region.
You can select extensions in a similar fashion:
# echo "C4E2 S1E0" > /sys/bus/counter/devices/counter0/chrdev_format
This would select extension 2 from Count 4, and extension 0 from
Signal 1.
* Users may read from this chrdev_format sysfs attribute in order to see
the currently configured format of the character device.
* Users may perform read/write operations on the /dev/counterX node
directly; the layout of the data is what they user has configured via
the chrdev_format sysfs attribute. For example:
# echo "C0 C1 S0 S1" > /sys/bus/counter/devices/counter0/chrdev_format
Yields the following /dev/counter0 memory layout:
+-----------------+------------------+----------+----------+
| Byte 0 - Byte 7 | Byte 8 - Byte 15 | Byte 16 | Byte 17 |
+-----------------+------------------+----------+----------+
| Count 0 | Count 1 | Signal 0 | Signal 2 |
+-----------------+------------------+----------+----------+
The number of bytes allotted for each component or extension is
determined by its respective data type: u8 will have 1 byte allotted,
u64 will have 8 bytes allotted, etc.
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 * / / u64 /
--------------- ----------
| |
| V
| +-----------+
| | read |
| +-----------+
| \ Count: 42 /
| -----------
|
V
+--------------------------------------------------+
| `/sys/bus/counter/devices/counterX/countY/count` |
+--------------------------------------------------+
\ Count: "42" /
--------------------------------------------------
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
MAINTAINERS | 1 +
drivers/counter/Makefile | 2 +-
drivers/counter/counter-chrdev.c | 612 +++++++++++++++++++++++++++++++
drivers/counter/counter-chrdev.h | 23 ++
drivers/counter/counter-core.c | 34 +-
drivers/counter/counter-sysfs.c | 38 +-
include/linux/counter.h | 12 +-
7 files changed, 714 insertions(+), 8 deletions(-)
create mode 100644 drivers/counter/counter-chrdev.c
create mode 100644 drivers/counter/counter-chrdev.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 7ea12db794be..b4ed407a13f8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4413,6 +4413,7 @@ F: Documentation/ABI/testing/sysfs-bus-counter*
F: Documentation/driver-api/generic-counter.rst
F: drivers/counter/
F: include/linux/counter.h
+F: include/uapi/linux/counter.h
F: include/uapi/linux/counter-types.h
CPMAC ETHERNET DRIVER
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index f48e337cbd85..d219b9c058e7 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -4,7 +4,7 @@
#
obj-$(CONFIG_COUNTER) += counter.o
-counter-y := counter-core.o counter-sysfs.o
+counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
new file mode 100644
index 000000000000..e143380edd21
--- /dev/null
+++ b/drivers/counter/counter-chrdev.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+
+#include <linux/cdev.h>
+#include <linux/counter.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include "counter-chrdev.h"
+#include "counter-common.h"
+
+struct counter_control {
+ loff_t offset;
+ size_t size;
+ struct list_head l;
+
+ struct counter_data data;
+ enum counter_owner_type type;
+ void *owner;
+};
+
+static int counter_data_u8_read(struct counter_device *const counter,
+ const struct counter_control *const control,
+ u8 __user *const buf)
+{
+ const struct counter_data *const data = &control->data;
+ int err;
+ u8 val;
+
+ switch (control->type) {
+ case COUNTER_OWNER_TYPE_DEVICE:
+ err = data->device_u8_read(counter, &val);
+ break;
+ case COUNTER_OWNER_TYPE_SIGNAL:
+ err = data->signal_u8_read(counter, control->owner, &val);
+ break;
+ case COUNTER_OWNER_TYPE_COUNT:
+ if (data->type == COUNTER_DATA_TYPE_SYNAPSE_ACTION)
+ err = data->action_read(counter, control->owner,
+ data->priv, &val);
+ else
+ err = data->count_u8_read(counter, control->owner,
+ &val);
+ break;
+ }
+ if (err)
+ return err;
+
+ return put_user(val, buf);
+}
+
+static int counter_data_u64_read(struct counter_device *const counter,
+ const struct counter_control *const control,
+ char __user *const buf, const size_t len,
+ const size_t offset)
+{
+ const struct counter_data *const data = &control->data;
+ int err;
+ u64 val;
+
+ switch (control->type) {
+ case COUNTER_OWNER_TYPE_DEVICE:
+ err = data->device_u64_read(counter, &val);
+ break;
+ case COUNTER_OWNER_TYPE_SIGNAL:
+ err = data->signal_u64_read(counter, control->owner, &val);
+ break;
+ case COUNTER_OWNER_TYPE_COUNT:
+ err = data->count_u64_read(counter, control->owner, &val);
+ break;
+ }
+ if (err)
+ return err;
+
+ return copy_to_user(buf, (const char *)&val + offset, len);
+}
+
+static int counter_control_read(struct counter_device *const counter,
+ const struct counter_control *const control,
+ char __user *const buf, const size_t len,
+ const size_t offset)
+{
+ /* Check if read operation is supported */
+ if (!control->data.device_u8_read)
+ return -EFAULT;
+
+ switch (control->data.type) {
+ case COUNTER_DATA_TYPE_U8:
+ case COUNTER_DATA_TYPE_BOOL:
+ case COUNTER_DATA_TYPE_SIGNAL:
+ case COUNTER_DATA_TYPE_COUNT_FUNCTION:
+ case COUNTER_DATA_TYPE_SYNAPSE_ACTION:
+ case COUNTER_DATA_TYPE_ENUM:
+ case COUNTER_DATA_TYPE_COUNT_DIRECTION:
+ case COUNTER_DATA_TYPE_COUNT_MODE:
+ return counter_data_u8_read(counter, control, buf);
+ case COUNTER_DATA_TYPE_U64:
+ return counter_data_u64_read(counter, control, buf, len,
+ offset);
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+ size_t len, loff_t *f_ps)
+{
+ struct counter_device *const counter = filp->private_data;
+ const loff_t start_ps = *f_ps;
+ struct counter_control *control;
+ loff_t next_control_ps;
+ size_t read_size;
+ int err;
+
+ mutex_lock(&counter->control_lock);
+
+ /* Handle controls */
+ list_for_each_entry(control, &counter->control_list, l) {
+ next_control_ps = control->offset + control->size;
+
+ /* Find first control item */
+ if (*f_ps >= next_control_ps)
+ continue;
+
+ read_size = (*f_ps + len > next_control_ps) ?
+ next_control_ps - *f_ps : len;
+
+ err = counter_control_read(counter, control, buf, read_size,
+ control->offset - *f_ps);
+ if (err) {
+ mutex_unlock(&counter->control_lock);
+ return err;
+ }
+
+ *f_ps += read_size;
+ buf += read_size;
+ len -= read_size;
+ if (!len)
+ goto exit_chrdev_read;
+ }
+
+exit_chrdev_read:
+ mutex_unlock(&counter->control_lock);
+ return *f_ps - start_ps;
+}
+
+static int counter_data_u8_write(struct counter_device *const counter,
+ const struct counter_control *const control,
+ const u8 __user *const buf)
+{
+ const struct counter_data *const data = &control->data;
+ int err;
+ u8 val;
+
+ err = get_user(val, buf);
+ if (err)
+ return err;
+
+ switch (control->type) {
+ case COUNTER_OWNER_TYPE_DEVICE:
+ err = data->device_u8_write(counter, val);
+ break;
+ case COUNTER_OWNER_TYPE_SIGNAL:
+ err = data->signal_u8_write(counter, control->owner, val);
+ break;
+ case COUNTER_OWNER_TYPE_COUNT:
+ if (data->type == COUNTER_DATA_TYPE_SYNAPSE_ACTION)
+ err = data->action_write(counter, control->owner,
+ data->priv, val);
+ else
+ err = data->count_u8_write(counter, control->owner,
+ val);
+ break;
+ }
+
+ return err;
+}
+
+static int counter_data_u64_write(struct counter_device *const counter,
+ const struct counter_control *const control,
+ const char __user *const buf,
+ const size_t len, const size_t offset)
+{
+ const struct counter_data *const data = &control->data;
+ int err;
+ u64 val = 0;
+
+ err = copy_from_user((char *)&val + offset, buf, len);
+ if (err)
+ return err;
+
+ switch (control->type) {
+ case COUNTER_OWNER_TYPE_DEVICE:
+ err = data->device_u64_write(counter, val);
+ break;
+ case COUNTER_OWNER_TYPE_SIGNAL:
+ err = data->signal_u64_write(counter, control->owner, val);
+ break;
+ case COUNTER_OWNER_TYPE_COUNT:
+ err = data->count_u64_write(counter, control->owner, val);
+ break;
+ }
+
+ return err;
+}
+
+static int counter_control_write(struct counter_device *const counter,
+ const struct counter_control *const control,
+ const char __user *const buf, const size_t len,
+ const size_t offset)
+{
+ /* Check if write operation is supported */
+ if (!control->data.device_u8_write)
+ return -EFAULT;
+
+ switch (control->data.type) {
+ case COUNTER_DATA_TYPE_U8:
+ case COUNTER_DATA_TYPE_BOOL:
+ case COUNTER_DATA_TYPE_SIGNAL:
+ case COUNTER_DATA_TYPE_COUNT_FUNCTION:
+ case COUNTER_DATA_TYPE_SYNAPSE_ACTION:
+ case COUNTER_DATA_TYPE_ENUM:
+ case COUNTER_DATA_TYPE_COUNT_DIRECTION:
+ case COUNTER_DATA_TYPE_COUNT_MODE:
+ return counter_data_u8_write(counter, control, buf);
+ case COUNTER_DATA_TYPE_U64:
+ return counter_data_u64_write(counter, control, buf, len,
+ offset);
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t counter_chrdev_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *f_ps)
+{
+ struct counter_device *const counter = filp->private_data;
+ const loff_t start_ps = *f_ps;
+ struct counter_control *control;
+ loff_t next_control_ps;
+ size_t write_size;
+ int err;
+
+ mutex_lock(&counter->control_lock);
+
+ /* Handle controls */
+ list_for_each_entry(control, &counter->control_list, l) {
+ next_control_ps = control->offset + control->size;
+
+ /* Find first control item */
+ if (*f_ps >= next_control_ps)
+ continue;
+
+ write_size = (*f_ps + len > next_control_ps) ?
+ next_control_ps - *f_ps : len;
+
+ err = counter_control_write(counter, control, buf, write_size,
+ control->offset - *f_ps);
+ if (err) {
+ mutex_unlock(&counter->control_lock);
+ return err;
+ }
+
+ *f_ps += write_size;
+ buf += write_size;
+ len -= write_size;
+ if (!len)
+ goto exit_chrdev_write;
+ }
+
+ mutex_unlock(&counter->control_lock);
+
+ return -EFAULT;
+
+exit_chrdev_write:
+ mutex_unlock(&counter->control_lock);
+ return *f_ps - start_ps;
+}
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+ struct counter_device *const counter = container_of(inode->i_cdev,
+ typeof(*counter),
+ chrdev);
+
+ get_device(&counter->dev);
+ filp->private_data = counter;
+
+ return generic_file_open(inode, filp);
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+ struct counter_device *const counter = container_of(inode->i_cdev,
+ typeof(*counter),
+ chrdev);
+
+ put_device(&counter->dev);
+
+ return 0;
+}
+
+static const struct file_operations counter_fops = {
+ .llseek = generic_file_llseek,
+ .read = counter_chrdev_read,
+ .write = counter_chrdev_write,
+ .open = counter_chrdev_open,
+ .release = counter_chrdev_release,
+};
+
+int counter_chrdev_add(struct counter_device *const counter,
+ const dev_t counter_devt)
+{
+ struct device *const dev = &counter->dev;
+ struct cdev *const chrdev = &counter->chrdev;
+
+ /* Initialize Counter character device selected controls list */
+ INIT_LIST_HEAD(&counter->control_list);
+
+ /* Initialize mutex for Counter character device selected controls */
+ mutex_init(&counter->control_lock);
+
+ /* Initialize character device */
+ cdev_init(chrdev, &counter_fops);
+ dev->devt = MKDEV(MAJOR(counter_devt), counter->id);
+ cdev_set_parent(chrdev, &dev->kobj);
+
+ return cdev_add(chrdev, dev->devt, 1);
+}
+
+static void counter_control_list_free(struct list_head *const controls)
+{
+ struct counter_control *p, *n;
+
+ list_for_each_entry_safe(p, n, controls, l) {
+ list_del(&p->l);
+ kfree(p);
+ }
+}
+
+void counter_chrdev_free(struct counter_device *const counter)
+{
+ cdev_del(&counter->chrdev);
+
+ mutex_lock(&counter->control_lock);
+ counter_control_list_free(&counter->control_list);
+ mutex_unlock(&counter->control_lock);
+}
+
+ssize_t counter_chrdev_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ const struct counter_signal *signal = counter->signals;
+ const struct counter_count *count = counter->counts;
+ size_t len = 0;
+ struct counter_control *control;
+ struct counter_data *ext;
+ size_t idx;
+ const struct counter_synapse *synapse;
+
+ mutex_lock(&counter->control_lock);
+
+ list_for_each_entry(control, &counter->control_list, l) {
+ switch (control->type) {
+ case COUNTER_OWNER_TYPE_DEVICE:
+ ext = counter->ext;
+ idx = counter->num_ext;
+ break;
+ case COUNTER_OWNER_TYPE_SIGNAL:
+ signal = control->owner;
+ ext = signal->ext;
+ idx = signal->num_ext;
+ len += sprintf(buf + len, "S%zu",
+ signal - counter->signals);
+ break;
+ case COUNTER_OWNER_TYPE_COUNT:
+ count = control->owner;
+ ext = count->ext;
+ idx = count->num_ext;
+ len += sprintf(buf + len, "C%zu",
+ count - counter->counts);
+ break;
+ }
+
+ switch (control->data.type) {
+ default:
+ /* Handle "count" control item */
+ if (control->data.count_u64_read == counter->count_read)
+ break;
+
+ /* Determine extension index */
+ while (--idx)
+ if (!strcmp(ext[idx].name, control->data.name))
+ break;
+
+ len += sprintf(buf + len, "E%zu", idx);
+ break;
+ case COUNTER_DATA_TYPE_COUNT_FUNCTION:
+ buf[len++] = 'F';
+ break;
+ case COUNTER_DATA_TYPE_SYNAPSE_ACTION:
+ synapse = control->data.priv;
+ len += sprintf(buf + len, "A%zu",
+ synapse - count->synapses);
+ break;
+ case COUNTER_DATA_TYPE_SIGNAL:
+ break;
+ }
+
+ buf[len++] = ' ';
+ }
+
+ mutex_unlock(&counter->control_lock);
+
+ return len;
+}
+
+static int counter_control_add(struct list_head *const controls,
+ const struct counter_data *const data,
+ const enum counter_owner_type type,
+ void *const owner)
+{
+ const struct counter_control *const tail = list_last_entry(controls,
+ typeof(*tail),
+ l);
+ struct counter_control *control;
+
+ /* Generate control list item */
+ control = kzalloc(sizeof(*control), GFP_KERNEL);
+ if (!control)
+ return -ENOMEM;
+
+ /* Configure control list item */
+ control->data = *data;
+ control->type = type;
+ control->owner = owner;
+ control->offset = list_empty(controls) ? 0 : tail->offset + tail->size;
+ switch (control->data.type) {
+ case COUNTER_DATA_TYPE_U8:
+ case COUNTER_DATA_TYPE_BOOL:
+ case COUNTER_DATA_TYPE_SIGNAL:
+ case COUNTER_DATA_TYPE_COUNT_FUNCTION:
+ case COUNTER_DATA_TYPE_SYNAPSE_ACTION:
+ case COUNTER_DATA_TYPE_ENUM:
+ case COUNTER_DATA_TYPE_COUNT_DIRECTION:
+ case COUNTER_DATA_TYPE_COUNT_MODE:
+ control->size = sizeof(u8);
+ break;
+ case COUNTER_DATA_TYPE_U64:
+ control->size = sizeof(u64);
+ break;
+ }
+
+ /* Add control list item to the list */
+ list_add_tail(&control->l, controls);
+
+ return 0;
+}
+
+ssize_t counter_chrdev_format_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct counter_device *const counter = dev_get_drvdata(dev);
+ struct list_head *const controls = &counter->control_list;
+ size_t pos = skip_spaces(buf) - buf;
+ int num;
+ char id, subid;
+ size_t idx, subidx, maxidx;
+ struct counter_signal *signal;
+ struct counter_count *count;
+ struct counter_data *ext;
+ struct counter_data data;
+ enum counter_owner_type type;
+ void *owner;
+ int err;
+
+ mutex_lock(&counter->control_lock);
+
+ /* Clean up old list */
+ counter_control_list_free(controls);
+
+ while (pos < len) {
+ /* Scan item */
+ num = sscanf(buf + pos, "%c%zu%c%zu", &id, &idx, &subid,
+ &subidx);
+ if (num < 2) {
+ err = -EINVAL;
+ goto err_control_create;
+ }
+
+ /* Ignore trailing whitespace */
+ if (isspace(subid))
+ num = 2;
+
+ /* Process owner */
+ switch (id) {
+ case 'S':
+ if (idx > counter->num_signals - 1) {
+ err = -EINVAL;
+ goto err_control_create;
+ }
+ signal = counter->signals + idx;
+ ext = signal->ext;
+ maxidx = signal->num_ext - 1;
+
+ /* Handle Signal item */
+ data.type = COUNTER_DATA_TYPE_SIGNAL;
+ data.signal_u8_read = counter->signal_read;
+ type = COUNTER_OWNER_TYPE_SIGNAL;
+ owner = signal;
+ break;
+ case 'C':
+ if (idx > counter->num_counts - 1) {
+ err = -EINVAL;
+ goto err_control_create;
+ }
+ count = counter->counts + idx;
+ ext = count->ext;
+ maxidx = count->num_ext - 1;
+
+ /* Handle Count item */
+ data.type = COUNTER_DATA_TYPE_U64,
+ data.count_u64_read = counter->count_read;
+ data.count_u64_write = counter->count_write;
+ type = COUNTER_OWNER_TYPE_COUNT;
+ owner = count;
+ break;
+ case 'E':
+ if (num > 2 || idx > counter->num_ext - 1) {
+ err = -EINVAL;
+ goto err_control_create;
+ }
+
+ /* Handle device extension item */
+ data = counter->ext[idx];
+ type = COUNTER_OWNER_TYPE_DEVICE;
+ owner = NULL;
+ break;
+ default:
+ err = -EINVAL;
+ goto err_control_create;
+ }
+
+ if (num == 3) {
+ if (subid != 'F') {
+ err = -EINVAL;
+ goto err_control_create;
+ }
+
+ /* Handle count function item */
+ data.type = COUNTER_DATA_TYPE_COUNT_FUNCTION,
+ data.count_u8_read = counter->function_read;
+ data.count_u8_write = counter->function_write;
+ } else if (num == 4) {
+ if (subidx > maxidx) {
+ err = -EINVAL;
+ goto err_control_create;
+ }
+
+ switch (subid) {
+ case 'A':
+ /* Handle Synapse action item */
+ data.type = COUNTER_DATA_TYPE_SYNAPSE_ACTION;
+ data.action_read = counter->action_read;
+ data.action_write = counter->action_write;
+ data.priv = count->synapses + subidx;
+ break;
+ case 'E':
+ /* Handle component extension item */
+ data = ext[subidx];
+ break;
+ default:
+ err = -EINVAL;
+ goto err_control_create;
+ }
+ }
+
+ /* Generate control item */
+ err = counter_control_add(controls, &data, type, owner);
+ if (err)
+ goto err_control_create;
+
+ /* Jump to next item */
+ while (isalnum(buf[pos]))
+ pos++;
+ pos = skip_spaces(buf + pos) - buf;
+ }
+
+ mutex_unlock(&counter->control_lock);
+
+ return len;
+
+err_control_create:
+ counter_control_list_free(controls);
+ mutex_unlock(&counter->control_lock);
+ return err;
+}
diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
new file mode 100644
index 000000000000..73af6d236bc8
--- /dev/null
+++ b/drivers/counter/counter-chrdev.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_CHRDEV_H_
+#define _COUNTER_CHRDEV_H_
+
+#include <linux/counter.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+int counter_chrdev_add(struct counter_device *const counter,
+ const dev_t counter_devt);
+void counter_chrdev_free(struct counter_device *const counter);
+
+ssize_t counter_chrdev_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t counter_chrdev_format_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len);
+
+#endif /* _COUNTER_CHRDEV_H_ */
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 499664809c75..2d0886648201 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -6,11 +6,14 @@
#include <linux/counter.h>
#include <linux/device.h>
#include <linux/export.h>
+#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/types.h>
+#include "counter-chrdev.h"
#include "counter-sysfs.h"
/* Provides a unique ID for each counter device */
@@ -33,6 +36,8 @@ static struct bus_type counter_bus_type = {
.name = "counter"
};
+static dev_t counter_devt;
+
/**
* counter_register - register Counter to the system
* @counter: pointer to Counter to register
@@ -62,10 +67,15 @@ int counter_register(struct counter_device *const counter)
device_initialize(dev);
dev_set_drvdata(dev, counter);
+ /* Add Counter character device */
+ err = counter_chrdev_add(counter, counter_devt);
+ if (err)
+ goto err_free_id;
+
/* Add Counter sysfs attributes */
err = counter_sysfs_add(counter);
if (err)
- goto err_free_id;
+ goto err_free_chrdev;
/* Add device to system */
err = device_add(dev);
@@ -76,6 +86,8 @@ int counter_register(struct counter_device *const counter)
err_free_sysfs:
counter_sysfs_free(counter);
+err_free_chrdev:
+ counter_chrdev_free(counter);
err_free_id:
ida_simple_remove(&counter_ida, counter->id);
return err;
@@ -93,6 +105,7 @@ void counter_unregister(struct counter_device *const counter)
if (counter) {
device_del(&counter->dev);
counter_sysfs_free(counter);
+ counter_chrdev_free(counter);
}
}
EXPORT_SYMBOL_GPL(counter_unregister);
@@ -139,13 +152,30 @@ int devm_counter_register(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_counter_register);
+#define COUNTER_DEV_MAX 256
+
static int __init counter_init(void)
{
- return bus_register(&counter_bus_type);
+ int err;
+
+ err = bus_register(&counter_bus_type);
+ if (err < 0)
+ return err;
+
+ err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+ if (err < 0)
+ goto err_unregister_bus;
+
+ return 0;
+
+err_unregister_bus:
+ bus_unregister(&counter_bus_type);
+ return err;
}
static void __exit counter_exit(void)
{
+ unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
bus_unregister(&counter_bus_type);
}
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index ca69e9d4e833..1279bd8a8af1 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include "counter-common.h"
+#include "counter-chrdev.h"
#include "counter-sysfs.h"
struct counter_attribute {
@@ -658,11 +659,29 @@ static int counter_num_counts_read(struct counter_device *counter, u8 *val)
return 0;
}
-static struct counter_data counter_num_signals_data =
- COUNTER_DATA_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
+static int counter_format_attr_create(struct counter_attribute_group *group,
+ struct counter_device *const counter)
+{
+ struct counter_attribute *counter_attr;
+ struct device_attribute *dev_attr;
-static struct counter_data counter_num_counts_data =
- COUNTER_DATA_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
+ /* Allocate Counter attribute */
+ counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
+ if (!counter_attr)
+ return -ENOMEM;
+ list_add(&counter_attr->l, &group->attr_list);
+ group->num_attr++;
+
+ /* Configure device attribute */
+ dev_attr = &counter_attr->dev_attr;
+ sysfs_attr_init(&dev_attr->attr);
+ dev_attr->attr.name = "chrdev_format";
+ dev_attr->attr.mode = 0644;
+ dev_attr->show = counter_chrdev_format_show;
+ dev_attr->store = counter_chrdev_format_store;
+
+ return 0;
+}
static void counter_groups_list_free(struct counter_device *const counter)
{
@@ -685,6 +704,12 @@ static void counter_groups_list_free(struct counter_device *const counter)
kfree(counter->groups_list);
}
+static struct counter_data counter_num_signals_data =
+ COUNTER_DATA_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
+
+static struct counter_data counter_num_counts_data =
+ COUNTER_DATA_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
+
static int counter_device_register(struct counter_attribute_group *group,
struct counter_device *const counter)
{
@@ -732,6 +757,11 @@ static int counter_device_register(struct counter_attribute_group *group,
goto err_free_groups_list;
}
+ /* Create chrdev_format attribute */
+ err = counter_format_attr_create(group, counter);
+ if (err)
+ goto err_free_groups_list;
+
return 0;
err_free_groups_list:
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 3eb567a5884b..88cc4f32dc2d 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,9 +6,11 @@
#ifndef _COUNTER_H_
#define _COUNTER_H_
+#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/types.h>
#include <uapi/linux/counter-types.h>
@@ -21,8 +23,8 @@ enum counter_data_type {
COUNTER_DATA_TYPE_U8,
COUNTER_DATA_TYPE_U64,
COUNTER_DATA_TYPE_BOOL,
- COUNTER_DATA_TYPE_COUNT_FUNCTION,
COUNTER_DATA_TYPE_SIGNAL,
+ COUNTER_DATA_TYPE_COUNT_FUNCTION,
COUNTER_DATA_TYPE_SYNAPSE_ACTION,
COUNTER_DATA_TYPE_ENUM,
COUNTER_DATA_TYPE_COUNT_DIRECTION,
@@ -166,6 +168,9 @@ struct counter_attribute_group {
* @priv: optional private data supplied by driver
* @id: unique ID used to identify the Counter
* @dev: internal device structure
+ * @chrdev: internal character device structure
+ * @control_list: Counter character device selected controls
+ * @control_lock: Mutex for Counter character device selected controls
* @dynamic_names_list: List for dynamic names
* @groups_list: attribute groups list (for Signals, Counts, and ext)
* @num_groups: number of attribute groups containers
@@ -204,6 +209,11 @@ struct counter_device {
int id;
struct device dev;
+ struct cdev chrdev;
+
+ struct list_head control_list;
+ struct mutex control_lock;
+
struct list_head dynamic_names_list;
struct counter_attribute_group *groups_list;
size_t num_groups;
--
2.26.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 4/4] docs: counter: Document character device interface
2020-06-17 1:39 [PATCH v3 0/4] Introduce the Counter character device interface William Breathitt Gray
2020-06-17 1:40 ` [PATCH v3 2/4] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
2020-06-17 1:40 ` [PATCH v3 3/4] counter: Add character device interface William Breathitt Gray
@ 2020-06-17 1:40 ` William Breathitt Gray
2 siblings, 0 replies; 9+ messages in thread
From: William Breathitt Gray @ 2020-06-17 1:40 UTC (permalink / raw)
To: jic23
Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
alexandre.torgue, William Breathitt Gray
This patch adds high-level documentation about the Counter subsystem
character device interface.
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
Documentation/ABI/testing/sysfs-bus-counter | 31 +++++
Documentation/driver-api/generic-counter.rst | 132 ++++++++++++++-----
2 files changed, 127 insertions(+), 36 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index 566bd99fe0a5..8533a8732544 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -193,6 +193,37 @@ Description:
both edges:
Any state transition.
+What: /sys/bus/counter/devices/counterX/chrdev_format
+KernelVersion: 5.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ Data format of the respective character device node of the
+ Counter. Reading this attribute returns the current data format
+ of the respective character device node; writing to this
+ attribute sets the current data format of the respective
+ character device node. This attribute interfaces via the
+ following format:
+
+ Components/extensions are specified by a character identifier
+ and an index offset; whitespace serves as delimiters. The
+ following character identifiers are supported:
+
+ C: Count
+ S: Signal
+ E: Extension
+ A: Synapse Action
+ F: Count Function
+
+ Count/Signal extensions may be specified by first specifying the
+ respective owning component then the desired extension
+ immediately following without delimiting whitespace; Synapse
+ Action and Count Function are specified in a similar manner.
+
+ For example, "C4 C2E6 C0F S7E1 C3A4 S5" would specify the
+ following data format: Count 4, Count 2's Extension 6, Count 0's
+ Count Function, Signal 7's Extension 1, Count 3's Synapse Action
+ 4, Signal 5.
+
What: /sys/bus/counter/devices/counterX/name
KernelVersion: 5.2
Contact: linux-iio@vger.kernel.org
diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index 8aaa6cd37fd4..d46ce65d1488 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
on the core idea of what the data and process represent (e.g. position
as interpreted from quadrature encoding data).
-Userspace Interface
-===================
-
-Several sysfs attributes are generated by the Generic Counter interface,
-and reside under the /sys/bus/counter/devices/counterX directory, where
-counterX refers to the respective counter device. Please see
-Documentation/ABI/testing/sysfs-bus-counter for detailed
-information on each Generic Counter interface sysfs attribute.
-
-Through these sysfs attributes, programs and scripts may interact with
-the Generic Counter paradigm Counts, Signals, and Synapses of respective
-counter devices.
-
Driver API
==========
@@ -378,13 +365,13 @@ driver is exemplified by the following::
+----------------------------+ |
| Processes data from device | -------------------
|----------------------------| / driver callbacks /
- | Type: unsigned long | -------------------
+ | Type: u64 | -------------------
| Value: 42 | |
+----------------------------+ |
| |
- ---------------- |
- / unsigned long / |
- ---------------- |
+ ---------- |
+ / u64 / |
+ ---------- |
| |
| V
| +----------------------+
@@ -399,25 +386,32 @@ driver is exemplified by the following::
| / 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 |
+ +--------------------+ +---------------------+
| |
- | +---------------+
- | |
- V |
- +--------------------+ |
- | Counter sysfs |<-+
- +--------------------+
- | Translates to the |
- | standard Counter |
- | sysfs output |
- |--------------------|
- | Type: const char * |
- | Value: "42" |
- +--------------------+
- |
- ---------------
- / const char * /
- ---------------
+ --------------- ----------
+ / const char * / / u64 /
+ --------------- ----------
+ | |
+ | V
+ | +-----------+
+ | | read |
+ | +-----------+
+ | \ Count: 42 /
+ | -----------
|
V
+--------------------------------------------------+
@@ -426,7 +420,7 @@ driver is exemplified by the following::
\ Count: "42" /
--------------------------------------------------
-There are three primary components involved:
+There are four primary components involved:
Counter device driver
---------------------
@@ -446,3 +440,69 @@ and vice versa.
Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file
for a detailed breakdown of the available Generic Counter interface
sysfs attributes.
+
+Counter chrdev
+--------------
+Translates counter data to the standard Counter character device; data
+is transferred via standard character device read/write 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-types.h` file.
+
+A `/sys/bus/counter/devices/counterX/chrdev_format` sysfs attribute is
+available to expose the character device data format.
+
+Users may write to this sysfs attribute to select the components they
+want to interface -- the layout can be determined as well from the
+order. For example::
+
+# echo "C0 C3 C2" > /sys/bus/counter/devices/counter0/chrdev_format
+
+This would select Counts 0, 3, and 2 (in that order) to be available
+in the `/dev/counter0` node as a contiguous memory region.
+
+Users can select extensions in a similar fashion::
+
+# echo "C4E2 S1E0" > /sys/bus/counter/devices/counter0/chrdev_format
+
+This would select extension 2 from Count 4, and extension 0 from
+Signal 1.
+
+Users may read from this `chrdev_format` sysfs attribute in order to see
+the currently configured format of the character device.
+
+Users may perform read/write operations on the `/dev/counterX` node
+directly; the layout of the data is what they user has configured via
+the chrdev_format sysfs attribute. For example::
+
+# echo "C0 C1 S0 S1" > /sys/bus/counter/devices/counter0/chrdev_format
+
+Yields the following `/dev/counter0` memory layout::
+
+ +-----------------+------------------+----------+----------+
+ | Byte 0 - Byte 7 | Byte 8 - Byte 15 | Byte 16 | Byte 17 |
+ +-----------------+------------------+----------+----------+
+ | Count 0 | Count 1 | Signal 0 | Signal 2 |
+ +-----------------+------------------+----------+----------+
+
+The number of bytes allotted for each component or extension is
+determined by its respective data type: u8 will have 1 byte allotted,
+u64 will have 8 bytes allotted, etc.
--
2.26.2
^ permalink raw reply related [flat|nested] 9+ messages in thread