static int counter_add_watch()

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;
}