1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Generic Counter sysfs interface
4 * Copyright (C) 2020 William Breathitt Gray
5 */
6 #include <linux/counter.h>
7 #include <linux/device.h>
8 #include <linux/err.h>
9 #include <linux/gfp.h>
10 #include <linux/kernel.h>
11 #include <linux/kstrtox.h>
12 #include <linux/list.h>
13 #include <linux/string.h>
14 #include <linux/sysfs.h>
15 #include <linux/types.h>
16
17 #include "counter-sysfs.h"
18
19 /**
20 * struct counter_attribute - Counter sysfs attribute
21 * @dev_attr: device attribute for sysfs
22 * @l: node to add Counter attribute to attribute group list
23 * @comp: Counter component callbacks and data
24 * @scope: Counter scope of the attribute
25 * @parent: pointer to the parent component
26 */
27 struct counter_attribute {
28 struct device_attribute dev_attr;
29 struct list_head l;
30
31 struct counter_comp comp;
32 enum counter_scope scope;
33 void *parent;
34 };
35
36 #define to_counter_attribute(_dev_attr) \
37 container_of(_dev_attr, struct counter_attribute, dev_attr)
38
39 /**
40 * struct counter_attribute_group - container for attribute group
41 * @name: name of the attribute group
42 * @attr_list: list to keep track of created attributes
43 * @num_attr: number of attributes
44 */
45 struct counter_attribute_group {
46 const char *name;
47 struct list_head attr_list;
48 size_t num_attr;
49 };
50
51 static const char *const counter_function_str[] = {
52 [COUNTER_FUNCTION_INCREASE] = "increase",
53 [COUNTER_FUNCTION_DECREASE] = "decrease",
54 [COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
55 [COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
56 [COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
57 [COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
58 [COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
59 [COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
60 };
61
62 static const char *const counter_signal_value_str[] = {
63 [COUNTER_SIGNAL_LEVEL_LOW] = "low",
64 [COUNTER_SIGNAL_LEVEL_HIGH] = "high"
65 };
66
67 static const char *const counter_synapse_action_str[] = {
68 [COUNTER_SYNAPSE_ACTION_NONE] = "none",
69 [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
70 [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
71 [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
72 };
73
74 static const char *const counter_count_direction_str[] = {
75 [COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
76 [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
77 };
78
79 static const char *const counter_count_mode_str[] = {
80 [COUNTER_COUNT_MODE_NORMAL] = "normal",
81 [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
82 [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
83 [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
84 };
85
counter_comp_u8_show(struct device * dev,struct device_attribute * attr,char * buf)86 static ssize_t counter_comp_u8_show(struct device *dev,
87 struct device_attribute *attr, char *buf)
88 {
89 const struct counter_attribute *const a = to_counter_attribute(attr);
90 struct counter_device *const counter = dev_get_drvdata(dev);
91 int err;
92 u8 data = 0;
93
94 switch (a->scope) {
95 case COUNTER_SCOPE_DEVICE:
96 err = a->comp.device_u8_read(counter, &data);
97 break;
98 case COUNTER_SCOPE_SIGNAL:
99 err = a->comp.signal_u8_read(counter, a->parent, &data);
100 break;
101 case COUNTER_SCOPE_COUNT:
102 err = a->comp.count_u8_read(counter, a->parent, &data);
103 break;
104 default:
105 return -EINVAL;
106 }
107 if (err < 0)
108 return err;
109
110 if (a->comp.type == COUNTER_COMP_BOOL)
111 /* data should already be boolean but ensure just to be safe */
112 data = !!data;
113
114 return sprintf(buf, "%u\n", (unsigned int)data);
115 }
116
counter_comp_u8_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)117 static ssize_t counter_comp_u8_store(struct device *dev,
118 struct device_attribute *attr,
119 const char *buf, size_t len)
120 {
121 const struct counter_attribute *const a = to_counter_attribute(attr);
122 struct counter_device *const counter = dev_get_drvdata(dev);
123 int err;
124 bool bool_data = 0;
125 u8 data = 0;
126
127 if (a->comp.type == COUNTER_COMP_BOOL) {
128 err = kstrtobool(buf, &bool_data);
129 data = bool_data;
130 } else
131 err = kstrtou8(buf, 0, &data);
132 if (err < 0)
133 return err;
134
135 switch (a->scope) {
136 case COUNTER_SCOPE_DEVICE:
137 err = a->comp.device_u8_write(counter, data);
138 break;
139 case COUNTER_SCOPE_SIGNAL:
140 err = a->comp.signal_u8_write(counter, a->parent, data);
141 break;
142 case COUNTER_SCOPE_COUNT:
143 err = a->comp.count_u8_write(counter, a->parent, data);
144 break;
145 default:
146 return -EINVAL;
147 }
148 if (err < 0)
149 return err;
150
151 return len;
152 }
153
counter_comp_u32_show(struct device * dev,struct device_attribute * attr,char * buf)154 static ssize_t counter_comp_u32_show(struct device *dev,
155 struct device_attribute *attr, char *buf)
156 {
157 const struct counter_attribute *const a = to_counter_attribute(attr);
158 struct counter_device *const counter = dev_get_drvdata(dev);
159 const struct counter_available *const avail = a->comp.priv;
160 int err;
161 u32 data = 0;
162
163 switch (a->scope) {
164 case COUNTER_SCOPE_DEVICE:
165 err = a->comp.device_u32_read(counter, &data);
166 break;
167 case COUNTER_SCOPE_SIGNAL:
168 err = a->comp.signal_u32_read(counter, a->parent, &data);
169 break;
170 case COUNTER_SCOPE_COUNT:
171 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
172 err = a->comp.action_read(counter, a->parent,
173 a->comp.priv, &data);
174 else
175 err = a->comp.count_u32_read(counter, a->parent, &data);
176 break;
177 default:
178 return -EINVAL;
179 }
180 if (err < 0)
181 return err;
182
183 switch (a->comp.type) {
184 case COUNTER_COMP_FUNCTION:
185 return sysfs_emit(buf, "%s\n", counter_function_str[data]);
186 case COUNTER_COMP_SIGNAL_LEVEL:
187 return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
188 case COUNTER_COMP_SYNAPSE_ACTION:
189 return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
190 case COUNTER_COMP_ENUM:
191 return sysfs_emit(buf, "%s\n", avail->strs[data]);
192 case COUNTER_COMP_COUNT_DIRECTION:
193 return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
194 case COUNTER_COMP_COUNT_MODE:
195 return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
196 default:
197 return sprintf(buf, "%u\n", (unsigned int)data);
198 }
199 }
200
counter_find_enum(u32 * const enum_item,const u32 * const enums,const size_t num_enums,const char * const buf,const char * const string_array[])201 static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
202 const size_t num_enums, const char *const buf,
203 const char *const string_array[])
204 {
205 size_t index;
206
207 for (index = 0; index < num_enums; index++) {
208 *enum_item = enums[index];
209 if (sysfs_streq(buf, string_array[*enum_item]))
210 return 0;
211 }
212
213 return -EINVAL;
214 }
215
counter_comp_u32_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)216 static ssize_t counter_comp_u32_store(struct device *dev,
217 struct device_attribute *attr,
218 const char *buf, size_t len)
219 {
220 const struct counter_attribute *const a = to_counter_attribute(attr);
221 struct counter_device *const counter = dev_get_drvdata(dev);
222 struct counter_count *const count = a->parent;
223 struct counter_synapse *const synapse = a->comp.priv;
224 const struct counter_available *const avail = a->comp.priv;
225 int err;
226 u32 data = 0;
227
228 switch (a->comp.type) {
229 case COUNTER_COMP_FUNCTION:
230 err = counter_find_enum(&data, count->functions_list,
231 count->num_functions, buf,
232 counter_function_str);
233 break;
234 case COUNTER_COMP_SYNAPSE_ACTION:
235 err = counter_find_enum(&data, synapse->actions_list,
236 synapse->num_actions, buf,
237 counter_synapse_action_str);
238 break;
239 case COUNTER_COMP_ENUM:
240 err = __sysfs_match_string(avail->strs, avail->num_items, buf);
241 data = err;
242 break;
243 case COUNTER_COMP_COUNT_MODE:
244 err = counter_find_enum(&data, avail->enums, avail->num_items,
245 buf, counter_count_mode_str);
246 break;
247 default:
248 err = kstrtou32(buf, 0, &data);
249 break;
250 }
251 if (err < 0)
252 return err;
253
254 switch (a->scope) {
255 case COUNTER_SCOPE_DEVICE:
256 err = a->comp.device_u32_write(counter, data);
257 break;
258 case COUNTER_SCOPE_SIGNAL:
259 err = a->comp.signal_u32_write(counter, a->parent, data);
260 break;
261 case COUNTER_SCOPE_COUNT:
262 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
263 err = a->comp.action_write(counter, count, synapse,
264 data);
265 else
266 err = a->comp.count_u32_write(counter, count, data);
267 break;
268 default:
269 return -EINVAL;
270 }
271 if (err < 0)
272 return err;
273
274 return len;
275 }
276
counter_comp_u64_show(struct device * dev,struct device_attribute * attr,char * buf)277 static ssize_t counter_comp_u64_show(struct device *dev,
278 struct device_attribute *attr, char *buf)
279 {
280 const struct counter_attribute *const a = to_counter_attribute(attr);
281 struct counter_device *const counter = dev_get_drvdata(dev);
282 int err;
283 u64 data = 0;
284
285 switch (a->scope) {
286 case COUNTER_SCOPE_DEVICE:
287 err = a->comp.device_u64_read(counter, &data);
288 break;
289 case COUNTER_SCOPE_SIGNAL:
290 err = a->comp.signal_u64_read(counter, a->parent, &data);
291 break;
292 case COUNTER_SCOPE_COUNT:
293 err = a->comp.count_u64_read(counter, a->parent, &data);
294 break;
295 default:
296 return -EINVAL;
297 }
298 if (err < 0)
299 return err;
300
301 return sprintf(buf, "%llu\n", (unsigned long long)data);
302 }
303
counter_comp_u64_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)304 static ssize_t counter_comp_u64_store(struct device *dev,
305 struct device_attribute *attr,
306 const char *buf, size_t len)
307 {
308 const struct counter_attribute *const a = to_counter_attribute(attr);
309 struct counter_device *const counter = dev_get_drvdata(dev);
310 int err;
311 u64 data = 0;
312
313 err = kstrtou64(buf, 0, &data);
314 if (err < 0)
315 return err;
316
317 switch (a->scope) {
318 case COUNTER_SCOPE_DEVICE:
319 err = a->comp.device_u64_write(counter, data);
320 break;
321 case COUNTER_SCOPE_SIGNAL:
322 err = a->comp.signal_u64_write(counter, a->parent, data);
323 break;
324 case COUNTER_SCOPE_COUNT:
325 err = a->comp.count_u64_write(counter, a->parent, data);
326 break;
327 default:
328 return -EINVAL;
329 }
330 if (err < 0)
331 return err;
332
333 return len;
334 }
335
enums_available_show(const u32 * const enums,const size_t num_enums,const char * const strs[],char * buf)336 static ssize_t enums_available_show(const u32 *const enums,
337 const size_t num_enums,
338 const char *const strs[], char *buf)
339 {
340 size_t len = 0;
341 size_t index;
342
343 for (index = 0; index < num_enums; index++)
344 len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
345
346 return len;
347 }
348
strs_available_show(const struct counter_available * const avail,char * buf)349 static ssize_t strs_available_show(const struct counter_available *const avail,
350 char *buf)
351 {
352 size_t len = 0;
353 size_t index;
354
355 for (index = 0; index < avail->num_items; index++)
356 len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
357
358 return len;
359 }
360
counter_comp_available_show(struct device * dev,struct device_attribute * attr,char * buf)361 static ssize_t counter_comp_available_show(struct device *dev,
362 struct device_attribute *attr,
363 char *buf)
364 {
365 const struct counter_attribute *const a = to_counter_attribute(attr);
366 const struct counter_count *const count = a->parent;
367 const struct counter_synapse *const synapse = a->comp.priv;
368 const struct counter_available *const avail = a->comp.priv;
369
370 switch (a->comp.type) {
371 case COUNTER_COMP_FUNCTION:
372 return enums_available_show(count->functions_list,
373 count->num_functions,
374 counter_function_str, buf);
375 case COUNTER_COMP_SYNAPSE_ACTION:
376 return enums_available_show(synapse->actions_list,
377 synapse->num_actions,
378 counter_synapse_action_str, buf);
379 case COUNTER_COMP_ENUM:
380 return strs_available_show(avail, buf);
381 case COUNTER_COMP_COUNT_MODE:
382 return enums_available_show(avail->enums, avail->num_items,
383 counter_count_mode_str, buf);
384 default:
385 return -EINVAL;
386 }
387 }
388
counter_avail_attr_create(struct device * const dev,struct counter_attribute_group * const group,const struct counter_comp * const comp,void * const parent)389 static int counter_avail_attr_create(struct device *const dev,
390 struct counter_attribute_group *const group,
391 const struct counter_comp *const comp, void *const parent)
392 {
393 struct counter_attribute *counter_attr;
394 struct device_attribute *dev_attr;
395
396 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
397 if (!counter_attr)
398 return -ENOMEM;
399
400 /* Configure Counter attribute */
401 counter_attr->comp.type = comp->type;
402 counter_attr->comp.priv = comp->priv;
403 counter_attr->parent = parent;
404
405 /* Initialize sysfs attribute */
406 dev_attr = &counter_attr->dev_attr;
407 sysfs_attr_init(&dev_attr->attr);
408
409 /* Configure device attribute */
410 dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
411 comp->name);
412 if (!dev_attr->attr.name)
413 return -ENOMEM;
414 dev_attr->attr.mode = 0444;
415 dev_attr->show = counter_comp_available_show;
416
417 /* Store list node */
418 list_add(&counter_attr->l, &group->attr_list);
419 group->num_attr++;
420
421 return 0;
422 }
423
counter_attr_create(struct device * const dev,struct counter_attribute_group * const group,const struct counter_comp * const comp,const enum counter_scope scope,void * const parent)424 static int counter_attr_create(struct device *const dev,
425 struct counter_attribute_group *const group,
426 const struct counter_comp *const comp,
427 const enum counter_scope scope,
428 void *const parent)
429 {
430 struct counter_attribute *counter_attr;
431 struct device_attribute *dev_attr;
432
433 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
434 if (!counter_attr)
435 return -ENOMEM;
436
437 /* Configure Counter attribute */
438 counter_attr->comp = *comp;
439 counter_attr->scope = scope;
440 counter_attr->parent = parent;
441
442 /* Configure device attribute */
443 dev_attr = &counter_attr->dev_attr;
444 sysfs_attr_init(&dev_attr->attr);
445 dev_attr->attr.name = comp->name;
446 switch (comp->type) {
447 case COUNTER_COMP_U8:
448 case COUNTER_COMP_BOOL:
449 if (comp->device_u8_read) {
450 dev_attr->attr.mode |= 0444;
451 dev_attr->show = counter_comp_u8_show;
452 }
453 if (comp->device_u8_write) {
454 dev_attr->attr.mode |= 0200;
455 dev_attr->store = counter_comp_u8_store;
456 }
457 break;
458 case COUNTER_COMP_SIGNAL_LEVEL:
459 case COUNTER_COMP_FUNCTION:
460 case COUNTER_COMP_SYNAPSE_ACTION:
461 case COUNTER_COMP_ENUM:
462 case COUNTER_COMP_COUNT_DIRECTION:
463 case COUNTER_COMP_COUNT_MODE:
464 if (comp->device_u32_read) {
465 dev_attr->attr.mode |= 0444;
466 dev_attr->show = counter_comp_u32_show;
467 }
468 if (comp->device_u32_write) {
469 dev_attr->attr.mode |= 0200;
470 dev_attr->store = counter_comp_u32_store;
471 }
472 break;
473 case COUNTER_COMP_U64:
474 if (comp->device_u64_read) {
475 dev_attr->attr.mode |= 0444;
476 dev_attr->show = counter_comp_u64_show;
477 }
478 if (comp->device_u64_write) {
479 dev_attr->attr.mode |= 0200;
480 dev_attr->store = counter_comp_u64_store;
481 }
482 break;
483 default:
484 return -EINVAL;
485 }
486
487 /* Store list node */
488 list_add(&counter_attr->l, &group->attr_list);
489 group->num_attr++;
490
491 /* Create "*_available" attribute if needed */
492 switch (comp->type) {
493 case COUNTER_COMP_FUNCTION:
494 case COUNTER_COMP_SYNAPSE_ACTION:
495 case COUNTER_COMP_ENUM:
496 case COUNTER_COMP_COUNT_MODE:
497 return counter_avail_attr_create(dev, group, comp, parent);
498 default:
499 return 0;
500 }
501 }
502
counter_comp_name_show(struct device * dev,struct device_attribute * attr,char * buf)503 static ssize_t counter_comp_name_show(struct device *dev,
504 struct device_attribute *attr, char *buf)
505 {
506 return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
507 }
508
counter_name_attr_create(struct device * const dev,struct counter_attribute_group * const group,const char * const name)509 static int counter_name_attr_create(struct device *const dev,
510 struct counter_attribute_group *const group,
511 const char *const name)
512 {
513 struct counter_attribute *counter_attr;
514
515 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
516 if (!counter_attr)
517 return -ENOMEM;
518
519 /* Configure Counter attribute */
520 counter_attr->comp.name = name;
521
522 /* Configure device attribute */
523 sysfs_attr_init(&counter_attr->dev_attr.attr);
524 counter_attr->dev_attr.attr.name = "name";
525 counter_attr->dev_attr.attr.mode = 0444;
526 counter_attr->dev_attr.show = counter_comp_name_show;
527
528 /* Store list node */
529 list_add(&counter_attr->l, &group->attr_list);
530 group->num_attr++;
531
532 return 0;
533 }
534
535 static struct counter_comp counter_signal_comp = {
536 .type = COUNTER_COMP_SIGNAL_LEVEL,
537 .name = "signal",
538 };
539
counter_signal_attrs_create(struct counter_device * const counter,struct counter_attribute_group * const cattr_group,struct counter_signal * const signal)540 static int counter_signal_attrs_create(struct counter_device *const counter,
541 struct counter_attribute_group *const cattr_group,
542 struct counter_signal *const signal)
543 {
544 const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
545 struct device *const dev = &counter->dev;
546 int err;
547 struct counter_comp comp;
548 size_t i;
549
550 /* Create main Signal attribute */
551 comp = counter_signal_comp;
552 comp.signal_u32_read = counter->ops->signal_read;
553 err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
554 if (err < 0)
555 return err;
556
557 /* Create Signal name attribute */
558 err = counter_name_attr_create(dev, cattr_group, signal->name);
559 if (err < 0)
560 return err;
561
562 /* Create an attribute for each extension */
563 for (i = 0; i < signal->num_ext; i++) {
564 err = counter_attr_create(dev, cattr_group, signal->ext + i,
565 scope, signal);
566 if (err < 0)
567 return err;
568 }
569
570 return 0;
571 }
572
counter_sysfs_signals_add(struct counter_device * const counter,struct counter_attribute_group * const groups)573 static int counter_sysfs_signals_add(struct counter_device *const counter,
574 struct counter_attribute_group *const groups)
575 {
576 size_t i;
577 int err;
578
579 /* Add each Signal */
580 for (i = 0; i < counter->num_signals; i++) {
581 /* Generate Signal attribute directory name */
582 groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
583 "signal%zu", i);
584 if (!groups[i].name)
585 return -ENOMEM;
586
587 /* Create all attributes associated with Signal */
588 err = counter_signal_attrs_create(counter, groups + i,
589 counter->signals + i);
590 if (err < 0)
591 return err;
592 }
593
594 return 0;
595 }
596
counter_sysfs_synapses_add(struct counter_device * const counter,struct counter_attribute_group * const group,struct counter_count * const count)597 static int counter_sysfs_synapses_add(struct counter_device *const counter,
598 struct counter_attribute_group *const group,
599 struct counter_count *const count)
600 {
601 size_t i;
602
603 /* Add each Synapse */
604 for (i = 0; i < count->num_synapses; i++) {
605 struct device *const dev = &counter->dev;
606 struct counter_synapse *synapse;
607 size_t id;
608 struct counter_comp comp;
609 int err;
610
611 synapse = count->synapses + i;
612
613 /* Generate Synapse action name */
614 id = synapse->signal - counter->signals;
615 comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
616 id);
617 if (!comp.name)
618 return -ENOMEM;
619
620 /* Create action attribute */
621 comp.type = COUNTER_COMP_SYNAPSE_ACTION;
622 comp.action_read = counter->ops->action_read;
623 comp.action_write = counter->ops->action_write;
624 comp.priv = synapse;
625 err = counter_attr_create(dev, group, &comp,
626 COUNTER_SCOPE_COUNT, count);
627 if (err < 0)
628 return err;
629 }
630
631 return 0;
632 }
633
634 static struct counter_comp counter_count_comp =
635 COUNTER_COMP_COUNT_U64("count", NULL, NULL);
636
637 static struct counter_comp counter_function_comp = {
638 .type = COUNTER_COMP_FUNCTION,
639 .name = "function",
640 };
641
counter_count_attrs_create(struct counter_device * const counter,struct counter_attribute_group * const cattr_group,struct counter_count * const count)642 static int counter_count_attrs_create(struct counter_device *const counter,
643 struct counter_attribute_group *const cattr_group,
644 struct counter_count *const count)
645 {
646 const enum counter_scope scope = COUNTER_SCOPE_COUNT;
647 struct device *const dev = &counter->dev;
648 int err;
649 struct counter_comp comp;
650 size_t i;
651
652 /* Create main Count attribute */
653 comp = counter_count_comp;
654 comp.count_u64_read = counter->ops->count_read;
655 comp.count_u64_write = counter->ops->count_write;
656 err = counter_attr_create(dev, cattr_group, &comp, scope, count);
657 if (err < 0)
658 return err;
659
660 /* Create Count name attribute */
661 err = counter_name_attr_create(dev, cattr_group, count->name);
662 if (err < 0)
663 return err;
664
665 /* Create Count function attribute */
666 comp = counter_function_comp;
667 comp.count_u32_read = counter->ops->function_read;
668 comp.count_u32_write = counter->ops->function_write;
669 err = counter_attr_create(dev, cattr_group, &comp, scope, count);
670 if (err < 0)
671 return err;
672
673 /* Create an attribute for each extension */
674 for (i = 0; i < count->num_ext; i++) {
675 err = counter_attr_create(dev, cattr_group, count->ext + i,
676 scope, count);
677 if (err < 0)
678 return err;
679 }
680
681 return 0;
682 }
683
counter_sysfs_counts_add(struct counter_device * const counter,struct counter_attribute_group * const groups)684 static int counter_sysfs_counts_add(struct counter_device *const counter,
685 struct counter_attribute_group *const groups)
686 {
687 size_t i;
688 struct counter_count *count;
689 int err;
690
691 /* Add each Count */
692 for (i = 0; i < counter->num_counts; i++) {
693 count = counter->counts + i;
694
695 /* Generate Count attribute directory name */
696 groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
697 "count%zu", i);
698 if (!groups[i].name)
699 return -ENOMEM;
700
701 /* Add sysfs attributes of the Synapses */
702 err = counter_sysfs_synapses_add(counter, groups + i, count);
703 if (err < 0)
704 return err;
705
706 /* Create all attributes associated with Count */
707 err = counter_count_attrs_create(counter, groups + i, count);
708 if (err < 0)
709 return err;
710 }
711
712 return 0;
713 }
714
counter_num_signals_read(struct counter_device * counter,u8 * val)715 static int counter_num_signals_read(struct counter_device *counter, u8 *val)
716 {
717 *val = counter->num_signals;
718 return 0;
719 }
720
counter_num_counts_read(struct counter_device * counter,u8 * val)721 static int counter_num_counts_read(struct counter_device *counter, u8 *val)
722 {
723 *val = counter->num_counts;
724 return 0;
725 }
726
727 static struct counter_comp counter_num_signals_comp =
728 COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
729
730 static struct counter_comp counter_num_counts_comp =
731 COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
732
counter_sysfs_attr_add(struct counter_device * const counter,struct counter_attribute_group * cattr_group)733 static int counter_sysfs_attr_add(struct counter_device *const counter,
734 struct counter_attribute_group *cattr_group)
735 {
736 const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
737 struct device *const dev = &counter->dev;
738 int err;
739 size_t i;
740
741 /* Add Signals sysfs attributes */
742 err = counter_sysfs_signals_add(counter, cattr_group);
743 if (err < 0)
744 return err;
745 cattr_group += counter->num_signals;
746
747 /* Add Counts sysfs attributes */
748 err = counter_sysfs_counts_add(counter, cattr_group);
749 if (err < 0)
750 return err;
751 cattr_group += counter->num_counts;
752
753 /* Create name attribute */
754 err = counter_name_attr_create(dev, cattr_group, counter->name);
755 if (err < 0)
756 return err;
757
758 /* Create num_signals attribute */
759 err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
760 scope, NULL);
761 if (err < 0)
762 return err;
763
764 /* Create num_counts attribute */
765 err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
766 scope, NULL);
767 if (err < 0)
768 return err;
769
770 /* Create an attribute for each extension */
771 for (i = 0; i < counter->num_ext; i++) {
772 err = counter_attr_create(dev, cattr_group, counter->ext + i,
773 scope, NULL);
774 if (err < 0)
775 return err;
776 }
777
778 return 0;
779 }
780
781 /**
782 * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
783 * @counter: Pointer to the Counter device structure
784 *
785 * Counter sysfs attributes are created and added to the respective device
786 * structure for later registration to the system. Resource-managed memory
787 * allocation is performed by this function, and this memory should be freed
788 * when no longer needed (automatically by a device_unregister call, or
789 * manually by a devres_release_all call).
790 */
counter_sysfs_add(struct counter_device * const counter)791 int counter_sysfs_add(struct counter_device *const counter)
792 {
793 struct device *const dev = &counter->dev;
794 const size_t num_groups = counter->num_signals + counter->num_counts + 1;
795 struct counter_attribute_group *cattr_groups;
796 size_t i, j;
797 int err;
798 struct attribute_group *groups;
799 struct counter_attribute *p;
800
801 /* Allocate space for attribute groups (signals, counts, and ext) */
802 cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
803 GFP_KERNEL);
804 if (!cattr_groups)
805 return -ENOMEM;
806
807 /* Initialize attribute lists */
808 for (i = 0; i < num_groups; i++)
809 INIT_LIST_HEAD(&cattr_groups[i].attr_list);
810
811 /* Add Counter device sysfs attributes */
812 err = counter_sysfs_attr_add(counter, cattr_groups);
813 if (err < 0)
814 return err;
815
816 /* Allocate attribute group pointers for association with device */
817 dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
818 GFP_KERNEL);
819 if (!dev->groups)
820 return -ENOMEM;
821
822 /* Allocate space for attribute groups */
823 groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
824 if (!groups)
825 return -ENOMEM;
826
827 /* Prepare each group of attributes for association */
828 for (i = 0; i < num_groups; i++) {
829 groups[i].name = cattr_groups[i].name;
830
831 /* Allocate space for attribute pointers */
832 groups[i].attrs = devm_kcalloc(dev,
833 cattr_groups[i].num_attr + 1,
834 sizeof(*groups[i].attrs),
835 GFP_KERNEL);
836 if (!groups[i].attrs)
837 return -ENOMEM;
838
839 /* Add attribute pointers to attribute group */
840 j = 0;
841 list_for_each_entry(p, &cattr_groups[i].attr_list, l)
842 groups[i].attrs[j++] = &p->dev_attr.attr;
843
844 /* Associate attribute group */
845 dev->groups[i] = &groups[i];
846 }
847
848 return 0;
849 }
850