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

source code of linux/drivers/iio/trigger/iio-trig-sysfs.c