On Sat, Jul 07, 2018 at 05:16:22PM +0200, Greg KH wrote: >On Thu, Jun 21, 2018 at 05:07:08PM -0400, William Breathitt Gray wrote: >> This patch introduces the Generic Counter interface for supporting >> counter devices. >> >> In the context of the Generic Counter interface, a counter is defined as >> a device that reports one or more "counts" based on the state changes of >> one or more "signals" as evaluated by a defined "count function." >> >> Driver callbacks should be provided to communicate with the device: to >> read and write various Signals and Counts, and to set and get the >> "action mode" and "count function" for various Synapses and Counts >> respectively. >> >> 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 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. >> >> A counter device is registered to the system by passing the respective >> initialized counter_device structure to the counter_register function; >> similarly, the counter_unregister function unregisters the respective >> Counter. The devm_counter_register and devm_counter_unregister functions >> serve as device memory-managed versions of the counter_register and >> counter_unregister functions respectively. >> >> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx> >> Signed-off-by: William Breathitt Gray <vilhelm.gray@xxxxxxxxx> >> --- >> MAINTAINERS | 7 + >> drivers/Kconfig | 2 + >> drivers/Makefile | 1 + >> drivers/counter/Kconfig | 18 + >> drivers/counter/Makefile | 8 + >> drivers/counter/generic-counter.c | 1519 +++++++++++++++++++++++++++++ >> include/linux/counter.h | 534 ++++++++++ >> 7 files changed, 2089 insertions(+) >> create mode 100644 drivers/counter/Kconfig >> create mode 100644 drivers/counter/Makefile >> create mode 100644 drivers/counter/generic-counter.c >> create mode 100644 include/linux/counter.h > >First cut of review, does counter.h really have to be that big? It >feels like some of the "internal" functions and structures could be >local to drivers/counter/ right? Most of the functions and structures in counter.h are used by driver callbacks to interact with the Generic Counter interface, and thus need to be exposed. There are some helper functions (for example, the counter_count_enum_read and counter_count_enum_write functions) which are not expected to be used directly by drivers, but as prepackaged functionality to populate macros (COUNTER_COUNT_ENUM in this case); in these cases, it is still necessary to expose these functions because the exposed macros are dependent on them. However, having such a large header file can be difficult for a human to parse and debug. Would it make sense for me to break this single large header file into several smaller header files by categories (e.g. counter_signal.h, counter_count.h, counter_count_enum.h, etc.), and use include lines to form a more concise counter.h header file? >> +menuconfig COUNTER >> + tristate "Counter support" >> + help >> + Provides Generic Counter interface support for counter devices. >> + >> + Counter devices are prevalent within 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 providing 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. > >No need to explain an "API" in a Kconfig help text, which is for a user >of the kernel, not for someone writing kernel code. Odds are individual >drivers will need to select this, or are you going to depend on this >option? I'm not sure if there will be situation where a user will want to compile generic-counter.c without a counter device driver, so we can go with the select style for now unless a need comes up for depend. I'll simplify the Kconfig text according. >> diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile >> new file mode 100644 >> index 000000000000..ad1ba7109cdc >> --- /dev/null >> +++ b/drivers/counter/Makefile >> @@ -0,0 +1,8 @@ >> +# >> +# Makefile for Counter devices >> +# >> + >> +# When adding new entries keep the list in alphabetical order > >Why does it matter? :) Fair point, I'll take this comment out. >> + >> +obj-$(CONFIG_COUNTER) += counter.o >> +counter-y := generic-counter.o > >Why not just call your .c file, "counter.c" to keep this simple? This is a hold over from when counter.c was composed of multiple files. I'll rename generic-counter.c to counter.c in order to simplify this line. >> diff --git a/drivers/counter/generic-counter.c b/drivers/counter/generic-counter.c >> new file mode 100644 >> index 000000000000..483826c8ce01 >> --- /dev/null >> +++ b/drivers/counter/generic-counter.c >> @@ -0,0 +1,1519 @@ >> +// SPDX-License-Identifier: GPL-2.0-only > >Please use the normal SPDX identifiers we are using, as described in the >kernel documentation. We aren't ready to use the "new" ones just yet. > >> +/* >> + * Generic Counter interface >> + * Copyright (C) 2017 William Breathitt Gray > >It's 2018 now :) No problem, I'll update the year text as well as the SPDX identifiers throughout the files in this patchset. >> + */ >> +#include <linux/device.h> >> +#include <linux/err.h> >> +#include <linux/export.h> >> +#include <linux/fs.h> >> +#include <linux/gfp.h> >> +#include <linux/idr.h> >> +#include <linux/init.h> >> +#include <linux/kernel.h> >> +#include <linux/list.h> >> +#include <linux/module.h> >> +#include <linux/printk.h> >> +#include <linux/slab.h> >> +#include <linux/string.h> >> +#include <linux/sysfs.h> >> +#include <linux/types.h> >> + >> +#include <linux/counter.h> > >Why a blank line? I'll clean this up. >> + >> +const char *const count_direction_str[2] = { >> + [COUNT_DIRECTION_FORWARD] = "forward", >> + [COUNT_DIRECTION_BACKWARD] = "backward" >> +}; >> +EXPORT_SYMBOL(count_direction_str); > >I have to ask, for all of these, why not EXPORT_SYMBOL_GPL()? This was an oversight: I intend for all of these to be EXPORT_SYMBOL_GPL. I'll fix these in the next revision. >> + >> +const char *const count_mode_str[4] = { >> + [COUNT_MODE_NORMAL] = "normal", >> + [COUNT_MODE_RANGE_LIMIT] = "range limit", >> + [COUNT_MODE_NON_RECYCLE] = "non-recycle", >> + [COUNT_MODE_MODULO_N] = "modulo-n" >> +}; >> +EXPORT_SYMBOL(count_mode_str); > >And why do you need to export strings? The idea is to provide a lookup table of string constants so that driver callbacks return consistent count_mode values to userspace. The count_mode sysfs attribute is implemented via a counter_count_enum_ext structure which takes an array of possible string options available, so that is why this particular string array is exported. The driver callbacks interact only with the index defines (COUNT_MODE_NORMAL, COUNT_MODE_RANGE_LIMIT, etc.), while the respective string constants are found in the array and supplied to/from userspace via the predefined Generic Counter counter_count_enum_read/counter_count_enum_write functions. >> + >> +ssize_t counter_signal_enum_read(struct counter_device *counter, >> + struct counter_signal *signal, void *priv, >> + char *buf) >> +{ >> + const struct counter_signal_enum_ext *const e = priv; >> + int err; >> + size_t index; >> + >> + if (!e->get) >> + return -EINVAL; > >How can e->get not be set? Shouldn't you just not have called into this >if so? e->get is a callback provided by the driver and may not be set. This configuration is possible if the device does not provide a read functionality for its options, but does permit write operations for those options. counter_signal_enum_read is used as a helper function to build the COUNTER_SIGNAL_ENUM macro (as the read callback). Since COUNTER_SIGNAL_ENUM is intended to be general, the counter_signal_enum_read is set as the read callback unconditionally -- this means e->get can't be checked before but must instead be checked from within to handle cases where the device does not support reads. >> + >> + err = e->get(counter, signal, &index); >> + if (err) >> + return err; >> + >> + if (index >= e->num_items) >> + return -EINVAL; >> + >> + return scnprintf(buf, PAGE_SIZE, "%s\n", e->items[index]); > >No need to do the scnprintf() crud, it's a sysfs file, a simple >sprintf() works just fine. Yeah, it makes people nervous, and it should :) Okay, I'll use sprintf in these situations. >> +} >> +EXPORT_SYMBOL(counter_signal_enum_read); > >sysfs attribute files really should be EXPORT_SYMBOL_GPL() please. > >> + >> +ssize_t counter_signal_enum_write(struct counter_device *counter, >> + struct counter_signal *signal, void *priv, >> + const char *buf, size_t len) >> +{ >> + const struct counter_signal_enum_ext *const e = priv; >> + ssize_t index; >> + int err; >> + >> + if (!e->set) >> + return -EINVAL; > >Again, how can this be true? This is the same situation as counter_signal_enum_read, but for write operations instead of reads. >> + >> + index = __sysfs_match_string(e->items, e->num_items, buf); >> + if (index < 0) >> + return index; >> + >> + err = e->set(counter, signal, index); >> + if (err) >> + return err; >> + >> + return len; >> +} >> +EXPORT_SYMBOL(counter_signal_enum_write); >> + >> +ssize_t counter_signal_enum_available_read(struct counter_device *counter, >> + struct counter_signal *signal, >> + void *priv, char *buf) >> +{ >> + const struct counter_signal_enum_ext *const e = priv; >> + size_t i; >> + size_t len = 0; >> + >> + if (!e->num_items) >> + return 0; >> + >> + for (i = 0; i < e->num_items; i++) >> + len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", >> + e->items[i]); > >a list of items? In sysfs? Are you _SURE_ you want to do that? I'm modeling this behavior on the IIO *_available sysfs attributes. I realize sysfs attributes are suppose to display only a single piece of information, but I believe this is acceptable in this case due the existing usage present in IIO. I'm open to a different design, but I think a list is the most straight-forward way to expose the available options provided by the device. >> + >> + return len; >> +} >> +EXPORT_SYMBOL(counter_signal_enum_available_read); >> + >> +ssize_t counter_count_enum_read(struct counter_device *counter, >> + struct counter_count *count, void *priv, >> + char *buf) >> +{ >> + const struct counter_count_enum_ext *const e = priv; >> + int err; >> + size_t index; >> + >> + if (!e->get) >> + return -EINVAL; > > >Same comment everywhere for this... > >let's skip lower in the files... > >> +static int counter_attribute_create( >> + struct counter_device_attr_group *const group, >> + const char *const prefix, >> + const char *const name, >> + ssize_t (*show)(struct device *dev, struct device_attribute *attr, >> + char *buf), >> + ssize_t (*store)(struct device *dev, struct device_attribute *attr, >> + const char *buf, size_t len), > >Typedefs for these function pointers are good to have. > >> + void *const component) > >That's a crazy list of parameters... These are a bit verbose, so I'll rework it with some typedefs to simplify this parameter list. >> +{ >> + struct counter_device_attr *counter_attr; >> + struct device_attribute *dev_attr; >> + int err; >> + struct list_head *const attr_list = &group->attr_list; >> + >> + /* Allocate a Counter device attribute */ >> + counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL); >> + if (!counter_attr) >> + return -ENOMEM; >> + dev_attr = &counter_attr->dev_attr; >> + >> + sysfs_attr_init(&dev_attr->attr); >> + >> + /* Configure device attribute */ >> + dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", prefix, name); >> + if (!dev_attr->attr.name) { >> + err = -ENOMEM; >> + goto err_free_counter_attr; >> + } >> + if (show) { >> + dev_attr->attr.mode |= 0444; >> + dev_attr->show = show; >> + } >> + if (store) { >> + dev_attr->attr.mode |= 0200; >> + dev_attr->store = store; >> + } >> + >> + /* Store associated Counter component with attribute */ >> + counter_attr->component = component; >> + >> + /* Keep track of the attribute for later cleanup */ >> + list_add(&counter_attr->l, attr_list); >> + group->num_attr++; > >So you are dynamically creating sysfs attributes? Why not just have one >big group and only enable them if needed? That would make things a lot >simpler, right? Counter device drivers are permitted to supply their own extension structures to enable the configuration of various miscellaneous features provided by the device; since these may be extensions are unique to each driver, the respective sysfs attributes must be dynamically generated because they are not known by the Generic Counter system beforehand. In order to avoid code duplication, I also leverage this function to generate the standard Generic Counter interface sysfs attributes as well; that's why you see all components end up here regardless of whether they are standard or extensions. >> +struct signal_comp_t { > >"_t"??? These are helper containers to hold component-relevant data to pass down when using the generic counter_attribute_create to generate the sysfs attributes. The *_t naming convention may not be appropriate for this case, so I can rename them to *_container or something along those lines. >> + struct counter_signal *signal; >> +}; >> + >> +static ssize_t counter_signal_show(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct counter_device *const counter = dev_get_drvdata(dev); >> + const struct counter_device_attr *const devattr = to_counter_attr(attr); >> + const struct signal_comp_t *const component = devattr->component; >> + struct counter_signal *const signal = component->signal; >> + int err; >> + struct signal_read_value val = { .buf = buf }; >> + >> + err = counter->ops->signal_read(counter, signal, &val); >> + if (err) >> + return err; >> + >> + return val.len; >> +} >> + >> +struct name_comp_t { > >"_t"??? > >Same for the rest of this file... > >> diff --git a/include/linux/counter.h b/include/linux/counter.h >> new file mode 100644 >> index 000000000000..88fc82ee30a7 >> --- /dev/null >> +++ b/include/linux/counter.h >> @@ -0,0 +1,534 @@ >> +/* SPDX-License-Identifier: GPL-2.0-only */ > >Same identifier issue. > >> +/* >> + * Counter interface >> + * Copyright (C) 2017 William Breathitt Gray > >2018! > >And again, it looks like this file can be a lot smaller, but I don't see >a user of it just yet, so I don't really know... > >thanks, > >greg k-h I'll make the updates noted in a version 8 submission, but I'll wait to submit it until you have a chance to review the rest of this current patchset. The counter device drivers in this directory (104-quad-8.c, stm32-lptimer-cnt.c, stm32-timer-cnt.c) should give you an idea of how the counter.h file is used at the moment. Thank you, William Breathitt Gray -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html