in counter-chrdev.c [231:346]
static int counter_add_watch(struct counter_device *const counter,
const unsigned long arg)
{
void __user *const uwatch = (void __user *)arg;
struct counter_watch watch;
struct counter_comp_node comp_node = {};
size_t parent, id;
struct counter_comp *ext;
size_t num_ext;
int err = 0;
if (copy_from_user(&watch, uwatch, sizeof(watch)))
return -EFAULT;
if (watch.component.type == COUNTER_COMPONENT_NONE)
goto no_component;
parent = watch.component.parent;
/* Configure parent component info for comp node */
switch (watch.component.scope) {
case COUNTER_SCOPE_DEVICE:
ext = counter->ext;
num_ext = counter->num_ext;
break;
case COUNTER_SCOPE_SIGNAL:
if (parent >= counter->num_signals)
return -EINVAL;
parent = array_index_nospec(parent, counter->num_signals);
comp_node.parent = counter->signals + parent;
ext = counter->signals[parent].ext;
num_ext = counter->signals[parent].num_ext;
break;
case COUNTER_SCOPE_COUNT:
if (parent >= counter->num_counts)
return -EINVAL;
parent = array_index_nospec(parent, counter->num_counts);
comp_node.parent = counter->counts + parent;
ext = counter->counts[parent].ext;
num_ext = counter->counts[parent].num_ext;
break;
default:
return -EINVAL;
}
id = watch.component.id;
/* Configure component info for comp node */
switch (watch.component.type) {
case COUNTER_COMPONENT_SIGNAL:
if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
return -EINVAL;
comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
comp_node.comp.signal_u32_read = counter->ops->signal_read;
break;
case COUNTER_COMPONENT_COUNT:
if (watch.component.scope != COUNTER_SCOPE_COUNT)
return -EINVAL;
comp_node.comp.type = COUNTER_COMP_U64;
comp_node.comp.count_u64_read = counter->ops->count_read;
break;
case COUNTER_COMPONENT_FUNCTION:
if (watch.component.scope != COUNTER_SCOPE_COUNT)
return -EINVAL;
comp_node.comp.type = COUNTER_COMP_FUNCTION;
comp_node.comp.count_u32_read = counter->ops->function_read;
break;
case COUNTER_COMPONENT_SYNAPSE_ACTION:
if (watch.component.scope != COUNTER_SCOPE_COUNT)
return -EINVAL;
if (id >= counter->counts[parent].num_synapses)
return -EINVAL;
id = array_index_nospec(id, counter->counts[parent].num_synapses);
comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
comp_node.comp.action_read = counter->ops->action_read;
comp_node.comp.priv = counter->counts[parent].synapses + id;
break;
case COUNTER_COMPONENT_EXTENSION:
if (id >= num_ext)
return -EINVAL;
id = array_index_nospec(id, num_ext);
comp_node.comp = ext[id];
break;
default:
return -EINVAL;
}
if (!counter_comp_read_is_set(comp_node.comp))
return -EOPNOTSUPP;
no_component:
mutex_lock(&counter->n_events_list_lock);
if (counter->ops->watch_validate) {
err = counter->ops->watch_validate(counter, &watch);
if (err < 0)
goto err_exit;
}
comp_node.component = watch.component;
err = counter_set_event_node(counter, &watch, &comp_node);
err_exit:
mutex_unlock(&counter->n_events_list_lock);
return err;
}