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(&iio_sysfs_trig_dev, "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 	iio_trigger_set_drvdata(t->trig, t);
161 
162 	t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work);
163 
164 	ret = iio_trigger_register(t->trig);
165 	if (ret)
166 		goto out2;
167 	list_add(&t->l, &iio_sysfs_trig_list);
168 	__module_get(THIS_MODULE);
169 	mutex_unlock(&iio_sysfs_trig_list_mut);
170 	return 0;
171 
172 out2:
173 	iio_trigger_free(t->trig);
174 free_t:
175 	kfree(t);
176 out1:
177 	mutex_unlock(&iio_sysfs_trig_list_mut);
178 	return ret;
179 }
180 
181 static int iio_sysfs_trigger_remove(int id)
182 {
183 	bool foundit = false;
184 	struct iio_sysfs_trig *t;
185 
186 	mutex_lock(&iio_sysfs_trig_list_mut);
187 	list_for_each_entry(t, &iio_sysfs_trig_list, l)
188 		if (id == t->id) {
189 			foundit = true;
190 			break;
191 		}
192 	if (!foundit) {
193 		mutex_unlock(&iio_sysfs_trig_list_mut);
194 		return -EINVAL;
195 	}
196 
197 	iio_trigger_unregister(t->trig);
198 	iio_trigger_free(t->trig);
199 
200 	list_del(&t->l);
201 	kfree(t);
202 	module_put(THIS_MODULE);
203 	mutex_unlock(&iio_sysfs_trig_list_mut);
204 	return 0;
205 }
206 
207 
208 static int __init iio_sysfs_trig_init(void)
209 {
210 	device_initialize(&iio_sysfs_trig_dev);
211 	dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
212 	return device_add(&iio_sysfs_trig_dev);
213 }
214 module_init(iio_sysfs_trig_init);
215 
216 static void __exit iio_sysfs_trig_exit(void)
217 {
218 	device_unregister(&iio_sysfs_trig_dev);
219 }
220 module_exit(iio_sysfs_trig_exit);
221 
222 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
223 MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
224 MODULE_LICENSE("GPL v2");
225 MODULE_ALIAS("platform:iio-trig-sysfs");
226