Linux-libre 5.4.48-gnu
[librecmc/linux-libre.git] / drivers / iio / trigger / iio-trig-sysfs.c
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
16 struct iio_sysfs_trig {
17         struct iio_trigger *trig;
18         struct irq_work work;
19         int id;
20         struct list_head l;
21 };
22
23 static LIST_HEAD(iio_sysfs_trig_list);
24 static DEFINE_MUTEX(iio_sysfs_trig_list_mut);
25
26 static int iio_sysfs_trigger_probe(int id);
27 static 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(buf, 10, &input);
36         if (ret)
37                 return ret;
38         ret = iio_sysfs_trigger_probe(input);
39         if (ret)
40                 return ret;
41         return len;
42 }
43 static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add);
44
45 static int iio_sysfs_trigger_remove(int id);
46 static 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(buf, 10, &input);
55         if (ret)
56                 return ret;
57         ret = iio_sysfs_trigger_remove(input);
58         if (ret)
59                 return ret;
60         return len;
61 }
62
63 static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove);
64
65 static struct attribute *iio_sysfs_trig_attrs[] = {
66         &dev_attr_add_trigger.attr,
67         &dev_attr_remove_trigger.attr,
68         NULL,
69 };
70
71 static const struct attribute_group iio_sysfs_trig_group = {
72         .attrs = iio_sysfs_trig_attrs,
73 };
74
75 static 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 */
82 static void iio_trigger_sysfs_release(struct device *dev)
83 {
84 }
85
86 static 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
92 static 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);
98 }
99
100 static 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(dev);
104         struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig);
105
106         irq_work_queue(&sysfs_trig->work);
107
108         return count;
109 }
110
111 static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll);
112
113 static struct attribute *iio_sysfs_trigger_attrs[] = {
114         &dev_attr_trigger_now.attr,
115         NULL,
116 };
117
118 static const struct attribute_group iio_sysfs_trigger_attr_group = {
119         .attrs = iio_sysfs_trigger_attrs,
120 };
121
122 static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = {
123         &iio_sysfs_trigger_attr_group,
124         NULL
125 };
126
127 static const struct iio_trigger_ops iio_sysfs_trigger_ops = {
128 };
129
130 static int iio_sysfs_trigger_probe(int id)
131 {
132         struct iio_sysfs_trig *t;
133         int ret;
134         bool foundit = false;
135
136         mutex_lock(&iio_sysfs_trig_list_mut);
137         list_for_each_entry(t, &iio_sysfs_trig_list, l)
138                 if (id == t->id) {
139                         foundit = true;
140                         break;
141                 }
142         if (foundit) {
143                 ret = -EINVAL;
144                 goto out1;
145         }
146         t = kmalloc(sizeof(*t), GFP_KERNEL);
147         if (t == NULL) {
148                 ret = -ENOMEM;
149                 goto out1;
150         }
151         t->id = id;
152         t->trig = iio_trigger_alloc("sysfstrig%d", id);
153         if (!t->trig) {
154                 ret = -ENOMEM;
155                 goto free_t;
156         }
157
158         t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
159         t->trig->ops = &iio_sysfs_trigger_ops;
160         t->trig->dev.parent = &iio_sysfs_trig_dev;
161         iio_trigger_set_drvdata(t->trig, t);
162
163         init_irq_work(&t->work, iio_sysfs_trigger_work);
164
165         ret = iio_trigger_register(t->trig);
166         if (ret)
167                 goto out2;
168         list_add(&t->l, &iio_sysfs_trig_list);
169         __module_get(THIS_MODULE);
170         mutex_unlock(&iio_sysfs_trig_list_mut);
171         return 0;
172
173 out2:
174         iio_trigger_free(t->trig);
175 free_t:
176         kfree(t);
177 out1:
178         mutex_unlock(&iio_sysfs_trig_list_mut);
179         return ret;
180 }
181
182 static int iio_sysfs_trigger_remove(int id)
183 {
184         bool foundit = false;
185         struct iio_sysfs_trig *t;
186
187         mutex_lock(&iio_sysfs_trig_list_mut);
188         list_for_each_entry(t, &iio_sysfs_trig_list, l)
189                 if (id == t->id) {
190                         foundit = true;
191                         break;
192                 }
193         if (!foundit) {
194                 mutex_unlock(&iio_sysfs_trig_list_mut);
195                 return -EINVAL;
196         }
197
198         iio_trigger_unregister(t->trig);
199         iio_trigger_free(t->trig);
200
201         list_del(&t->l);
202         kfree(t);
203         module_put(THIS_MODULE);
204         mutex_unlock(&iio_sysfs_trig_list_mut);
205         return 0;
206 }
207
208
209 static int __init iio_sysfs_trig_init(void)
210 {
211         device_initialize(&iio_sysfs_trig_dev);
212         dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
213         return device_add(&iio_sysfs_trig_dev);
214 }
215 module_init(iio_sysfs_trig_init);
216
217 static void __exit iio_sysfs_trig_exit(void)
218 {
219         device_unregister(&iio_sysfs_trig_dev);
220 }
221 module_exit(iio_sysfs_trig_exit);
222
223 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
224 MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
225 MODULE_LICENSE("GPL v2");
226 MODULE_ALIAS("platform:iio-trig-sysfs");