xref: /linux/drivers/iio/trigger/iio-trig-sysfs.c (revision 995071d36bb9804b644265450142fcb91c427ee8)
1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e64e7d5cSJonathan Cameron /*
3e64e7d5cSJonathan Cameron  * Copyright 2011 Analog Devices Inc.
4e64e7d5cSJonathan Cameron  */
5e64e7d5cSJonathan Cameron 
6e64e7d5cSJonathan Cameron #include <linux/kernel.h>
7e64e7d5cSJonathan Cameron #include <linux/module.h>
8e64e7d5cSJonathan Cameron #include <linux/platform_device.h>
9e64e7d5cSJonathan Cameron #include <linux/slab.h>
10e64e7d5cSJonathan Cameron #include <linux/list.h>
11e64e7d5cSJonathan Cameron #include <linux/irq_work.h>
12e64e7d5cSJonathan Cameron 
13e64e7d5cSJonathan Cameron #include <linux/iio/iio.h>
14e64e7d5cSJonathan Cameron #include <linux/iio/trigger.h>
15e64e7d5cSJonathan Cameron 
16e64e7d5cSJonathan Cameron struct iio_sysfs_trig {
17e64e7d5cSJonathan Cameron 	struct iio_trigger *trig;
18e64e7d5cSJonathan Cameron 	struct irq_work work;
19e64e7d5cSJonathan Cameron 	int id;
20e64e7d5cSJonathan Cameron 	struct list_head l;
21e64e7d5cSJonathan Cameron };
22e64e7d5cSJonathan Cameron 
23e64e7d5cSJonathan Cameron static LIST_HEAD(iio_sysfs_trig_list);
2410a485c5SDenis CIOCCA static DEFINE_MUTEX(iio_sysfs_trig_list_mut);
25e64e7d5cSJonathan Cameron 
26e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_probe(int id);
27e64e7d5cSJonathan Cameron static ssize_t iio_sysfs_trig_add(struct device *dev,
28e64e7d5cSJonathan Cameron 				  struct device_attribute *attr,
29e64e7d5cSJonathan Cameron 				  const char *buf,
30e64e7d5cSJonathan Cameron 				  size_t len)
31e64e7d5cSJonathan Cameron {
32e64e7d5cSJonathan Cameron 	int ret;
33e64e7d5cSJonathan Cameron 	unsigned long input;
34e64e7d5cSJonathan Cameron 
35ddeb64f3SJingoo Han 	ret = kstrtoul(buf, 10, &input);
36e64e7d5cSJonathan Cameron 	if (ret)
37e64e7d5cSJonathan Cameron 		return ret;
38e64e7d5cSJonathan Cameron 	ret = iio_sysfs_trigger_probe(input);
39e64e7d5cSJonathan Cameron 	if (ret)
40e64e7d5cSJonathan Cameron 		return ret;
41e64e7d5cSJonathan Cameron 	return len;
42e64e7d5cSJonathan Cameron }
43e64e7d5cSJonathan Cameron static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add);
44e64e7d5cSJonathan Cameron 
45e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_remove(int id);
46e64e7d5cSJonathan Cameron static ssize_t iio_sysfs_trig_remove(struct device *dev,
47e64e7d5cSJonathan Cameron 				     struct device_attribute *attr,
48e64e7d5cSJonathan Cameron 				     const char *buf,
49e64e7d5cSJonathan Cameron 				     size_t len)
50e64e7d5cSJonathan Cameron {
51e64e7d5cSJonathan Cameron 	int ret;
52e64e7d5cSJonathan Cameron 	unsigned long input;
53e64e7d5cSJonathan Cameron 
54ddeb64f3SJingoo Han 	ret = kstrtoul(buf, 10, &input);
55e64e7d5cSJonathan Cameron 	if (ret)
56e64e7d5cSJonathan Cameron 		return ret;
57e64e7d5cSJonathan Cameron 	ret = iio_sysfs_trigger_remove(input);
58e64e7d5cSJonathan Cameron 	if (ret)
59e64e7d5cSJonathan Cameron 		return ret;
60e64e7d5cSJonathan Cameron 	return len;
61e64e7d5cSJonathan Cameron }
62e64e7d5cSJonathan Cameron 
63e64e7d5cSJonathan Cameron static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove);
64e64e7d5cSJonathan Cameron 
65e64e7d5cSJonathan Cameron static struct attribute *iio_sysfs_trig_attrs[] = {
66e64e7d5cSJonathan Cameron 	&dev_attr_add_trigger.attr,
67e64e7d5cSJonathan Cameron 	&dev_attr_remove_trigger.attr,
68e64e7d5cSJonathan Cameron 	NULL,
69e64e7d5cSJonathan Cameron };
70e64e7d5cSJonathan Cameron 
71e64e7d5cSJonathan Cameron static const struct attribute_group iio_sysfs_trig_group = {
72e64e7d5cSJonathan Cameron 	.attrs = iio_sysfs_trig_attrs,
73e64e7d5cSJonathan Cameron };
74e64e7d5cSJonathan Cameron 
75e64e7d5cSJonathan Cameron static const struct attribute_group *iio_sysfs_trig_groups[] = {
76e64e7d5cSJonathan Cameron 	&iio_sysfs_trig_group,
77e64e7d5cSJonathan Cameron 	NULL
78e64e7d5cSJonathan Cameron };
79e64e7d5cSJonathan Cameron 
80e64e7d5cSJonathan Cameron 
81e64e7d5cSJonathan Cameron /* Nothing to actually do upon release */
82e64e7d5cSJonathan Cameron static void iio_trigger_sysfs_release(struct device *dev)
83e64e7d5cSJonathan Cameron {
84e64e7d5cSJonathan Cameron }
85e64e7d5cSJonathan Cameron 
86e64e7d5cSJonathan Cameron static struct device iio_sysfs_trig_dev = {
87e64e7d5cSJonathan Cameron 	.bus = &iio_bus_type,
88e64e7d5cSJonathan Cameron 	.groups = iio_sysfs_trig_groups,
89e64e7d5cSJonathan Cameron 	.release = &iio_trigger_sysfs_release,
90e64e7d5cSJonathan Cameron };
91e64e7d5cSJonathan Cameron 
92e64e7d5cSJonathan Cameron static void iio_sysfs_trigger_work(struct irq_work *work)
93e64e7d5cSJonathan Cameron {
94e64e7d5cSJonathan Cameron 	struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig,
95e64e7d5cSJonathan Cameron 							work);
96e64e7d5cSJonathan Cameron 
97398fd22bSPeter Meerwald 	iio_trigger_poll(trig->trig);
98e64e7d5cSJonathan Cameron }
99e64e7d5cSJonathan Cameron 
100e64e7d5cSJonathan Cameron static ssize_t iio_sysfs_trigger_poll(struct device *dev,
101e64e7d5cSJonathan Cameron 		struct device_attribute *attr, const char *buf, size_t count)
102e64e7d5cSJonathan Cameron {
103e64e7d5cSJonathan Cameron 	struct iio_trigger *trig = to_iio_trigger(dev);
104e64e7d5cSJonathan Cameron 	struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig);
105e64e7d5cSJonathan Cameron 
106e64e7d5cSJonathan Cameron 	irq_work_queue(&sysfs_trig->work);
107e64e7d5cSJonathan Cameron 
108e64e7d5cSJonathan Cameron 	return count;
109e64e7d5cSJonathan Cameron }
110e64e7d5cSJonathan Cameron 
111e64e7d5cSJonathan Cameron static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll);
112e64e7d5cSJonathan Cameron 
113e64e7d5cSJonathan Cameron static struct attribute *iio_sysfs_trigger_attrs[] = {
114e64e7d5cSJonathan Cameron 	&dev_attr_trigger_now.attr,
115e64e7d5cSJonathan Cameron 	NULL,
116e64e7d5cSJonathan Cameron };
117e64e7d5cSJonathan Cameron 
118e64e7d5cSJonathan Cameron static const struct attribute_group iio_sysfs_trigger_attr_group = {
119e64e7d5cSJonathan Cameron 	.attrs = iio_sysfs_trigger_attrs,
120e64e7d5cSJonathan Cameron };
121e64e7d5cSJonathan Cameron 
122e64e7d5cSJonathan Cameron static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = {
123e64e7d5cSJonathan Cameron 	&iio_sysfs_trigger_attr_group,
124e64e7d5cSJonathan Cameron 	NULL
125e64e7d5cSJonathan Cameron };
126e64e7d5cSJonathan Cameron 
127e64e7d5cSJonathan Cameron static const struct iio_trigger_ops iio_sysfs_trigger_ops = {
128e64e7d5cSJonathan Cameron };
129e64e7d5cSJonathan Cameron 
130e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_probe(int id)
131e64e7d5cSJonathan Cameron {
132e64e7d5cSJonathan Cameron 	struct iio_sysfs_trig *t;
133e64e7d5cSJonathan Cameron 	int ret;
134e64e7d5cSJonathan Cameron 	bool foundit = false;
135450a5ff7SRoberta Dobrescu 
13610a485c5SDenis CIOCCA 	mutex_lock(&iio_sysfs_trig_list_mut);
137e64e7d5cSJonathan Cameron 	list_for_each_entry(t, &iio_sysfs_trig_list, l)
138e64e7d5cSJonathan Cameron 		if (id == t->id) {
139e64e7d5cSJonathan Cameron 			foundit = true;
140e64e7d5cSJonathan Cameron 			break;
141e64e7d5cSJonathan Cameron 		}
142e64e7d5cSJonathan Cameron 	if (foundit) {
143e64e7d5cSJonathan Cameron 		ret = -EINVAL;
144e64e7d5cSJonathan Cameron 		goto out1;
145e64e7d5cSJonathan Cameron 	}
146e64e7d5cSJonathan Cameron 	t = kmalloc(sizeof(*t), GFP_KERNEL);
147e64e7d5cSJonathan Cameron 	if (t == NULL) {
148e64e7d5cSJonathan Cameron 		ret = -ENOMEM;
149e64e7d5cSJonathan Cameron 		goto out1;
150e64e7d5cSJonathan Cameron 	}
151e64e7d5cSJonathan Cameron 	t->id = id;
152*995071d3SGwendal Grignou 	t->trig = iio_trigger_alloc(&iio_sysfs_trig_dev, "sysfstrig%d", id);
153e64e7d5cSJonathan Cameron 	if (!t->trig) {
154e64e7d5cSJonathan Cameron 		ret = -ENOMEM;
155e64e7d5cSJonathan Cameron 		goto free_t;
156e64e7d5cSJonathan Cameron 	}
157e64e7d5cSJonathan Cameron 
158e64e7d5cSJonathan Cameron 	t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
159e64e7d5cSJonathan Cameron 	t->trig->ops = &iio_sysfs_trigger_ops;
160e64e7d5cSJonathan Cameron 	iio_trigger_set_drvdata(t->trig, t);
161e64e7d5cSJonathan Cameron 
1623db1a3faSLinus Torvalds 	t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work);
163e64e7d5cSJonathan Cameron 
164e64e7d5cSJonathan Cameron 	ret = iio_trigger_register(t->trig);
165e64e7d5cSJonathan Cameron 	if (ret)
166e64e7d5cSJonathan Cameron 		goto out2;
167e64e7d5cSJonathan Cameron 	list_add(&t->l, &iio_sysfs_trig_list);
168e64e7d5cSJonathan Cameron 	__module_get(THIS_MODULE);
16910a485c5SDenis CIOCCA 	mutex_unlock(&iio_sysfs_trig_list_mut);
170e64e7d5cSJonathan Cameron 	return 0;
171e64e7d5cSJonathan Cameron 
172e64e7d5cSJonathan Cameron out2:
17310e840dfSAlison Schofield 	iio_trigger_free(t->trig);
174e64e7d5cSJonathan Cameron free_t:
175e64e7d5cSJonathan Cameron 	kfree(t);
176e64e7d5cSJonathan Cameron out1:
17710a485c5SDenis CIOCCA 	mutex_unlock(&iio_sysfs_trig_list_mut);
178e64e7d5cSJonathan Cameron 	return ret;
179e64e7d5cSJonathan Cameron }
180e64e7d5cSJonathan Cameron 
181e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_remove(int id)
182e64e7d5cSJonathan Cameron {
183e64e7d5cSJonathan Cameron 	bool foundit = false;
184e64e7d5cSJonathan Cameron 	struct iio_sysfs_trig *t;
185450a5ff7SRoberta Dobrescu 
18610a485c5SDenis CIOCCA 	mutex_lock(&iio_sysfs_trig_list_mut);
187e64e7d5cSJonathan Cameron 	list_for_each_entry(t, &iio_sysfs_trig_list, l)
188e64e7d5cSJonathan Cameron 		if (id == t->id) {
189e64e7d5cSJonathan Cameron 			foundit = true;
190e64e7d5cSJonathan Cameron 			break;
191e64e7d5cSJonathan Cameron 		}
192e64e7d5cSJonathan Cameron 	if (!foundit) {
19310a485c5SDenis CIOCCA 		mutex_unlock(&iio_sysfs_trig_list_mut);
194e64e7d5cSJonathan Cameron 		return -EINVAL;
195e64e7d5cSJonathan Cameron 	}
196e64e7d5cSJonathan Cameron 
197e64e7d5cSJonathan Cameron 	iio_trigger_unregister(t->trig);
198e64e7d5cSJonathan Cameron 	iio_trigger_free(t->trig);
199e64e7d5cSJonathan Cameron 
200e64e7d5cSJonathan Cameron 	list_del(&t->l);
201e64e7d5cSJonathan Cameron 	kfree(t);
202e64e7d5cSJonathan Cameron 	module_put(THIS_MODULE);
20310a485c5SDenis CIOCCA 	mutex_unlock(&iio_sysfs_trig_list_mut);
204e64e7d5cSJonathan Cameron 	return 0;
205e64e7d5cSJonathan Cameron }
206e64e7d5cSJonathan Cameron 
207e64e7d5cSJonathan Cameron 
208e64e7d5cSJonathan Cameron static int __init iio_sysfs_trig_init(void)
209e64e7d5cSJonathan Cameron {
210e64e7d5cSJonathan Cameron 	device_initialize(&iio_sysfs_trig_dev);
211e64e7d5cSJonathan Cameron 	dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
212e64e7d5cSJonathan Cameron 	return device_add(&iio_sysfs_trig_dev);
213e64e7d5cSJonathan Cameron }
214e64e7d5cSJonathan Cameron module_init(iio_sysfs_trig_init);
215e64e7d5cSJonathan Cameron 
216e64e7d5cSJonathan Cameron static void __exit iio_sysfs_trig_exit(void)
217e64e7d5cSJonathan Cameron {
218e64e7d5cSJonathan Cameron 	device_unregister(&iio_sysfs_trig_dev);
219e64e7d5cSJonathan Cameron }
220e64e7d5cSJonathan Cameron module_exit(iio_sysfs_trig_exit);
221e64e7d5cSJonathan Cameron 
2229920ed25SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
223e64e7d5cSJonathan Cameron MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
224e64e7d5cSJonathan Cameron MODULE_LICENSE("GPL v2");
225e64e7d5cSJonathan Cameron MODULE_ALIAS("platform:iio-trig-sysfs");
226