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