diff options
author | William Breathitt Gray <vilhelm.gray@gmail.com> | 2019-04-02 02:30:36 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-04-25 15:33:37 -0400 |
commit | 0040a390d2fde44a03b3a05cf0cdf3e692ece60f (patch) | |
tree | 29901e0b8bc3a2e4387509bb35203b6f79f70f59 | |
parent | 7df95299b94a63ec67a6389fc02dc25019a80ee8 (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-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/counter/Kconfig | 10 | ||||
-rw-r--r-- | drivers/counter/Makefile | 5 | ||||
-rw-r--r-- | drivers/counter/counter.c | 1567 | ||||
-rw-r--r-- | include/linux/counter.h | 510 | ||||
-rw-r--r-- | include/linux/counter_enum.h | 45 |
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/ | |||
4054 | S: Maintained | 4054 | S: Maintained |
4055 | F: drivers/net/wan/cosa* | 4055 | F: drivers/net/wan/cosa* |
4056 | 4056 | ||
4057 | COUNTER SUBSYSTEM | ||
4058 | M: William Breathitt Gray <vilhelm.gray@gmail.com> | ||
4059 | L: linux-iio@vger.kernel.org | ||
4060 | S: Maintained | ||
4061 | F: drivers/counter/ | ||
4062 | F: include/linux/counter.h | ||
4063 | F: include/linux/counter_enum.h | ||
4064 | |||
4057 | CPMAC ETHERNET DRIVER | 4065 | CPMAC ETHERNET DRIVER |
4058 | M: Florian Fainelli <f.fainelli@gmail.com> | 4066 | M: Florian Fainelli <f.fainelli@gmail.com> |
4059 | L: netdev@vger.kernel.org | 4067 | L: 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 | ||
231 | source "drivers/interconnect/Kconfig" | 231 | source "drivers/interconnect/Kconfig" |
232 | 232 | ||
233 | source "drivers/counter/Kconfig" | ||
234 | |||
233 | endmenu | 235 | endmenu |
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/ | |||
187 | obj-$(CONFIG_SIOX) += siox/ | 187 | obj-$(CONFIG_SIOX) += siox/ |
188 | obj-$(CONFIG_GNSS) += gnss/ | 188 | obj-$(CONFIG_GNSS) += gnss/ |
189 | obj-$(CONFIG_INTERCONNECT) += interconnect/ | 189 | obj-$(CONFIG_INTERCONNECT) += interconnect/ |
190 | obj-$(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 | |||
5 | menuconfig 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 | |||
5 | obj-$(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 | |||
23 | const char *const counter_count_direction_str[2] = { | ||
24 | [COUNTER_COUNT_DIRECTION_FORWARD] = "forward", | ||
25 | [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward" | ||
26 | }; | ||
27 | EXPORT_SYMBOL_GPL(counter_count_direction_str); | ||
28 | |||
29 | const 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 | }; | ||
35 | EXPORT_SYMBOL_GPL(counter_count_mode_str); | ||
36 | |||
37 | ssize_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 | } | ||
57 | EXPORT_SYMBOL_GPL(counter_signal_enum_read); | ||
58 | |||
59 | ssize_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 | } | ||
80 | EXPORT_SYMBOL_GPL(counter_signal_enum_write); | ||
81 | |||
82 | ssize_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 | } | ||
98 | EXPORT_SYMBOL_GPL(counter_signal_enum_available_read); | ||
99 | |||
100 | ssize_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 | } | ||
120 | EXPORT_SYMBOL_GPL(counter_count_enum_read); | ||
121 | |||
122 | ssize_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 | } | ||
143 | EXPORT_SYMBOL_GPL(counter_count_enum_write); | ||
144 | |||
145 | ssize_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 | } | ||
161 | EXPORT_SYMBOL_GPL(counter_count_enum_available_read); | ||
162 | |||
163 | ssize_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 | } | ||
182 | EXPORT_SYMBOL_GPL(counter_device_enum_read); | ||
183 | |||
184 | ssize_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 | } | ||
204 | EXPORT_SYMBOL_GPL(counter_device_enum_write); | ||
205 | |||
206 | ssize_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 | } | ||
221 | EXPORT_SYMBOL_GPL(counter_device_enum_available_read); | ||
222 | |||
223 | static 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 | */ | ||
237 | void 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 | } | ||
247 | EXPORT_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 | */ | ||
258 | void 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 | } | ||
270 | EXPORT_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 | */ | ||
285 | int 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 | } | ||
301 | EXPORT_SYMBOL_GPL(counter_count_write_value_get); | ||
302 | |||
303 | struct 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 | |||
314 | struct counter_device_attr { | ||
315 | struct device_attribute dev_attr; | ||
316 | struct list_head l; | ||
317 | void *component; | ||
318 | }; | ||
319 | |||
320 | static 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 | |||
360 | err_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 | |||
368 | struct counter_signal_unit { | ||
369 | struct counter_signal *signal; | ||
370 | }; | ||
371 | |||
372 | static 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 | |||
389 | struct counter_name_unit { | ||
390 | const char *name; | ||
391 | }; | ||
392 | |||
393 | static 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 | |||
402 | static 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 | |||
433 | err_free_name_comp: | ||
434 | kfree(name_comp); | ||
435 | return err; | ||
436 | } | ||
437 | |||
438 | struct counter_signal_ext_unit { | ||
439 | struct counter_signal *signal; | ||
440 | const struct counter_signal_ext *ext; | ||
441 | }; | ||
442 | |||
443 | static 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 | |||
453 | static 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 | |||
465 | static 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 | |||
478 | static 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 | |||
518 | err_free_attr_list: | ||
519 | counter_device_attr_list_free(&group->attr_list); | ||
520 | return err; | ||
521 | } | ||
522 | |||
523 | static 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 | |||
563 | err_free_attr_list: | ||
564 | counter_device_attr_list_free(&group->attr_list); | ||
565 | return err; | ||
566 | } | ||
567 | |||
568 | static 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 | |||
599 | err_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 | |||
607 | static 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 | |||
614 | struct counter_action_unit { | ||
615 | struct counter_synapse *synapse; | ||
616 | struct counter_count *count; | ||
617 | }; | ||
618 | |||
619 | static 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 | |||
641 | static 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 | |||
674 | struct counter_action_avail_unit { | ||
675 | const enum counter_synapse_action *actions_list; | ||
676 | size_t num_actions; | ||
677 | }; | ||
678 | |||
679 | static 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 | |||
697 | static 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 | |||
771 | err_free_prefix: | ||
772 | kfree(prefix); | ||
773 | err_free_attr_list: | ||
774 | counter_device_attr_list_free(&group->attr_list); | ||
775 | return err; | ||
776 | } | ||
777 | |||
778 | struct counter_count_unit { | ||
779 | struct counter_count *count; | ||
780 | }; | ||
781 | |||
782 | static 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 | |||
800 | static 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 | |||
818 | static 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 | |||
829 | static 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 | |||
850 | static 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 | |||
882 | struct counter_count_ext_unit { | ||
883 | struct counter_count *count; | ||
884 | const struct counter_count_ext *ext; | ||
885 | }; | ||
886 | |||
887 | static 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 | |||
897 | static 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 | |||
909 | static 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 | |||
948 | err_free_attr_list: | ||
949 | counter_device_attr_list_free(&group->attr_list); | ||
950 | return err; | ||
951 | } | ||
952 | |||
953 | struct counter_func_avail_unit { | ||
954 | const enum counter_count_function *functions_list; | ||
955 | size_t num_functions; | ||
956 | }; | ||
957 | |||
958 | static 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 | |||
978 | static 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 | |||
1063 | err_free_attr_list: | ||
1064 | counter_device_attr_list_free(&group->attr_list); | ||
1065 | return err; | ||
1066 | } | ||
1067 | |||
1068 | static 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 | |||
1104 | err_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 | |||
1112 | struct counter_size_unit { | ||
1113 | size_t size; | ||
1114 | }; | ||
1115 | |||
1116 | static 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 | |||
1125 | static 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 | |||
1151 | err_free_size_comp: | ||
1152 | kfree(size_comp); | ||
1153 | return err; | ||
1154 | } | ||
1155 | |||
1156 | struct counter_ext_unit { | ||
1157 | const struct counter_device_ext *ext; | ||
1158 | }; | ||
1159 | |||
1160 | static 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 | |||
1170 | static 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 | |||
1181 | static 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 | |||
1217 | err_free_attr_list: | ||
1218 | counter_device_attr_list_free(&group->attr_list); | ||
1219 | return err; | ||
1220 | } | ||
1221 | |||
1222 | static 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 | |||
1252 | err_free_attr_list: | ||
1253 | counter_device_attr_list_free(&group->attr_list); | ||
1254 | return err; | ||
1255 | } | ||
1256 | |||
1257 | static 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 | |||
1277 | static 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 | |||
1321 | err_free_groups_list: | ||
1322 | counter_device_groups_list_free(groups_list, num_groups); | ||
1323 | return err; | ||
1324 | } | ||
1325 | |||
1326 | static 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 | |||
1366 | err_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 */ | ||
1377 | static DEFINE_IDA(counter_ida); | ||
1378 | |||
1379 | static 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 | |||
1391 | static struct device_type counter_device_type = { | ||
1392 | .name = "counter_device", | ||
1393 | .release = counter_device_release | ||
1394 | }; | ||
1395 | |||
1396 | static 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 | */ | ||
1408 | int 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 | |||
1454 | err_free_groups: | ||
1455 | kfree(device_state->groups); | ||
1456 | err_free_groups_list: | ||
1457 | counter_device_groups_list_free(device_state->groups_list, | ||
1458 | device_state->num_groups); | ||
1459 | err_free_id: | ||
1460 | ida_simple_remove(&counter_ida, device_state->id); | ||
1461 | err_free_device_state: | ||
1462 | kfree(device_state); | ||
1463 | return err; | ||
1464 | } | ||
1465 | EXPORT_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 | */ | ||
1473 | void counter_unregister(struct counter_device *const counter) | ||
1474 | { | ||
1475 | if (counter) | ||
1476 | device_del(&counter->device_state->dev); | ||
1477 | } | ||
1478 | EXPORT_SYMBOL_GPL(counter_unregister); | ||
1479 | |||
1480 | static 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 | */ | ||
1500 | int 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 | } | ||
1520 | EXPORT_SYMBOL_GPL(devm_counter_register); | ||
1521 | |||
1522 | static 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 | */ | ||
1541 | void 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 | } | ||
1550 | EXPORT_SYMBOL_GPL(devm_counter_unregister); | ||
1551 | |||
1552 | static int __init counter_init(void) | ||
1553 | { | ||
1554 | return bus_register(&counter_bus_type); | ||
1555 | } | ||
1556 | |||
1557 | static void __exit counter_exit(void) | ||
1558 | { | ||
1559 | bus_unregister(&counter_bus_type); | ||
1560 | } | ||
1561 | |||
1562 | subsys_initcall(counter_init); | ||
1563 | module_exit(counter_exit); | ||
1564 | |||
1565 | MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); | ||
1566 | MODULE_DESCRIPTION("Generic Counter interface"); | ||
1567 | MODULE_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 | |||
13 | enum counter_count_direction { | ||
14 | COUNTER_COUNT_DIRECTION_FORWARD = 0, | ||
15 | COUNTER_COUNT_DIRECTION_BACKWARD | ||
16 | }; | ||
17 | extern const char *const counter_count_direction_str[2]; | ||
18 | |||
19 | enum 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 | }; | ||
25 | extern const char *const counter_count_mode_str[4]; | ||
26 | |||
27 | struct counter_device; | ||
28 | struct 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 | */ | ||
37 | struct 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 | */ | ||
56 | struct 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 | */ | ||
84 | struct 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 | |||
124 | enum 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 | */ | ||
138 | struct 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 | |||
146 | struct 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 | */ | ||
155 | struct 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 | |||
165 | enum 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 | */ | ||
190 | struct 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 | */ | ||
225 | struct 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 | */ | ||
271 | struct 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 | */ | ||
285 | struct 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 | */ | ||
298 | struct 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 | */ | ||
308 | struct 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 | */ | ||
317 | struct 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 | */ | ||
355 | struct 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 | */ | ||
384 | struct 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 | */ | ||
410 | struct 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 | */ | ||
462 | struct 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 | |||
480 | enum counter_signal_level { | ||
481 | COUNTER_SIGNAL_LEVEL_LOW = 0, | ||
482 | COUNTER_SIGNAL_LEVEL_HIGH | ||
483 | }; | ||
484 | |||
485 | enum counter_signal_value_type { | ||
486 | COUNTER_SIGNAL_LEVEL = 0 | ||
487 | }; | ||
488 | |||
489 | enum counter_count_value_type { | ||
490 | COUNTER_COUNT_POSITION = 0, | ||
491 | }; | ||
492 | |||
493 | void counter_signal_read_value_set(struct counter_signal_read_value *const val, | ||
494 | const enum counter_signal_value_type type, | ||
495 | void *const data); | ||
496 | void counter_count_read_value_set(struct counter_count_read_value *const val, | ||
497 | const enum counter_count_value_type type, | ||
498 | void *const data); | ||
499 | int 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 | |||
503 | int counter_register(struct counter_device *const counter); | ||
504 | void counter_unregister(struct counter_device *const counter); | ||
505 | int devm_counter_register(struct device *dev, | ||
506 | struct counter_device *const counter); | ||
507 | void 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 | |||
11 | struct counter_device; | ||
12 | struct counter_signal; | ||
13 | struct counter_count; | ||
14 | |||
15 | ssize_t counter_signal_enum_read(struct counter_device *counter, | ||
16 | struct counter_signal *signal, void *priv, | ||
17 | char *buf); | ||
18 | ssize_t counter_signal_enum_write(struct counter_device *counter, | ||
19 | struct counter_signal *signal, void *priv, | ||
20 | const char *buf, size_t len); | ||
21 | |||
22 | ssize_t counter_signal_enum_available_read(struct counter_device *counter, | ||
23 | struct counter_signal *signal, | ||
24 | void *priv, char *buf); | ||
25 | |||
26 | ssize_t counter_count_enum_read(struct counter_device *counter, | ||
27 | struct counter_count *count, void *priv, | ||
28 | char *buf); | ||
29 | ssize_t counter_count_enum_write(struct counter_device *counter, | ||
30 | struct counter_count *count, void *priv, | ||
31 | const char *buf, size_t len); | ||
32 | |||
33 | ssize_t counter_count_enum_available_read(struct counter_device *counter, | ||
34 | struct counter_count *count, | ||
35 | void *priv, char *buf); | ||
36 | |||
37 | ssize_t counter_device_enum_read(struct counter_device *counter, void *priv, | ||
38 | char *buf); | ||
39 | ssize_t counter_device_enum_write(struct counter_device *counter, void *priv, | ||
40 | const char *buf, size_t len); | ||
41 | |||
42 | ssize_t counter_device_enum_available_read(struct counter_device *counter, | ||
43 | void *priv, char *buf); | ||
44 | |||
45 | #endif /* _COUNTER_ENUM_H_ */ | ||