summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Breathitt Gray <vilhelm.gray@gmail.com>2019-04-02 02:30:36 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-04-25 15:33:37 -0400
commit0040a390d2fde44a03b3a05cf0cdf3e692ece60f (patch)
tree29901e0b8bc3a2e4387509bb35203b6f79f70f59
parent7df95299b94a63ec67a6389fc02dc25019a80ee8 (diff)
counter: Introduce the Generic Counter interface
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@huawei.com> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/counter/Kconfig10
-rw-r--r--drivers/counter/Makefile5
-rw-r--r--drivers/counter/counter.c1567
-rw-r--r--include/linux/counter.h510
-rw-r--r--include/linux/counter_enum.h45
8 files changed, 2148 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 8c4ee2479498..5038b36867e9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4054,6 +4054,14 @@ W: http://www.fi.muni.cz/~kas/cosa/
4054S: Maintained 4054S: Maintained
4055F: drivers/net/wan/cosa* 4055F: drivers/net/wan/cosa*
4056 4056
4057COUNTER SUBSYSTEM
4058M: William Breathitt Gray <vilhelm.gray@gmail.com>
4059L: linux-iio@vger.kernel.org
4060S: Maintained
4061F: drivers/counter/
4062F: include/linux/counter.h
4063F: include/linux/counter_enum.h
4064
4057CPMAC ETHERNET DRIVER 4065CPMAC ETHERNET DRIVER
4058M: Florian Fainelli <f.fainelli@gmail.com> 4066M: Florian Fainelli <f.fainelli@gmail.com>
4059L: netdev@vger.kernel.org 4067L: netdev@vger.kernel.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 45f9decb9848..e8231663f201 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -230,4 +230,6 @@ source "drivers/slimbus/Kconfig"
230 230
231source "drivers/interconnect/Kconfig" 231source "drivers/interconnect/Kconfig"
232 232
233source "drivers/counter/Kconfig"
234
233endmenu 235endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index c61cde554340..28b030d7988d 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -187,3 +187,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
187obj-$(CONFIG_SIOX) += siox/ 187obj-$(CONFIG_SIOX) += siox/
188obj-$(CONFIG_GNSS) += gnss/ 188obj-$(CONFIG_GNSS) += gnss/
189obj-$(CONFIG_INTERCONNECT) += interconnect/ 189obj-$(CONFIG_INTERCONNECT) += interconnect/
190obj-$(CONFIG_COUNTER) += counter/
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
new file mode 100644
index 000000000000..a74998400282
--- /dev/null
+++ b/drivers/counter/Kconfig
@@ -0,0 +1,10 @@
1#
2# Counter devices
3#
4
5menuconfig COUNTER
6 tristate "Counter support"
7 help
8 This enables counter device support through the Generic Counter
9 interface. You only need to enable this, if you also want to enable
10 one or more of the counter device drivers below.
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
new file mode 100644
index 000000000000..b1464604bdbe
--- /dev/null
+++ b/drivers/counter/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for Counter devices
3#
4
5obj-$(CONFIG_COUNTER) += counter.o
diff --git a/drivers/counter/counter.c b/drivers/counter/counter.c
new file mode 100644
index 000000000000..106bc7180cd8
--- /dev/null
+++ b/drivers/counter/counter.c
@@ -0,0 +1,1567 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Generic Counter interface
4 * Copyright (C) 2018 William Breathitt Gray
5 */
6#include <linux/counter.h>
7#include <linux/device.h>
8#include <linux/err.h>
9#include <linux/export.h>
10#include <linux/fs.h>
11#include <linux/gfp.h>
12#include <linux/idr.h>
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/list.h>
16#include <linux/module.h>
17#include <linux/printk.h>
18#include <linux/slab.h>
19#include <linux/string.h>
20#include <linux/sysfs.h>
21#include <linux/types.h>
22
23const char *const counter_count_direction_str[2] = {
24 [COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
25 [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
26};
27EXPORT_SYMBOL_GPL(counter_count_direction_str);
28
29const char *const counter_count_mode_str[4] = {
30 [COUNTER_COUNT_MODE_NORMAL] = "normal",
31 [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
32 [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
33 [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
34};
35EXPORT_SYMBOL_GPL(counter_count_mode_str);
36
37ssize_t counter_signal_enum_read(struct counter_device *counter,
38 struct counter_signal *signal, void *priv,
39 char *buf)
40{
41 const struct counter_signal_enum_ext *const e = priv;
42 int err;
43 size_t index;
44
45 if (!e->get)
46 return -EINVAL;
47
48 err = e->get(counter, signal, &index);
49 if (err)
50 return err;
51
52 if (index >= e->num_items)
53 return -EINVAL;
54
55 return sprintf(buf, "%s\n", e->items[index]);
56}
57EXPORT_SYMBOL_GPL(counter_signal_enum_read);
58
59ssize_t counter_signal_enum_write(struct counter_device *counter,
60 struct counter_signal *signal, void *priv,
61 const char *buf, size_t len)
62{
63 const struct counter_signal_enum_ext *const e = priv;
64 ssize_t index;
65 int err;
66
67 if (!e->set)
68 return -EINVAL;
69
70 index = __sysfs_match_string(e->items, e->num_items, buf);
71 if (index < 0)
72 return index;
73
74 err = e->set(counter, signal, index);
75 if (err)
76 return err;
77
78 return len;
79}
80EXPORT_SYMBOL_GPL(counter_signal_enum_write);
81
82ssize_t counter_signal_enum_available_read(struct counter_device *counter,
83 struct counter_signal *signal,
84 void *priv, char *buf)
85{
86 const struct counter_signal_enum_ext *const e = priv;
87 size_t i;
88 size_t len = 0;
89
90 if (!e->num_items)
91 return 0;
92
93 for (i = 0; i < e->num_items; i++)
94 len += sprintf(buf + len, "%s\n", e->items[i]);
95
96 return len;
97}
98EXPORT_SYMBOL_GPL(counter_signal_enum_available_read);
99
100ssize_t counter_count_enum_read(struct counter_device *counter,
101 struct counter_count *count, void *priv,
102 char *buf)
103{
104 const struct counter_count_enum_ext *const e = priv;
105 int err;
106 size_t index;
107
108 if (!e->get)
109 return -EINVAL;
110
111 err = e->get(counter, count, &index);
112 if (err)
113 return err;
114
115 if (index >= e->num_items)
116 return -EINVAL;
117
118 return sprintf(buf, "%s\n", e->items[index]);
119}
120EXPORT_SYMBOL_GPL(counter_count_enum_read);
121
122ssize_t counter_count_enum_write(struct counter_device *counter,
123 struct counter_count *count, void *priv,
124 const char *buf, size_t len)
125{
126 const struct counter_count_enum_ext *const e = priv;
127 ssize_t index;
128 int err;
129
130 if (!e->set)
131 return -EINVAL;
132
133 index = __sysfs_match_string(e->items, e->num_items, buf);
134 if (index < 0)
135 return index;
136
137 err = e->set(counter, count, index);
138 if (err)
139 return err;
140
141 return len;
142}
143EXPORT_SYMBOL_GPL(counter_count_enum_write);
144
145ssize_t counter_count_enum_available_read(struct counter_device *counter,
146 struct counter_count *count,
147 void *priv, char *buf)
148{
149 const struct counter_count_enum_ext *const e = priv;
150 size_t i;
151 size_t len = 0;
152
153 if (!e->num_items)
154 return 0;
155
156 for (i = 0; i < e->num_items; i++)
157 len += sprintf(buf + len, "%s\n", e->items[i]);
158
159 return len;
160}
161EXPORT_SYMBOL_GPL(counter_count_enum_available_read);
162
163ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
164 char *buf)
165{
166 const struct counter_device_enum_ext *const e = priv;
167 int err;
168 size_t index;
169
170 if (!e->get)
171 return -EINVAL;
172
173 err = e->get(counter, &index);
174 if (err)
175 return err;
176
177 if (index >= e->num_items)
178 return -EINVAL;
179
180 return sprintf(buf, "%s\n", e->items[index]);
181}
182EXPORT_SYMBOL_GPL(counter_device_enum_read);
183
184ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
185 const char *buf, size_t len)
186{
187 const struct counter_device_enum_ext *const e = priv;
188 ssize_t index;
189 int err;
190
191 if (!e->set)
192 return -EINVAL;
193
194 index = __sysfs_match_string(e->items, e->num_items, buf);
195 if (index < 0)
196 return index;
197
198 err = e->set(counter, index);
199 if (err)
200 return err;
201
202 return len;
203}
204EXPORT_SYMBOL_GPL(counter_device_enum_write);
205
206ssize_t counter_device_enum_available_read(struct counter_device *counter,
207 void *priv, char *buf)
208{
209 const struct counter_device_enum_ext *const e = priv;
210 size_t i;
211 size_t len = 0;
212
213 if (!e->num_items)
214 return 0;
215
216 for (i = 0; i < e->num_items; i++)
217 len += sprintf(buf + len, "%s\n", e->items[i]);
218
219 return len;
220}
221EXPORT_SYMBOL_GPL(counter_device_enum_available_read);
222
223static const char *const counter_signal_level_str[] = {
224 [COUNTER_SIGNAL_LEVEL_LOW] = "low",
225 [COUNTER_SIGNAL_LEVEL_HIGH] = "high"
226};
227
228/**
229 * counter_signal_read_value_set - set counter_signal_read_value data
230 * @val: counter_signal_read_value structure to set
231 * @type: property Signal data represents
232 * @data: Signal data
233 *
234 * This function sets an opaque counter_signal_read_value structure with the
235 * provided Signal data.
236 */
237void counter_signal_read_value_set(struct counter_signal_read_value *const val,
238 const enum counter_signal_value_type type,
239 void *const data)
240{
241 if (type == COUNTER_SIGNAL_LEVEL)
242 val->len = sprintf(val->buf, "%s\n",
243 counter_signal_level_str[*(enum counter_signal_level *)data]);
244 else
245 val->len = 0;
246}
247EXPORT_SYMBOL_GPL(counter_signal_read_value_set);
248
249/**
250 * counter_count_read_value_set - set counter_count_read_value data
251 * @val: counter_count_read_value structure to set
252 * @type: property Count data represents
253 * @data: Count data
254 *
255 * This function sets an opaque counter_count_read_value structure with the
256 * provided Count data.
257 */
258void counter_count_read_value_set(struct counter_count_read_value *const val,
259 const enum counter_count_value_type type,
260 void *const data)
261{
262 switch (type) {
263 case COUNTER_COUNT_POSITION:
264 val->len = sprintf(val->buf, "%lu\n", *(unsigned long *)data);
265 break;
266 default:
267 val->len = 0;
268 }
269}
270EXPORT_SYMBOL_GPL(counter_count_read_value_set);
271
272/**
273 * counter_count_write_value_get - get counter_count_write_value data
274 * @data: Count data
275 * @type: property Count data represents
276 * @val: counter_count_write_value structure containing data
277 *
278 * This function extracts Count data from the provided opaque
279 * counter_count_write_value structure and stores it at the address provided by
280 * @data.
281 *
282 * RETURNS:
283 * 0 on success, negative error number on failure.
284 */
285int counter_count_write_value_get(void *const data,
286 const enum counter_count_value_type type,
287 const struct counter_count_write_value *const val)
288{
289 int err;
290
291 switch (type) {
292 case COUNTER_COUNT_POSITION:
293 err = kstrtoul(val->buf, 0, data);
294 if (err)
295 return err;
296 break;
297 }
298
299 return 0;
300}
301EXPORT_SYMBOL_GPL(counter_count_write_value_get);
302
303struct counter_attr_parm {
304 struct counter_device_attr_group *group;
305 const char *prefix;
306 const char *name;
307 ssize_t (*show)(struct device *dev, struct device_attribute *attr,
308 char *buf);
309 ssize_t (*store)(struct device *dev, struct device_attribute *attr,
310 const char *buf, size_t len);
311 void *component;
312};
313
314struct counter_device_attr {
315 struct device_attribute dev_attr;
316 struct list_head l;
317 void *component;
318};
319
320static int counter_attribute_create(const struct counter_attr_parm *const parm)
321{
322 struct counter_device_attr *counter_attr;
323 struct device_attribute *dev_attr;
324 int err;
325 struct list_head *const attr_list = &parm->group->attr_list;
326
327 /* Allocate a Counter device attribute */
328 counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
329 if (!counter_attr)
330 return -ENOMEM;
331 dev_attr = &counter_attr->dev_attr;
332
333 sysfs_attr_init(&dev_attr->attr);
334
335 /* Configure device attribute */
336 dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", parm->prefix,
337 parm->name);
338 if (!dev_attr->attr.name) {
339 err = -ENOMEM;
340 goto err_free_counter_attr;
341 }
342 if (parm->show) {
343 dev_attr->attr.mode |= 0444;
344 dev_attr->show = parm->show;
345 }
346 if (parm->store) {
347 dev_attr->attr.mode |= 0200;
348 dev_attr->store = parm->store;
349 }
350
351 /* Store associated Counter component with attribute */
352 counter_attr->component = parm->component;
353
354 /* Keep track of the attribute for later cleanup */
355 list_add(&counter_attr->l, attr_list);
356 parm->group->num_attr++;
357
358 return 0;
359
360err_free_counter_attr:
361 kfree(counter_attr);
362 return err;
363}
364
365#define to_counter_attr(_dev_attr) \
366 container_of(_dev_attr, struct counter_device_attr, dev_attr)
367
368struct counter_signal_unit {
369 struct counter_signal *signal;
370};
371
372static ssize_t counter_signal_show(struct device *dev,
373 struct device_attribute *attr, char *buf)
374{
375 struct counter_device *const counter = dev_get_drvdata(dev);
376 const struct counter_device_attr *const devattr = to_counter_attr(attr);
377 const struct counter_signal_unit *const component = devattr->component;
378 struct counter_signal *const signal = component->signal;
379 int err;
380 struct counter_signal_read_value val = { .buf = buf };
381
382 err = counter->ops->signal_read(counter, signal, &val);
383 if (err)
384 return err;
385
386 return val.len;
387}
388
389struct counter_name_unit {
390 const char *name;
391};
392
393static ssize_t counter_device_attr_name_show(struct device *dev,
394 struct device_attribute *attr,
395 char *buf)
396{
397 const struct counter_name_unit *const comp = to_counter_attr(attr)->component;
398
399 return sprintf(buf, "%s\n", comp->name);
400}
401
402static int counter_name_attribute_create(
403 struct counter_device_attr_group *const group,
404 const char *const name)
405{
406 struct counter_name_unit *name_comp;
407 struct counter_attr_parm parm;
408 int err;
409
410 /* Skip if no name */
411 if (!name)
412 return 0;
413
414 /* Allocate name attribute component */
415 name_comp = kmalloc(sizeof(*name_comp), GFP_KERNEL);
416 if (!name_comp)
417 return -ENOMEM;
418 name_comp->name = name;
419
420 /* Allocate Signal name attribute */
421 parm.group = group;
422 parm.prefix = "";
423 parm.name = "name";
424 parm.show = counter_device_attr_name_show;
425 parm.store = NULL;
426 parm.component = name_comp;
427 err = counter_attribute_create(&parm);
428 if (err)
429 goto err_free_name_comp;
430
431 return 0;
432
433err_free_name_comp:
434 kfree(name_comp);
435 return err;
436}
437
438struct counter_signal_ext_unit {
439 struct counter_signal *signal;
440 const struct counter_signal_ext *ext;
441};
442
443static ssize_t counter_signal_ext_show(struct device *dev,
444 struct device_attribute *attr, char *buf)
445{
446 const struct counter_device_attr *const devattr = to_counter_attr(attr);
447 const struct counter_signal_ext_unit *const comp = devattr->component;
448 const struct counter_signal_ext *const ext = comp->ext;
449
450 return ext->read(dev_get_drvdata(dev), comp->signal, ext->priv, buf);
451}
452
453static ssize_t counter_signal_ext_store(struct device *dev,
454 struct device_attribute *attr,
455 const char *buf, size_t len)
456{
457 const struct counter_device_attr *const devattr = to_counter_attr(attr);
458 const struct counter_signal_ext_unit *const comp = devattr->component;
459 const struct counter_signal_ext *const ext = comp->ext;
460
461 return ext->write(dev_get_drvdata(dev), comp->signal, ext->priv, buf,
462 len);
463}
464
465static void counter_device_attr_list_free(struct list_head *attr_list)
466{
467 struct counter_device_attr *p, *n;
468
469 list_for_each_entry_safe(p, n, attr_list, l) {
470 /* free attribute name and associated component memory */
471 kfree(p->dev_attr.attr.name);
472 kfree(p->component);
473 list_del(&p->l);
474 kfree(p);
475 }
476}
477
478static int counter_signal_ext_register(
479 struct counter_device_attr_group *const group,
480 struct counter_signal *const signal)
481{
482 const size_t num_ext = signal->num_ext;
483 size_t i;
484 const struct counter_signal_ext *ext;
485 struct counter_signal_ext_unit *signal_ext_comp;
486 struct counter_attr_parm parm;
487 int err;
488
489 /* Create an attribute for each extension */
490 for (i = 0 ; i < num_ext; i++) {
491 ext = signal->ext + i;
492
493 /* Allocate signal_ext attribute component */
494 signal_ext_comp = kmalloc(sizeof(*signal_ext_comp), GFP_KERNEL);
495 if (!signal_ext_comp) {
496 err = -ENOMEM;
497 goto err_free_attr_list;
498 }
499 signal_ext_comp->signal = signal;
500 signal_ext_comp->ext = ext;
501
502 /* Allocate a Counter device attribute */
503 parm.group = group;
504 parm.prefix = "";
505 parm.name = ext->name;
506 parm.show = (ext->read) ? counter_signal_ext_show : NULL;
507 parm.store = (ext->write) ? counter_signal_ext_store : NULL;
508 parm.component = signal_ext_comp;
509 err = counter_attribute_create(&parm);
510 if (err) {
511 kfree(signal_ext_comp);
512 goto err_free_attr_list;
513 }
514 }
515
516 return 0;
517
518err_free_attr_list:
519 counter_device_attr_list_free(&group->attr_list);
520 return err;
521}
522
523static int counter_signal_attributes_create(
524 struct counter_device_attr_group *const group,
525 const struct counter_device *const counter,
526 struct counter_signal *const signal)
527{
528 struct counter_signal_unit *signal_comp;
529 struct counter_attr_parm parm;
530 int err;
531
532 /* Allocate Signal attribute component */
533 signal_comp = kmalloc(sizeof(*signal_comp), GFP_KERNEL);
534 if (!signal_comp)
535 return -ENOMEM;
536 signal_comp->signal = signal;
537
538 /* Create main Signal attribute */
539 parm.group = group;
540 parm.prefix = "";
541 parm.name = "signal";
542 parm.show = (counter->ops->signal_read) ? counter_signal_show : NULL;
543 parm.store = NULL;
544 parm.component = signal_comp;
545 err = counter_attribute_create(&parm);
546 if (err) {
547 kfree(signal_comp);
548 return err;
549 }
550
551 /* Create Signal name attribute */
552 err = counter_name_attribute_create(group, signal->name);
553 if (err)
554 goto err_free_attr_list;
555
556 /* Register Signal extension attributes */
557 err = counter_signal_ext_register(group, signal);
558 if (err)
559 goto err_free_attr_list;
560
561 return 0;
562
563err_free_attr_list:
564 counter_device_attr_list_free(&group->attr_list);
565 return err;
566}
567
568static int counter_signals_register(
569 struct counter_device_attr_group *const groups_list,
570 const struct counter_device *const counter)
571{
572 const size_t num_signals = counter->num_signals;
573 size_t i;
574 struct counter_signal *signal;
575 const char *name;
576 int err;
577
578 /* Register each Signal */
579 for (i = 0; i < num_signals; i++) {
580 signal = counter->signals + i;
581
582 /* Generate Signal attribute directory name */
583 name = kasprintf(GFP_KERNEL, "signal%d", signal->id);
584 if (!name) {
585 err = -ENOMEM;
586 goto err_free_attr_groups;
587 }
588 groups_list[i].attr_group.name = name;
589
590 /* Create all attributes associated with Signal */
591 err = counter_signal_attributes_create(groups_list + i, counter,
592 signal);
593 if (err)
594 goto err_free_attr_groups;
595 }
596
597 return 0;
598
599err_free_attr_groups:
600 do {
601 kfree(groups_list[i].attr_group.name);
602 counter_device_attr_list_free(&groups_list[i].attr_list);
603 } while (i--);
604 return err;
605}
606
607static const char *const counter_synapse_action_str[] = {
608 [COUNTER_SYNAPSE_ACTION_NONE] = "none",
609 [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
610 [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
611 [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
612};
613
614struct counter_action_unit {
615 struct counter_synapse *synapse;
616 struct counter_count *count;
617};
618
619static ssize_t counter_action_show(struct device *dev,
620 struct device_attribute *attr, char *buf)
621{
622 const struct counter_device_attr *const devattr = to_counter_attr(attr);
623 int err;
624 struct counter_device *const counter = dev_get_drvdata(dev);
625 const struct counter_action_unit *const component = devattr->component;
626 struct counter_count *const count = component->count;
627 struct counter_synapse *const synapse = component->synapse;
628 size_t action_index;
629 enum counter_synapse_action action;
630
631 err = counter->ops->action_get(counter, count, synapse, &action_index);
632 if (err)
633 return err;
634
635 synapse->action = action_index;
636
637 action = synapse->actions_list[action_index];
638 return sprintf(buf, "%s\n", counter_synapse_action_str[action]);
639}
640
641static ssize_t counter_action_store(struct device *dev,
642 struct device_attribute *attr,
643 const char *buf, size_t len)
644{
645 const struct counter_device_attr *const devattr = to_counter_attr(attr);
646 const struct counter_action_unit *const component = devattr->component;
647 struct counter_synapse *const synapse = component->synapse;
648 size_t action_index;
649 const size_t num_actions = synapse->num_actions;
650 enum counter_synapse_action action;
651 int err;
652 struct counter_device *const counter = dev_get_drvdata(dev);
653 struct counter_count *const count = component->count;
654
655 /* Find requested action mode */
656 for (action_index = 0; action_index < num_actions; action_index++) {
657 action = synapse->actions_list[action_index];
658 if (sysfs_streq(buf, counter_synapse_action_str[action]))
659 break;
660 }
661 /* If requested action mode not found */
662 if (action_index >= num_actions)
663 return -EINVAL;
664
665 err = counter->ops->action_set(counter, count, synapse, action_index);
666 if (err)
667 return err;
668
669 synapse->action = action_index;
670
671 return len;
672}
673
674struct counter_action_avail_unit {
675 const enum counter_synapse_action *actions_list;
676 size_t num_actions;
677};
678
679static ssize_t counter_synapse_action_available_show(struct device *dev,
680 struct device_attribute *attr, char *buf)
681{
682 const struct counter_device_attr *const devattr = to_counter_attr(attr);
683 const struct counter_action_avail_unit *const component = devattr->component;
684 size_t i;
685 enum counter_synapse_action action;
686 ssize_t len = 0;
687
688 for (i = 0; i < component->num_actions; i++) {
689 action = component->actions_list[i];
690 len += sprintf(buf + len, "%s\n",
691 counter_synapse_action_str[action]);
692 }
693
694 return len;
695}
696
697static int counter_synapses_register(
698 struct counter_device_attr_group *const group,
699 const struct counter_device *const counter,
700 struct counter_count *const count, const char *const count_attr_name)
701{
702 size_t i;
703 struct counter_synapse *synapse;
704 const char *prefix;
705 struct counter_action_unit *action_comp;
706 struct counter_attr_parm parm;
707 int err;
708 struct counter_action_avail_unit *avail_comp;
709
710 /* Register each Synapse */
711 for (i = 0; i < count->num_synapses; i++) {
712 synapse = count->synapses + i;
713
714 /* Generate attribute prefix */
715 prefix = kasprintf(GFP_KERNEL, "signal%d_",
716 synapse->signal->id);
717 if (!prefix) {
718 err = -ENOMEM;
719 goto err_free_attr_list;
720 }
721
722 /* Allocate action attribute component */
723 action_comp = kmalloc(sizeof(*action_comp), GFP_KERNEL);
724 if (!action_comp) {
725 err = -ENOMEM;
726 goto err_free_prefix;
727 }
728 action_comp->synapse = synapse;
729 action_comp->count = count;
730
731 /* Create action attribute */
732 parm.group = group;
733 parm.prefix = prefix;
734 parm.name = "action";
735 parm.show = (counter->ops->action_get) ? counter_action_show : NULL;
736 parm.store = (counter->ops->action_set) ? counter_action_store : NULL;
737 parm.component = action_comp;
738 err = counter_attribute_create(&parm);
739 if (err) {
740 kfree(action_comp);
741 goto err_free_prefix;
742 }
743
744 /* Allocate action available attribute component */
745 avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
746 if (!avail_comp) {
747 err = -ENOMEM;
748 goto err_free_prefix;
749 }
750 avail_comp->actions_list = synapse->actions_list;
751 avail_comp->num_actions = synapse->num_actions;
752
753 /* Create action_available attribute */
754 parm.group = group;
755 parm.prefix = prefix;
756 parm.name = "action_available";
757 parm.show = counter_synapse_action_available_show;
758 parm.store = NULL;
759 parm.component = avail_comp;
760 err = counter_attribute_create(&parm);
761 if (err) {
762 kfree(avail_comp);
763 goto err_free_prefix;
764 }
765
766 kfree(prefix);
767 }
768
769 return 0;
770
771err_free_prefix:
772 kfree(prefix);
773err_free_attr_list:
774 counter_device_attr_list_free(&group->attr_list);
775 return err;
776}
777
778struct counter_count_unit {
779 struct counter_count *count;
780};
781
782static ssize_t counter_count_show(struct device *dev,
783 struct device_attribute *attr,
784 char *buf)
785{
786 struct counter_device *const counter = dev_get_drvdata(dev);
787 const struct counter_device_attr *const devattr = to_counter_attr(attr);
788 const struct counter_count_unit *const component = devattr->component;
789 struct counter_count *const count = component->count;
790 int err;
791 struct counter_count_read_value val = { .buf = buf };
792
793 err = counter->ops->count_read(counter, count, &val);
794 if (err)
795 return err;
796
797 return val.len;
798}
799
800static ssize_t counter_count_store(struct device *dev,
801 struct device_attribute *attr,
802 const char *buf, size_t len)
803{
804 struct counter_device *const counter = dev_get_drvdata(dev);
805 const struct counter_device_attr *const devattr = to_counter_attr(attr);
806 const struct counter_count_unit *const component = devattr->component;
807 struct counter_count *const count = component->count;
808 int err;
809 struct counter_count_write_value val = { .buf = buf };
810
811 err = counter->ops->count_write(counter, count, &val);
812 if (err)
813 return err;
814
815 return len;
816}
817
818static const char *const counter_count_function_str[] = {
819 [COUNTER_COUNT_FUNCTION_INCREASE] = "increase",
820 [COUNTER_COUNT_FUNCTION_DECREASE] = "decrease",
821 [COUNTER_COUNT_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
822 [COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
823 [COUNTER_COUNT_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
824 [COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
825 [COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
826 [COUNTER_COUNT_FUNCTION_QUADRATURE_X4] = "quadrature x4"
827};
828
829static ssize_t counter_function_show(struct device *dev,
830 struct device_attribute *attr, char *buf)
831{
832 int err;
833 struct counter_device *const counter = dev_get_drvdata(dev);
834 const struct counter_device_attr *const devattr = to_counter_attr(attr);
835 const struct counter_count_unit *const component = devattr->component;
836 struct counter_count *const count = component->count;
837 size_t func_index;
838 enum counter_count_function function;
839
840 err = counter->ops->function_get(counter, count, &func_index);
841 if (err)
842 return err;
843
844 count->function = func_index;
845
846 function = count->functions_list[func_index];
847 return sprintf(buf, "%s\n", counter_count_function_str[function]);
848}
849
850static ssize_t counter_function_store(struct device *dev,
851 struct device_attribute *attr,
852 const char *buf, size_t len)
853{
854 const struct counter_device_attr *const devattr = to_counter_attr(attr);
855 const struct counter_count_unit *const component = devattr->component;
856 struct counter_count *const count = component->count;
857 const size_t num_functions = count->num_functions;
858 size_t func_index;
859 enum counter_count_function function;
860 int err;
861 struct counter_device *const counter = dev_get_drvdata(dev);
862
863 /* Find requested Count function mode */
864 for (func_index = 0; func_index < num_functions; func_index++) {
865 function = count->functions_list[func_index];
866 if (sysfs_streq(buf, counter_count_function_str[function]))
867 break;
868 }
869 /* Return error if requested Count function mode not found */
870 if (func_index >= num_functions)
871 return -EINVAL;
872
873 err = counter->ops->function_set(counter, count, func_index);
874 if (err)
875 return err;
876
877 count->function = func_index;
878
879 return len;
880}
881
882struct counter_count_ext_unit {
883 struct counter_count *count;
884 const struct counter_count_ext *ext;
885};
886
887static ssize_t counter_count_ext_show(struct device *dev,
888 struct device_attribute *attr, char *buf)
889{
890 const struct counter_device_attr *const devattr = to_counter_attr(attr);
891 const struct counter_count_ext_unit *const comp = devattr->component;
892 const struct counter_count_ext *const ext = comp->ext;
893
894 return ext->read(dev_get_drvdata(dev), comp->count, ext->priv, buf);
895}
896
897static ssize_t counter_count_ext_store(struct device *dev,
898 struct device_attribute *attr,
899 const char *buf, size_t len)
900{
901 const struct counter_device_attr *const devattr = to_counter_attr(attr);
902 const struct counter_count_ext_unit *const comp = devattr->component;
903 const struct counter_count_ext *const ext = comp->ext;
904
905 return ext->write(dev_get_drvdata(dev), comp->count, ext->priv, buf,
906 len);
907}
908
909static int counter_count_ext_register(
910 struct counter_device_attr_group *const group,
911 struct counter_count *const count)
912{
913 size_t i;
914 const struct counter_count_ext *ext;
915 struct counter_count_ext_unit *count_ext_comp;
916 struct counter_attr_parm parm;
917 int err;
918
919 /* Create an attribute for each extension */
920 for (i = 0 ; i < count->num_ext; i++) {
921 ext = count->ext + i;
922
923 /* Allocate count_ext attribute component */
924 count_ext_comp = kmalloc(sizeof(*count_ext_comp), GFP_KERNEL);
925 if (!count_ext_comp) {
926 err = -ENOMEM;
927 goto err_free_attr_list;
928 }
929 count_ext_comp->count = count;
930 count_ext_comp->ext = ext;
931
932 /* Allocate count_ext attribute */
933 parm.group = group;
934 parm.prefix = "";
935 parm.name = ext->name;
936 parm.show = (ext->read) ? counter_count_ext_show : NULL;
937 parm.store = (ext->write) ? counter_count_ext_store : NULL;
938 parm.component = count_ext_comp;
939 err = counter_attribute_create(&parm);
940 if (err) {
941 kfree(count_ext_comp);
942 goto err_free_attr_list;
943 }
944 }
945
946 return 0;
947
948err_free_attr_list:
949 counter_device_attr_list_free(&group->attr_list);
950 return err;
951}
952
953struct counter_func_avail_unit {
954 const enum counter_count_function *functions_list;
955 size_t num_functions;
956};
957
958static ssize_t counter_count_function_available_show(struct device *dev,
959 struct device_attribute *attr, char *buf)
960{
961 const struct counter_device_attr *const devattr = to_counter_attr(attr);
962 const struct counter_func_avail_unit *const component = devattr->component;
963 const enum counter_count_function *const func_list = component->functions_list;
964 const size_t num_functions = component->num_functions;
965 size_t i;
966 enum counter_count_function function;
967 ssize_t len = 0;
968
969 for (i = 0; i < num_functions; i++) {
970 function = func_list[i];
971 len += sprintf(buf + len, "%s\n",
972 counter_count_function_str[function]);
973 }
974
975 return len;
976}
977
978static int counter_count_attributes_create(
979 struct counter_device_attr_group *const group,
980 const struct counter_device *const counter,
981 struct counter_count *const count)
982{
983 struct counter_count_unit *count_comp;
984 struct counter_attr_parm parm;
985 int err;
986 struct counter_count_unit *func_comp;
987 struct counter_func_avail_unit *avail_comp;
988
989 /* Allocate count attribute component */
990 count_comp = kmalloc(sizeof(*count_comp), GFP_KERNEL);
991 if (!count_comp)
992 return -ENOMEM;
993 count_comp->count = count;
994
995 /* Create main Count attribute */
996 parm.group = group;
997 parm.prefix = "";
998 parm.name = "count";
999 parm.show = (counter->ops->count_read) ? counter_count_show : NULL;
1000 parm.store = (counter->ops->count_write) ? counter_count_store : NULL;
1001 parm.component = count_comp;
1002 err = counter_attribute_create(&parm);
1003 if (err) {
1004 kfree(count_comp);
1005 return err;
1006 }
1007
1008 /* Allocate function attribute component */
1009 func_comp = kmalloc(sizeof(*func_comp), GFP_KERNEL);
1010 if (!func_comp) {
1011 err = -ENOMEM;
1012 goto err_free_attr_list;
1013 }
1014 func_comp->count = count;
1015
1016 /* Create Count function attribute */
1017 parm.group = group;
1018 parm.prefix = "";
1019 parm.name = "function";
1020 parm.show = (counter->ops->function_get) ? counter_function_show : NULL;
1021 parm.store = (counter->ops->function_set) ? counter_function_store : NULL;
1022 parm.component = func_comp;
1023 err = counter_attribute_create(&parm);
1024 if (err) {
1025 kfree(func_comp);
1026 goto err_free_attr_list;
1027 }
1028
1029 /* Allocate function available attribute component */
1030 avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
1031 if (!avail_comp) {
1032 err = -ENOMEM;
1033 goto err_free_attr_list;
1034 }
1035 avail_comp->functions_list = count->functions_list;
1036 avail_comp->num_functions = count->num_functions;
1037
1038 /* Create Count function_available attribute */
1039 parm.group = group;
1040 parm.prefix = "";
1041 parm.name = "function_available";
1042 parm.show = counter_count_function_available_show;
1043 parm.store = NULL;
1044 parm.component = avail_comp;
1045 err = counter_attribute_create(&parm);
1046 if (err) {
1047 kfree(avail_comp);
1048 goto err_free_attr_list;
1049 }
1050
1051 /* Create Count name attribute */
1052 err = counter_name_attribute_create(group, count->name);
1053 if (err)
1054 goto err_free_attr_list;
1055
1056 /* Register Count extension attributes */
1057 err = counter_count_ext_register(group, count);
1058 if (err)
1059 goto err_free_attr_list;
1060
1061 return 0;
1062
1063err_free_attr_list:
1064 counter_device_attr_list_free(&group->attr_list);
1065 return err;
1066}
1067
1068static int counter_counts_register(
1069 struct counter_device_attr_group *const groups_list,
1070 const struct counter_device *const counter)
1071{
1072 size_t i;
1073 struct counter_count *count;
1074 const char *name;
1075 int err;
1076
1077 /* Register each Count */
1078 for (i = 0; i < counter->num_counts; i++) {
1079 count = counter->counts + i;
1080
1081 /* Generate Count attribute directory name */
1082 name = kasprintf(GFP_KERNEL, "count%d", count->id);
1083 if (!name) {
1084 err = -ENOMEM;
1085 goto err_free_attr_groups;
1086 }
1087 groups_list[i].attr_group.name = name;
1088
1089 /* Register the Synapses associated with each Count */
1090 err = counter_synapses_register(groups_list + i, counter, count,
1091 name);
1092 if (err)
1093 goto err_free_attr_groups;
1094
1095 /* Create all attributes associated with Count */
1096 err = counter_count_attributes_create(groups_list + i, counter,
1097 count);
1098 if (err)
1099 goto err_free_attr_groups;
1100 }
1101
1102 return 0;
1103
1104err_free_attr_groups:
1105 do {
1106 kfree(groups_list[i].attr_group.name);
1107 counter_device_attr_list_free(&groups_list[i].attr_list);
1108 } while (i--);
1109 return err;
1110}
1111
1112struct counter_size_unit {
1113 size_t size;
1114};
1115
1116static ssize_t counter_device_attr_size_show(struct device *dev,
1117 struct device_attribute *attr,
1118 char *buf)
1119{
1120 const struct counter_size_unit *const comp = to_counter_attr(attr)->component;
1121
1122 return sprintf(buf, "%zu\n", comp->size);
1123}
1124
1125static int counter_size_attribute_create(
1126 struct counter_device_attr_group *const group,
1127 const size_t size, const char *const name)
1128{
1129 struct counter_size_unit *size_comp;
1130 struct counter_attr_parm parm;
1131 int err;
1132
1133 /* Allocate size attribute component */
1134 size_comp = kmalloc(sizeof(*size_comp), GFP_KERNEL);
1135 if (!size_comp)
1136 return -ENOMEM;
1137 size_comp->size = size;
1138
1139 parm.group = group;
1140 parm.prefix = "";
1141 parm.name = name;
1142 parm.show = counter_device_attr_size_show;
1143 parm.store = NULL;
1144 parm.component = size_comp;
1145 err = counter_attribute_create(&parm);
1146 if (err)
1147 goto err_free_size_comp;
1148
1149 return 0;
1150
1151err_free_size_comp:
1152 kfree(size_comp);
1153 return err;
1154}
1155
1156struct counter_ext_unit {
1157 const struct counter_device_ext *ext;
1158};
1159
1160static ssize_t counter_device_ext_show(struct device *dev,
1161 struct device_attribute *attr, char *buf)
1162{
1163 const struct counter_device_attr *const devattr = to_counter_attr(attr);
1164 const struct counter_ext_unit *const component = devattr->component;
1165 const struct counter_device_ext *const ext = component->ext;
1166
1167 return ext->read(dev_get_drvdata(dev), ext->priv, buf);
1168}
1169
1170static ssize_t counter_device_ext_store(struct device *dev,
1171 struct device_attribute *attr,
1172 const char *buf, size_t len)
1173{
1174 const struct counter_device_attr *const devattr = to_counter_attr(attr);
1175 const struct counter_ext_unit *const component = devattr->component;
1176 const struct counter_device_ext *const ext = component->ext;
1177
1178 return ext->write(dev_get_drvdata(dev), ext->priv, buf, len);
1179}
1180
1181static int counter_device_ext_register(
1182 struct counter_device_attr_group *const group,
1183 struct counter_device *const counter)
1184{
1185 size_t i;
1186 struct counter_ext_unit *ext_comp;
1187 struct counter_attr_parm parm;
1188 int err;
1189
1190 /* Create an attribute for each extension */
1191 for (i = 0 ; i < counter->num_ext; i++) {
1192 /* Allocate extension attribute component */
1193 ext_comp = kmalloc(sizeof(*ext_comp), GFP_KERNEL);
1194 if (!ext_comp) {
1195 err = -ENOMEM;
1196 goto err_free_attr_list;
1197 }
1198
1199 ext_comp->ext = counter->ext + i;
1200
1201 /* Allocate extension attribute */
1202 parm.group = group;
1203 parm.prefix = "";
1204 parm.name = counter->ext[i].name;
1205 parm.show = (counter->ext[i].read) ? counter_device_ext_show : NULL;
1206 parm.store = (counter->ext[i].write) ? counter_device_ext_store : NULL;
1207 parm.component = ext_comp;
1208 err = counter_attribute_create(&parm);
1209 if (err) {
1210 kfree(ext_comp);
1211 goto err_free_attr_list;
1212 }
1213 }
1214
1215 return 0;
1216
1217err_free_attr_list:
1218 counter_device_attr_list_free(&group->attr_list);
1219 return err;
1220}
1221
1222static int counter_global_attr_register(
1223 struct counter_device_attr_group *const group,
1224 struct counter_device *const counter)
1225{
1226 int err;
1227
1228 /* Create name attribute */
1229 err = counter_name_attribute_create(group, counter->name);
1230 if (err)
1231 return err;
1232
1233 /* Create num_counts attribute */
1234 err = counter_size_attribute_create(group, counter->num_counts,
1235 "num_counts");
1236 if (err)
1237 goto err_free_attr_list;
1238
1239 /* Create num_signals attribute */
1240 err = counter_size_attribute_create(group, counter->num_signals,
1241 "num_signals");
1242 if (err)
1243 goto err_free_attr_list;
1244
1245 /* Register Counter device extension attributes */
1246 err = counter_device_ext_register(group, counter);
1247 if (err)
1248 goto err_free_attr_list;
1249
1250 return 0;
1251
1252err_free_attr_list:
1253 counter_device_attr_list_free(&group->attr_list);
1254 return err;
1255}
1256
1257static void counter_device_groups_list_free(
1258 struct counter_device_attr_group *const groups_list,
1259 const size_t num_groups)
1260{
1261 struct counter_device_attr_group *group;
1262 size_t i;
1263
1264 /* loop through all attribute groups (signals, counts, global, etc.) */
1265 for (i = 0; i < num_groups; i++) {
1266 group = groups_list + i;
1267
1268 /* free all attribute group and associated attributes memory */
1269 kfree(group->attr_group.name);
1270 kfree(group->attr_group.attrs);
1271 counter_device_attr_list_free(&group->attr_list);
1272 }
1273
1274 kfree(groups_list);
1275}
1276
1277static int counter_device_groups_list_prepare(
1278 struct counter_device *const counter)
1279{
1280 const size_t total_num_groups =
1281 counter->num_signals + counter->num_counts + 1;
1282 struct counter_device_attr_group *groups_list;
1283 size_t i;
1284 int err;
1285 size_t num_groups = 0;
1286
1287 /* Allocate space for attribute groups (signals, counts, and ext) */
1288 groups_list = kcalloc(total_num_groups, sizeof(*groups_list),
1289 GFP_KERNEL);
1290 if (!groups_list)
1291 return -ENOMEM;
1292
1293 /* Initialize attribute lists */
1294 for (i = 0; i < total_num_groups; i++)
1295 INIT_LIST_HEAD(&groups_list[i].attr_list);
1296
1297 /* Register Signals */
1298 err = counter_signals_register(groups_list, counter);
1299 if (err)
1300 goto err_free_groups_list;
1301 num_groups += counter->num_signals;
1302
1303 /* Register Counts and respective Synapses */
1304 err = counter_counts_register(groups_list + num_groups, counter);
1305 if (err)
1306 goto err_free_groups_list;
1307 num_groups += counter->num_counts;
1308
1309 /* Register Counter global attributes */
1310 err = counter_global_attr_register(groups_list + num_groups, counter);
1311 if (err)
1312 goto err_free_groups_list;
1313 num_groups++;
1314
1315 /* Store groups_list in device_state */
1316 counter->device_state->groups_list = groups_list;
1317 counter->device_state->num_groups = num_groups;
1318
1319 return 0;
1320
1321err_free_groups_list:
1322 counter_device_groups_list_free(groups_list, num_groups);
1323 return err;
1324}
1325
1326static int counter_device_groups_prepare(
1327 struct counter_device_state *const device_state)
1328{
1329 size_t i, j;
1330 struct counter_device_attr_group *group;
1331 int err;
1332 struct counter_device_attr *p;
1333
1334 /* Allocate attribute groups for association with device */
1335 device_state->groups = kcalloc(device_state->num_groups + 1,
1336 sizeof(*device_state->groups),
1337 GFP_KERNEL);
1338 if (!device_state->groups)
1339 return -ENOMEM;
1340
1341 /* Prepare each group of attributes for association */
1342 for (i = 0; i < device_state->num_groups; i++) {
1343 group = device_state->groups_list + i;
1344
1345 /* Allocate space for attribute pointers in attribute group */
1346 group->attr_group.attrs = kcalloc(group->num_attr + 1,
1347 sizeof(*group->attr_group.attrs), GFP_KERNEL);
1348 if (!group->attr_group.attrs) {
1349 err = -ENOMEM;
1350 goto err_free_groups;
1351 }
1352
1353 /* Add attribute pointers to attribute group */
1354 j = 0;
1355 list_for_each_entry(p, &group->attr_list, l)
1356 group->attr_group.attrs[j++] = &p->dev_attr.attr;
1357
1358 /* Group attributes in attribute group */
1359 device_state->groups[i] = &group->attr_group;
1360 }
1361 /* Associate attributes with device */
1362 device_state->dev.groups = device_state->groups;
1363
1364 return 0;
1365
1366err_free_groups:
1367 do {
1368 group = device_state->groups_list + i;
1369 kfree(group->attr_group.attrs);
1370 group->attr_group.attrs = NULL;
1371 } while (i--);
1372 kfree(device_state->groups);
1373 return err;
1374}
1375
1376/* Provides a unique ID for each counter device */
1377static DEFINE_IDA(counter_ida);
1378
1379static void counter_device_release(struct device *dev)
1380{
1381 struct counter_device *const counter = dev_get_drvdata(dev);
1382 struct counter_device_state *const device_state = counter->device_state;
1383
1384 kfree(device_state->groups);
1385 counter_device_groups_list_free(device_state->groups_list,
1386 device_state->num_groups);
1387 ida_simple_remove(&counter_ida, device_state->id);
1388 kfree(device_state);
1389}
1390
1391static struct device_type counter_device_type = {
1392 .name = "counter_device",
1393 .release = counter_device_release
1394};
1395
1396static struct bus_type counter_bus_type = {
1397 .name = "counter"
1398};
1399
1400/**
1401 * counter_register - register Counter to the system
1402 * @counter: pointer to Counter to register
1403 *
1404 * This function registers a Counter to the system. A sysfs "counter" directory
1405 * will be created and populated with sysfs attributes correlating with the
1406 * Counter Signals, Synapses, and Counts respectively.
1407 */
1408int counter_register(struct counter_device *const counter)
1409{
1410 struct counter_device_state *device_state;
1411 int err;
1412
1413 /* Allocate internal state container for Counter device */
1414 device_state = kzalloc(sizeof(*device_state), GFP_KERNEL);
1415 if (!device_state)
1416 return -ENOMEM;
1417 counter->device_state = device_state;
1418
1419 /* Acquire unique ID */
1420 device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
1421 if (device_state->id < 0) {
1422 err = device_state->id;
1423 goto err_free_device_state;
1424 }
1425
1426 /* Configure device structure for Counter */
1427 device_state->dev.type = &counter_device_type;
1428 device_state->dev.bus = &counter_bus_type;
1429 if (counter->parent) {
1430 device_state->dev.parent = counter->parent;
1431 device_state->dev.of_node = counter->parent->of_node;
1432 }
1433 dev_set_name(&device_state->dev, "counter%d", device_state->id);
1434 device_initialize(&device_state->dev);
1435 dev_set_drvdata(&device_state->dev, counter);
1436
1437 /* Prepare device attributes */
1438 err = counter_device_groups_list_prepare(counter);
1439 if (err)
1440 goto err_free_id;
1441
1442 /* Organize device attributes to groups and match to device */
1443 err = counter_device_groups_prepare(device_state);
1444 if (err)
1445 goto err_free_groups_list;
1446
1447 /* Add device to system */
1448 err = device_add(&device_state->dev);
1449 if (err)
1450 goto err_free_groups;
1451
1452 return 0;
1453
1454err_free_groups:
1455 kfree(device_state->groups);
1456err_free_groups_list:
1457 counter_device_groups_list_free(device_state->groups_list,
1458 device_state->num_groups);
1459err_free_id:
1460 ida_simple_remove(&counter_ida, device_state->id);
1461err_free_device_state:
1462 kfree(device_state);
1463 return err;
1464}
1465EXPORT_SYMBOL_GPL(counter_register);
1466
1467/**
1468 * counter_unregister - unregister Counter from the system
1469 * @counter: pointer to Counter to unregister
1470 *
1471 * The Counter is unregistered from the system; all allocated memory is freed.
1472 */
1473void counter_unregister(struct counter_device *const counter)
1474{
1475 if (counter)
1476 device_del(&counter->device_state->dev);
1477}
1478EXPORT_SYMBOL_GPL(counter_unregister);
1479
1480static void devm_counter_unreg(struct device *dev, void *res)
1481{
1482 counter_unregister(*(struct counter_device **)res);
1483}
1484
1485/**
1486 * devm_counter_register - Resource-managed counter_register
1487 * @dev: device to allocate counter_device for
1488 * @counter: pointer to Counter to register
1489 *
1490 * Managed counter_register. The Counter registered with this function is
1491 * automatically unregistered on driver detach. This function calls
1492 * counter_register internally. Refer to that function for more information.
1493 *
1494 * If an Counter registered with this function needs to be unregistered
1495 * separately, devm_counter_unregister must be used.
1496 *
1497 * RETURNS:
1498 * 0 on success, negative error number on failure.
1499 */
1500int devm_counter_register(struct device *dev,
1501 struct counter_device *const counter)
1502{
1503 struct counter_device **ptr;
1504 int ret;
1505
1506 ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL);
1507 if (!ptr)
1508 return -ENOMEM;
1509
1510 ret = counter_register(counter);
1511 if (!ret) {
1512 *ptr = counter;
1513 devres_add(dev, ptr);
1514 } else {
1515 devres_free(ptr);
1516 }
1517
1518 return ret;
1519}
1520EXPORT_SYMBOL_GPL(devm_counter_register);
1521
1522static int devm_counter_match(struct device *dev, void *res, void *data)
1523{
1524 struct counter_device **r = res;
1525
1526 if (!r || !*r) {
1527 WARN_ON(!r || !*r);
1528 return 0;
1529 }
1530
1531 return *r == data;
1532}
1533
1534/**
1535 * devm_counter_unregister - Resource-managed counter_unregister
1536 * @dev: device this counter_device belongs to
1537 * @counter: pointer to Counter associated with the device
1538 *
1539 * Unregister Counter registered with devm_counter_register.
1540 */
1541void devm_counter_unregister(struct device *dev,
1542 struct counter_device *const counter)
1543{
1544 int rc;
1545
1546 rc = devres_release(dev, devm_counter_unreg, devm_counter_match,
1547 counter);
1548 WARN_ON(rc);
1549}
1550EXPORT_SYMBOL_GPL(devm_counter_unregister);
1551
1552static int __init counter_init(void)
1553{
1554 return bus_register(&counter_bus_type);
1555}
1556
1557static void __exit counter_exit(void)
1558{
1559 bus_unregister(&counter_bus_type);
1560}
1561
1562subsys_initcall(counter_init);
1563module_exit(counter_exit);
1564
1565MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1566MODULE_DESCRIPTION("Generic Counter interface");
1567MODULE_LICENSE("GPL v2");
diff --git a/include/linux/counter.h b/include/linux/counter.h
new file mode 100644
index 000000000000..a061cdcdef7c
--- /dev/null
+++ b/include/linux/counter.h
@@ -0,0 +1,510 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Counter interface
4 * Copyright (C) 2018 William Breathitt Gray
5 */
6#ifndef _COUNTER_H_
7#define _COUNTER_H_
8
9#include <linux/counter_enum.h>
10#include <linux/device.h>
11#include <linux/types.h>
12
13enum counter_count_direction {
14 COUNTER_COUNT_DIRECTION_FORWARD = 0,
15 COUNTER_COUNT_DIRECTION_BACKWARD
16};
17extern const char *const counter_count_direction_str[2];
18
19enum counter_count_mode {
20 COUNTER_COUNT_MODE_NORMAL = 0,
21 COUNTER_COUNT_MODE_RANGE_LIMIT,
22 COUNTER_COUNT_MODE_NON_RECYCLE,
23 COUNTER_COUNT_MODE_MODULO_N
24};
25extern const char *const counter_count_mode_str[4];
26
27struct counter_device;
28struct counter_signal;
29
30/**
31 * struct counter_signal_ext - Counter Signal extensions
32 * @name: attribute name
33 * @read: read callback for this attribute; may be NULL
34 * @write: write callback for this attribute; may be NULL
35 * @priv: data private to the driver
36 */
37struct counter_signal_ext {
38 const char *name;
39 ssize_t (*read)(struct counter_device *counter,
40 struct counter_signal *signal, void *priv, char *buf);
41 ssize_t (*write)(struct counter_device *counter,
42 struct counter_signal *signal, void *priv,
43 const char *buf, size_t len);
44 void *priv;
45};
46
47/**
48 * struct counter_signal - Counter Signal node
49 * @id: unique ID used to identify signal
50 * @name: device-specific Signal name; ideally, this should match the name
51 * as it appears in the datasheet documentation
52 * @ext: optional array of Counter Signal extensions
53 * @num_ext: number of Counter Signal extensions specified in @ext
54 * @priv: optional private data supplied by driver
55 */
56struct counter_signal {
57 int id;
58 const char *name;
59
60 const struct counter_signal_ext *ext;
61 size_t num_ext;
62
63 void *priv;
64};
65
66/**
67 * struct counter_signal_enum_ext - Signal enum extension attribute
68 * @items: Array of strings
69 * @num_items: Number of items specified in @items
70 * @set: Set callback function; may be NULL
71 * @get: Get callback function; may be NULL
72 *
73 * The counter_signal_enum_ext structure can be used to implement enum style
74 * Signal extension attributes. Enum style attributes are those which have a set
75 * of strings that map to unsigned integer values. The Generic Counter Signal
76 * enum extension helper code takes care of mapping between value and string, as
77 * well as generating a "_available" file which contains a list of all available
78 * items. The get callback is used to query the currently active item; the index
79 * of the item within the respective items array is returned via the 'item'
80 * parameter. The set callback is called when the attribute is updated; the
81 * 'item' parameter contains the index of the newly activated item within the
82 * respective items array.
83 */
84struct counter_signal_enum_ext {
85 const char * const *items;
86 size_t num_items;
87 int (*get)(struct counter_device *counter,
88 struct counter_signal *signal, size_t *item);
89 int (*set)(struct counter_device *counter,
90 struct counter_signal *signal, size_t item);
91};
92
93/**
94 * COUNTER_SIGNAL_ENUM() - Initialize Signal enum extension
95 * @_name: Attribute name
96 * @_e: Pointer to a counter_signal_enum_ext structure
97 *
98 * This should usually be used together with COUNTER_SIGNAL_ENUM_AVAILABLE()
99 */
100#define COUNTER_SIGNAL_ENUM(_name, _e) \
101{ \
102 .name = (_name), \
103 .read = counter_signal_enum_read, \
104 .write = counter_signal_enum_write, \
105 .priv = (_e) \
106}
107
108/**
109 * COUNTER_SIGNAL_ENUM_AVAILABLE() - Initialize Signal enum available extension
110 * @_name: Attribute name ("_available" will be appended to the name)
111 * @_e: Pointer to a counter_signal_enum_ext structure
112 *
113 * Creates a read only attribute that lists all the available enum items in a
114 * newline separated list. This should usually be used together with
115 * COUNTER_SIGNAL_ENUM()
116 */
117#define COUNTER_SIGNAL_ENUM_AVAILABLE(_name, _e) \
118{ \
119 .name = (_name "_available"), \
120 .read = counter_signal_enum_available_read, \
121 .priv = (_e) \
122}
123
124enum counter_synapse_action {
125 COUNTER_SYNAPSE_ACTION_NONE = 0,
126 COUNTER_SYNAPSE_ACTION_RISING_EDGE,
127 COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
128 COUNTER_SYNAPSE_ACTION_BOTH_EDGES
129};
130
131/**
132 * struct counter_synapse - Counter Synapse node
133 * @action: index of current action mode
134 * @actions_list: array of available action modes
135 * @num_actions: number of action modes specified in @actions_list
136 * @signal: pointer to associated signal
137 */
138struct counter_synapse {
139 size_t action;
140 const enum counter_synapse_action *actions_list;
141 size_t num_actions;
142
143 struct counter_signal *signal;
144};
145
146struct counter_count;
147
148/**
149 * struct counter_count_ext - Counter Count extension
150 * @name: attribute name
151 * @read: read callback for this attribute; may be NULL
152 * @write: write callback for this attribute; may be NULL
153 * @priv: data private to the driver
154 */
155struct counter_count_ext {
156 const char *name;
157 ssize_t (*read)(struct counter_device *counter,
158 struct counter_count *count, void *priv, char *buf);
159 ssize_t (*write)(struct counter_device *counter,
160 struct counter_count *count, void *priv,
161 const char *buf, size_t len);
162 void *priv;
163};
164
165enum counter_count_function {
166 COUNTER_COUNT_FUNCTION_INCREASE = 0,
167 COUNTER_COUNT_FUNCTION_DECREASE,
168 COUNTER_COUNT_FUNCTION_PULSE_DIRECTION,
169 COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A,
170 COUNTER_COUNT_FUNCTION_QUADRATURE_X1_B,
171 COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A,
172 COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B,
173 COUNTER_COUNT_FUNCTION_QUADRATURE_X4
174};
175
176/**
177 * struct counter_count - Counter Count node
178 * @id: unique ID used to identify Count
179 * @name: device-specific Count name; ideally, this should match
180 * the name as it appears in the datasheet documentation
181 * @function: index of current function mode
182 * @functions_list: array available function modes
183 * @num_functions: number of function modes specified in @functions_list
184 * @synapses: array of synapses for initialization
185 * @num_synapses: number of synapses specified in @synapses
186 * @ext: optional array of Counter Count extensions
187 * @num_ext: number of Counter Count extensions specified in @ext
188 * @priv: optional private data supplied by driver
189 */
190struct counter_count {
191 int id;
192 const char *name;
193
194 size_t function;
195 const enum counter_count_function *functions_list;
196 size_t num_functions;
197
198 struct counter_synapse *synapses;
199 size_t num_synapses;
200
201 const struct counter_count_ext *ext;
202 size_t num_ext;
203
204 void *priv;
205};
206
207/**
208 * struct counter_count_enum_ext - Count enum extension attribute
209 * @items: Array of strings
210 * @num_items: Number of items specified in @items
211 * @set: Set callback function; may be NULL
212 * @get: Get callback function; may be NULL
213 *
214 * The counter_count_enum_ext structure can be used to implement enum style
215 * Count extension attributes. Enum style attributes are those which have a set
216 * of strings that map to unsigned integer values. The Generic Counter Count
217 * enum extension helper code takes care of mapping between value and string, as
218 * well as generating a "_available" file which contains a list of all available
219 * items. The get callback is used to query the currently active item; the index
220 * of the item within the respective items array is returned via the 'item'
221 * parameter. The set callback is called when the attribute is updated; the
222 * 'item' parameter contains the index of the newly activated item within the
223 * respective items array.
224 */
225struct counter_count_enum_ext {
226 const char * const *items;
227 size_t num_items;
228 int (*get)(struct counter_device *counter, struct counter_count *count,
229 size_t *item);
230 int (*set)(struct counter_device *counter, struct counter_count *count,
231 size_t item);
232};
233
234/**
235 * COUNTER_COUNT_ENUM() - Initialize Count enum extension
236 * @_name: Attribute name
237 * @_e: Pointer to a counter_count_enum_ext structure
238 *
239 * This should usually be used together with COUNTER_COUNT_ENUM_AVAILABLE()
240 */
241#define COUNTER_COUNT_ENUM(_name, _e) \
242{ \
243 .name = (_name), \
244 .read = counter_count_enum_read, \
245 .write = counter_count_enum_write, \
246 .priv = (_e) \
247}
248
249/**
250 * COUNTER_COUNT_ENUM_AVAILABLE() - Initialize Count enum available extension
251 * @_name: Attribute name ("_available" will be appended to the name)
252 * @_e: Pointer to a counter_count_enum_ext structure
253 *
254 * Creates a read only attribute that lists all the available enum items in a
255 * newline separated list. This should usually be used together with
256 * COUNTER_COUNT_ENUM()
257 */
258#define COUNTER_COUNT_ENUM_AVAILABLE(_name, _e) \
259{ \
260 .name = (_name "_available"), \
261 .read = counter_count_enum_available_read, \
262 .priv = (_e) \
263}
264
265/**
266 * struct counter_device_attr_group - internal container for attribute group
267 * @attr_group: Counter sysfs attributes group
268 * @attr_list: list to keep track of created Counter sysfs attributes
269 * @num_attr: number of Counter sysfs attributes
270 */
271struct counter_device_attr_group {
272 struct attribute_group attr_group;
273 struct list_head attr_list;
274 size_t num_attr;
275};
276
277/**
278 * struct counter_device_state - internal state container for a Counter device
279 * @id: unique ID used to identify the Counter
280 * @dev: internal device structure
281 * @groups_list: attribute groups list (for Signals, Counts, and ext)
282 * @num_groups: number of attribute groups containers
283 * @groups: Counter sysfs attribute groups (to populate @dev.groups)
284 */
285struct counter_device_state {
286 int id;
287 struct device dev;
288 struct counter_device_attr_group *groups_list;
289 size_t num_groups;
290 const struct attribute_group **groups;
291};
292
293/**
294 * struct counter_signal_read_value - Opaque Signal read value
295 * @buf: string representation of Signal read value
296 * @len: length of string in @buf
297 */
298struct counter_signal_read_value {
299 char *buf;
300 size_t len;
301};
302
303/**
304 * struct counter_count_read_value - Opaque Count read value
305 * @buf: string representation of Count read value
306 * @len: length of string in @buf
307 */
308struct counter_count_read_value {
309 char *buf;
310 size_t len;
311};
312
313/**
314 * struct counter_count_write_value - Opaque Count write value
315 * @buf: string representation of Count write value
316 */
317struct counter_count_write_value {
318 const char *buf;
319};
320
321/**
322 * struct counter_ops - Callbacks from driver
323 * @signal_read: optional read callback for Signal attribute. The read
324 * value of the respective Signal should be passed back via
325 * the val parameter. val points to an opaque type which
326 * should be set only by calling the
327 * counter_signal_read_value_set function from within the
328 * signal_read callback.
329 * @count_read: optional read callback for Count attribute. The read
330 * value of the respective Count should be passed back via
331 * the val parameter. val points to an opaque type which
332 * should be set only by calling the
333 * counter_count_read_value_set function from within the
334 * count_read callback.
335 * @count_write: optional write callback for Count attribute. The write
336 * value for the respective Count is passed in via the val
337 * parameter. val points to an opaque type which should be
338 * accessed only by calling the
339 * counter_count_write_value_get function.
340 * @function_get: function to get the current count function mode. Returns
341 * 0 on success and negative error code on error. The index
342 * of the respective Count's returned function mode should
343 * be passed back via the function parameter.
344 * @function_set: function to set the count function mode. function is the
345 * index of the requested function mode from the respective
346 * Count's functions_list array.
347 * @action_get: function to get the current action mode. Returns 0 on
348 * success and negative error code on error. The index of
349 * the respective Signal's returned action mode should be
350 * passed back via the action parameter.
351 * @action_set: function to set the action mode. action is the index of
352 * the requested action mode from the respective Synapse's
353 * actions_list array.
354 */
355struct counter_ops {
356 int (*signal_read)(struct counter_device *counter,
357 struct counter_signal *signal,
358 struct counter_signal_read_value *val);
359 int (*count_read)(struct counter_device *counter,
360 struct counter_count *count,
361 struct counter_count_read_value *val);
362 int (*count_write)(struct counter_device *counter,
363 struct counter_count *count,
364 struct counter_count_write_value *val);
365 int (*function_get)(struct counter_device *counter,
366 struct counter_count *count, size_t *function);
367 int (*function_set)(struct counter_device *counter,
368 struct counter_count *count, size_t function);
369 int (*action_get)(struct counter_device *counter,
370 struct counter_count *count,
371 struct counter_synapse *synapse, size_t *action);
372 int (*action_set)(struct counter_device *counter,
373 struct counter_count *count,
374 struct counter_synapse *synapse, size_t action);
375};
376
377/**
378 * struct counter_device_ext - Counter device extension
379 * @name: attribute name
380 * @read: read callback for this attribute; may be NULL
381 * @write: write callback for this attribute; may be NULL
382 * @priv: data private to the driver
383 */
384struct counter_device_ext {
385 const char *name;
386 ssize_t (*read)(struct counter_device *counter, void *priv, char *buf);
387 ssize_t (*write)(struct counter_device *counter, void *priv,
388 const char *buf, size_t len);
389 void *priv;
390};
391
392/**
393 * struct counter_device_enum_ext - Counter enum extension attribute
394 * @items: Array of strings
395 * @num_items: Number of items specified in @items
396 * @set: Set callback function; may be NULL
397 * @get: Get callback function; may be NULL
398 *
399 * The counter_device_enum_ext structure can be used to implement enum style
400 * Counter extension attributes. Enum style attributes are those which have a
401 * set of strings that map to unsigned integer values. The Generic Counter enum
402 * extension helper code takes care of mapping between value and string, as well
403 * as generating a "_available" file which contains a list of all available
404 * items. The get callback is used to query the currently active item; the index
405 * of the item within the respective items array is returned via the 'item'
406 * parameter. The set callback is called when the attribute is updated; the
407 * 'item' parameter contains the index of the newly activated item within the
408 * respective items array.
409 */
410struct counter_device_enum_ext {
411 const char * const *items;
412 size_t num_items;
413 int (*get)(struct counter_device *counter, size_t *item);
414 int (*set)(struct counter_device *counter, size_t item);
415};
416
417/**
418 * COUNTER_DEVICE_ENUM() - Initialize Counter enum extension
419 * @_name: Attribute name
420 * @_e: Pointer to a counter_device_enum_ext structure
421 *
422 * This should usually be used together with COUNTER_DEVICE_ENUM_AVAILABLE()
423 */
424#define COUNTER_DEVICE_ENUM(_name, _e) \
425{ \
426 .name = (_name), \
427 .read = counter_device_enum_read, \
428 .write = counter_device_enum_write, \
429 .priv = (_e) \
430}
431
432/**
433 * COUNTER_DEVICE_ENUM_AVAILABLE() - Initialize Counter enum available extension
434 * @_name: Attribute name ("_available" will be appended to the name)
435 * @_e: Pointer to a counter_device_enum_ext structure
436 *
437 * Creates a read only attribute that lists all the available enum items in a
438 * newline separated list. This should usually be used together with
439 * COUNTER_DEVICE_ENUM()
440 */
441#define COUNTER_DEVICE_ENUM_AVAILABLE(_name, _e) \
442{ \
443 .name = (_name "_available"), \
444 .read = counter_device_enum_available_read, \
445 .priv = (_e) \
446}
447
448/**
449 * struct counter_device - Counter data structure
450 * @name: name of the device as it appears in the datasheet
451 * @parent: optional parent device providing the counters
452 * @device_state: internal device state container
453 * @ops: callbacks from driver
454 * @signals: array of Signals
455 * @num_signals: number of Signals specified in @signals
456 * @counts: array of Counts
457 * @num_counts: number of Counts specified in @counts
458 * @ext: optional array of Counter device extensions
459 * @num_ext: number of Counter device extensions specified in @ext
460 * @priv: optional private data supplied by driver
461 */
462struct counter_device {
463 const char *name;
464 struct device *parent;
465 struct counter_device_state *device_state;
466
467 const struct counter_ops *ops;
468
469 struct counter_signal *signals;
470 size_t num_signals;
471 struct counter_count *counts;
472 size_t num_counts;
473
474 const struct counter_device_ext *ext;
475 size_t num_ext;
476
477 void *priv;
478};
479
480enum counter_signal_level {
481 COUNTER_SIGNAL_LEVEL_LOW = 0,
482 COUNTER_SIGNAL_LEVEL_HIGH
483};
484
485enum counter_signal_value_type {
486 COUNTER_SIGNAL_LEVEL = 0
487};
488
489enum counter_count_value_type {
490 COUNTER_COUNT_POSITION = 0,
491};
492
493void counter_signal_read_value_set(struct counter_signal_read_value *const val,
494 const enum counter_signal_value_type type,
495 void *const data);
496void counter_count_read_value_set(struct counter_count_read_value *const val,
497 const enum counter_count_value_type type,
498 void *const data);
499int counter_count_write_value_get(void *const data,
500 const enum counter_count_value_type type,
501 const struct counter_count_write_value *const val);
502
503int counter_register(struct counter_device *const counter);
504void counter_unregister(struct counter_device *const counter);
505int devm_counter_register(struct device *dev,
506 struct counter_device *const counter);
507void devm_counter_unregister(struct device *dev,
508 struct counter_device *const counter);
509
510#endif /* _COUNTER_H_ */
diff --git a/include/linux/counter_enum.h b/include/linux/counter_enum.h
new file mode 100644
index 000000000000..9f917298a88f
--- /dev/null
+++ b/include/linux/counter_enum.h
@@ -0,0 +1,45 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Counter interface enum functions
4 * Copyright (C) 2018 William Breathitt Gray
5 */
6#ifndef _COUNTER_ENUM_H_
7#define _COUNTER_ENUM_H_
8
9#include <linux/types.h>
10
11struct counter_device;
12struct counter_signal;
13struct counter_count;
14
15ssize_t counter_signal_enum_read(struct counter_device *counter,
16 struct counter_signal *signal, void *priv,
17 char *buf);
18ssize_t counter_signal_enum_write(struct counter_device *counter,
19 struct counter_signal *signal, void *priv,
20 const char *buf, size_t len);
21
22ssize_t counter_signal_enum_available_read(struct counter_device *counter,
23 struct counter_signal *signal,
24 void *priv, char *buf);
25
26ssize_t counter_count_enum_read(struct counter_device *counter,
27 struct counter_count *count, void *priv,
28 char *buf);
29ssize_t counter_count_enum_write(struct counter_device *counter,
30 struct counter_count *count, void *priv,
31 const char *buf, size_t len);
32
33ssize_t counter_count_enum_available_read(struct counter_device *counter,
34 struct counter_count *count,
35 void *priv, char *buf);
36
37ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
38 char *buf);
39ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
40 const char *buf, size_t len);
41
42ssize_t counter_device_enum_available_read(struct counter_device *counter,
43 void *priv, char *buf);
44
45#endif /* _COUNTER_ENUM_H_ */