Lines Matching +full:counter +full:- +full:0
1 // SPDX-License-Identifier: GPL-2.0
3 * Generic Counter character device interface
7 #include <linux/counter.h>
24 #include "counter-chrdev.h"
68 struct counter_device *const counter = filp->private_data; in counter_chrdev_read() local
72 if (!counter->ops) in counter_chrdev_read()
73 return -ENODEV; in counter_chrdev_read()
76 return -EINVAL; in counter_chrdev_read()
79 if (kfifo_is_empty(&counter->events)) { in counter_chrdev_read()
80 if (filp->f_flags & O_NONBLOCK) in counter_chrdev_read()
81 return -EAGAIN; in counter_chrdev_read()
83 err = wait_event_interruptible(counter->events_wait, in counter_chrdev_read()
84 !kfifo_is_empty(&counter->events) || in counter_chrdev_read()
85 !counter->ops); in counter_chrdev_read()
86 if (err < 0) in counter_chrdev_read()
88 if (!counter->ops) in counter_chrdev_read()
89 return -ENODEV; in counter_chrdev_read()
92 if (mutex_lock_interruptible(&counter->events_out_lock)) in counter_chrdev_read()
93 return -ERESTARTSYS; in counter_chrdev_read()
94 err = kfifo_to_user(&counter->events, buf, len, &copied); in counter_chrdev_read()
95 mutex_unlock(&counter->events_out_lock); in counter_chrdev_read()
96 if (err < 0) in counter_chrdev_read()
106 struct counter_device *const counter = filp->private_data; in counter_chrdev_poll() local
107 __poll_t events = 0; in counter_chrdev_poll()
109 if (!counter->ops) in counter_chrdev_poll()
112 poll_wait(filp, &counter->events_wait, pollt); in counter_chrdev_poll()
114 if (!kfifo_is_empty(&counter->events)) in counter_chrdev_poll()
127 list_for_each_entry_safe(q, o, &p->comp_list, l) { in counter_events_list_free()
128 list_del(&q->l); in counter_events_list_free()
133 list_del(&p->l); in counter_events_list_free()
138 static int counter_set_event_node(struct counter_device *const counter, in counter_set_event_node() argument
143 int err = 0; in counter_set_event_node()
147 list_for_each_entry(event_node, &counter->next_events_list, l) in counter_set_event_node()
148 if (event_node->event == watch->event && in counter_set_event_node()
149 event_node->channel == watch->channel) in counter_set_event_node()
153 if (&event_node->l == &counter->next_events_list) { in counter_set_event_node()
157 return -ENOMEM; in counter_set_event_node()
160 event_node->event = watch->event; in counter_set_event_node()
161 event_node->channel = watch->channel; in counter_set_event_node()
162 INIT_LIST_HEAD(&event_node->comp_list); in counter_set_event_node()
163 list_add(&event_node->l, &counter->next_events_list); in counter_set_event_node()
167 list_for_each_entry(comp_node, &event_node->comp_list, l) in counter_set_event_node()
168 if (comp_node->parent == cfg->parent && in counter_set_event_node()
169 counter_comp_read_is_equal(comp_node->comp, cfg->comp)) { in counter_set_event_node()
170 err = -EINVAL; in counter_set_event_node()
177 err = -ENOMEM; in counter_set_event_node()
183 list_add_tail(&comp_node->l, &event_node->comp_list); in counter_set_event_node()
187 if (list_empty(&event_node->comp_list)) { in counter_set_event_node()
188 list_del(&event_node->l); in counter_set_event_node()
195 static int counter_enable_events(struct counter_device *const counter) in counter_enable_events() argument
198 int err = 0; in counter_enable_events()
200 mutex_lock(&counter->n_events_list_lock); in counter_enable_events()
201 spin_lock_irqsave(&counter->events_list_lock, flags); in counter_enable_events()
203 counter_events_list_free(&counter->events_list); in counter_enable_events()
204 list_replace_init(&counter->next_events_list, in counter_enable_events()
205 &counter->events_list); in counter_enable_events()
207 if (counter->ops->events_configure) in counter_enable_events()
208 err = counter->ops->events_configure(counter); in counter_enable_events()
210 spin_unlock_irqrestore(&counter->events_list_lock, flags); in counter_enable_events()
211 mutex_unlock(&counter->n_events_list_lock); in counter_enable_events()
216 static int counter_disable_events(struct counter_device *const counter) in counter_disable_events() argument
219 int err = 0; in counter_disable_events()
221 spin_lock_irqsave(&counter->events_list_lock, flags); in counter_disable_events()
223 counter_events_list_free(&counter->events_list); in counter_disable_events()
225 if (counter->ops->events_configure) in counter_disable_events()
226 err = counter->ops->events_configure(counter); in counter_disable_events()
228 spin_unlock_irqrestore(&counter->events_list_lock, flags); in counter_disable_events()
230 mutex_lock(&counter->n_events_list_lock); in counter_disable_events()
232 counter_events_list_free(&counter->next_events_list); in counter_disable_events()
234 mutex_unlock(&counter->n_events_list_lock); in counter_disable_events()
245 *id = 0; in counter_get_ext()
246 for (*ext_idx = 0; *ext_idx < num_ext; (*ext_idx)++) { in counter_get_ext()
248 return 0; in counter_get_ext()
250 if (ext->type == COUNTER_COMP_ARRAY) { in counter_get_ext()
251 element = ext->priv; in counter_get_ext()
253 if (component_id - *id < element->length) in counter_get_ext()
254 return 0; in counter_get_ext()
256 *id += element->length; in counter_get_ext()
261 return -EINVAL; in counter_get_ext()
264 static int counter_add_watch(struct counter_device *const counter, in counter_add_watch() argument
274 int err = 0; in counter_add_watch()
277 return -EFAULT; in counter_add_watch()
287 ext = counter->ext; in counter_add_watch()
288 num_ext = counter->num_ext; in counter_add_watch()
291 if (parent >= counter->num_signals) in counter_add_watch()
292 return -EINVAL; in counter_add_watch()
293 parent = array_index_nospec(parent, counter->num_signals); in counter_add_watch()
295 comp_node.parent = counter->signals + parent; in counter_add_watch()
297 ext = counter->signals[parent].ext; in counter_add_watch()
298 num_ext = counter->signals[parent].num_ext; in counter_add_watch()
301 if (parent >= counter->num_counts) in counter_add_watch()
302 return -EINVAL; in counter_add_watch()
303 parent = array_index_nospec(parent, counter->num_counts); in counter_add_watch()
305 comp_node.parent = counter->counts + parent; in counter_add_watch()
307 ext = counter->counts[parent].ext; in counter_add_watch()
308 num_ext = counter->counts[parent].num_ext; in counter_add_watch()
311 return -EINVAL; in counter_add_watch()
320 return -EINVAL; in counter_add_watch()
323 comp_node.comp.signal_u32_read = counter->ops->signal_read; in counter_add_watch()
327 return -EINVAL; in counter_add_watch()
330 comp_node.comp.count_u64_read = counter->ops->count_read; in counter_add_watch()
334 return -EINVAL; in counter_add_watch()
337 comp_node.comp.count_u32_read = counter->ops->function_read; in counter_add_watch()
341 return -EINVAL; in counter_add_watch()
342 if (id >= counter->counts[parent].num_synapses) in counter_add_watch()
343 return -EINVAL; in counter_add_watch()
344 id = array_index_nospec(id, counter->counts[parent].num_synapses); in counter_add_watch()
347 comp_node.comp.action_read = counter->ops->action_read; in counter_add_watch()
348 comp_node.comp.priv = counter->counts[parent].synapses + id; in counter_add_watch()
352 if (err < 0) in counter_add_watch()
358 return -EINVAL; in counter_add_watch()
361 return -EOPNOTSUPP; in counter_add_watch()
364 mutex_lock(&counter->n_events_list_lock); in counter_add_watch()
366 if (counter->ops->watch_validate) { in counter_add_watch()
367 err = counter->ops->watch_validate(counter, &watch); in counter_add_watch()
368 if (err < 0) in counter_add_watch()
374 err = counter_set_event_node(counter, &watch, &comp_node); in counter_add_watch()
377 mutex_unlock(&counter->n_events_list_lock); in counter_add_watch()
385 struct counter_device *const counter = filp->private_data; in counter_chrdev_ioctl() local
386 int ret = -ENODEV; in counter_chrdev_ioctl()
388 mutex_lock(&counter->ops_exist_lock); in counter_chrdev_ioctl()
390 if (!counter->ops) in counter_chrdev_ioctl()
395 ret = counter_add_watch(counter, arg); in counter_chrdev_ioctl()
398 ret = counter_enable_events(counter); in counter_chrdev_ioctl()
401 ret = counter_disable_events(counter); in counter_chrdev_ioctl()
404 ret = -ENOIOCTLCMD; in counter_chrdev_ioctl()
409 mutex_unlock(&counter->ops_exist_lock); in counter_chrdev_ioctl()
416 struct counter_device *const counter = container_of(inode->i_cdev, in counter_chrdev_open() local
417 typeof(*counter), in counter_chrdev_open()
420 get_device(&counter->dev); in counter_chrdev_open()
421 filp->private_data = counter; in counter_chrdev_open()
428 struct counter_device *const counter = filp->private_data; in counter_chrdev_release() local
429 int ret = 0; in counter_chrdev_release()
431 mutex_lock(&counter->ops_exist_lock); in counter_chrdev_release()
433 if (!counter->ops) { in counter_chrdev_release()
435 counter_events_list_free(&counter->events_list); in counter_chrdev_release()
436 counter_events_list_free(&counter->next_events_list); in counter_chrdev_release()
437 ret = -ENODEV; in counter_chrdev_release()
441 ret = counter_disable_events(counter); in counter_chrdev_release()
442 if (ret < 0) { in counter_chrdev_release()
443 mutex_unlock(&counter->ops_exist_lock); in counter_chrdev_release()
448 mutex_unlock(&counter->ops_exist_lock); in counter_chrdev_release()
450 put_device(&counter->dev); in counter_chrdev_release()
465 int counter_chrdev_add(struct counter_device *const counter) in counter_chrdev_add() argument
467 /* Initialize Counter events lists */ in counter_chrdev_add()
468 INIT_LIST_HEAD(&counter->events_list); in counter_chrdev_add()
469 INIT_LIST_HEAD(&counter->next_events_list); in counter_chrdev_add()
470 spin_lock_init(&counter->events_list_lock); in counter_chrdev_add()
471 mutex_init(&counter->n_events_list_lock); in counter_chrdev_add()
472 init_waitqueue_head(&counter->events_wait); in counter_chrdev_add()
473 spin_lock_init(&counter->events_in_lock); in counter_chrdev_add()
474 mutex_init(&counter->events_out_lock); in counter_chrdev_add()
477 cdev_init(&counter->chrdev, &counter_fops); in counter_chrdev_add()
479 /* Allocate Counter events queue */ in counter_chrdev_add()
480 return kfifo_alloc(&counter->events, 64, GFP_KERNEL); in counter_chrdev_add()
483 void counter_chrdev_remove(struct counter_device *const counter) in counter_chrdev_remove() argument
485 kfifo_free(&counter->events); in counter_chrdev_remove()
488 static int counter_get_array_data(struct counter_device *const counter, in counter_get_array_data() argument
494 const struct counter_array *const element = comp->priv; in counter_get_array_data()
495 u32 value_u32 = 0; in counter_get_array_data()
498 switch (element->type) { in counter_get_array_data()
501 return -EINVAL; in counter_get_array_data()
502 ret = comp->signal_array_u32_read(counter, parent, idx, in counter_get_array_data()
509 return comp->device_array_u64_read(counter, idx, value); in counter_get_array_data()
511 return comp->signal_array_u64_read(counter, parent, idx, in counter_get_array_data()
514 return comp->count_array_u64_read(counter, parent, idx, in counter_get_array_data()
517 return -EINVAL; in counter_get_array_data()
520 return -EINVAL; in counter_get_array_data()
524 static int counter_get_data(struct counter_device *const counter, in counter_get_data() argument
528 const struct counter_comp *const comp = &comp_node->comp; in counter_get_data()
529 const enum counter_scope scope = comp_node->component.scope; in counter_get_data()
530 const size_t id = comp_node->component.id; in counter_get_data()
531 struct counter_signal *const signal = comp_node->parent; in counter_get_data()
532 struct counter_count *const count = comp_node->parent; in counter_get_data()
533 u8 value_u8 = 0; in counter_get_data()
534 u32 value_u32 = 0; in counter_get_data()
540 if (comp_node->component.type == COUNTER_COMPONENT_NONE) in counter_get_data()
541 return 0; in counter_get_data()
543 switch (comp->type) { in counter_get_data()
548 ret = comp->device_u8_read(counter, &value_u8); in counter_get_data()
551 ret = comp->signal_u8_read(counter, signal, &value_u8); in counter_get_data()
554 ret = comp->count_u8_read(counter, count, &value_u8); in counter_get_data()
557 return -EINVAL; in counter_get_data()
569 ret = comp->device_u32_read(counter, &value_u32); in counter_get_data()
572 ret = comp->signal_u32_read(counter, signal, in counter_get_data()
576 ret = comp->count_u32_read(counter, count, &value_u32); in counter_get_data()
579 return -EINVAL; in counter_get_data()
586 return comp->device_u64_read(counter, value); in counter_get_data()
588 return comp->signal_u64_read(counter, signal, value); in counter_get_data()
590 return comp->count_u64_read(counter, count, value); in counter_get_data()
592 return -EINVAL; in counter_get_data()
595 ret = comp->action_read(counter, count, comp->priv, &value_u32); in counter_get_data()
601 ext = counter->ext; in counter_get_data()
602 num_ext = counter->num_ext; in counter_get_data()
605 ext = signal->ext; in counter_get_data()
606 num_ext = signal->num_ext; in counter_get_data()
609 ext = count->ext; in counter_get_data()
610 num_ext = count->num_ext; in counter_get_data()
613 return -EINVAL; in counter_get_data()
616 if (ret < 0) in counter_get_data()
619 return counter_get_array_data(counter, scope, comp_node->parent, in counter_get_data()
620 comp, id - ext_id, value); in counter_get_data()
622 return -EINVAL; in counter_get_data()
627 * counter_push_event - queue event for userspace reading
628 * @counter: pointer to Counter structure
635 void counter_push_event(struct counter_device *const counter, const u8 event, in counter_push_event() argument
639 unsigned int copied = 0; in counter_push_event()
649 spin_lock_irqsave(&counter->events_list_lock, flags); in counter_push_event()
652 list_for_each_entry(event_node, &counter->events_list, l) in counter_push_event()
653 if (event_node->event == event && in counter_push_event()
654 event_node->channel == channel) in counter_push_event()
658 if (&event_node->l == &counter->events_list) in counter_push_event()
662 list_for_each_entry(comp_node, &event_node->comp_list, l) { in counter_push_event()
663 ev.watch.component = comp_node->component; in counter_push_event()
664 ev.status = -counter_get_data(counter, comp_node, &ev.value); in counter_push_event()
666 copied += kfifo_in_spinlocked_noirqsave(&counter->events, &ev, in counter_push_event()
667 1, &counter->events_in_lock); in counter_push_event()
671 spin_unlock_irqrestore(&counter->events_list_lock, flags); in counter_push_event()
674 wake_up_poll(&counter->events_wait, EPOLLIN); in counter_push_event()
676 EXPORT_SYMBOL_NS_GPL(counter_push_event, COUNTER);