1 /*
2  * Copyright 2011 Analog Devices Inc.
3  *
4  * Licensed under the GPL-2.
5  *
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/slab.h>
12 #include <linux/list.h>
13 
14 #include "../iio.h"
15 #include "../trigger.h"
16 
17 struct iio_sysfs_trig {
18 	struct iio_trigger *trig;
19 	int id;
20 	struct list_head l;
21 };
22 
23 static LIST_HEAD(iio_sysfs_trig_list);
24 static DEFINE_MUTEX(iio_syfs_trig_list_mut);
25 
26 static int iio_sysfs_trigger_probe(int id);
iio_sysfs_trig_add(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)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 = strict_strtoul(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);
iio_sysfs_trig_remove(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)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 = strict_strtoul(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 */
iio_trigger_sysfs_release(struct device * dev)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 
iio_sysfs_trigger_poll(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)92 static ssize_t iio_sysfs_trigger_poll(struct device *dev,
93 		struct device_attribute *attr, const char *buf, size_t count)
94 {
95 	struct iio_trigger *trig = dev_get_drvdata(dev);
96 	iio_trigger_poll_chained(trig, 0);
97 
98 	return count;
99 }
100 
101 static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll);
102 
103 static struct attribute *iio_sysfs_trigger_attrs[] = {
104 	&dev_attr_trigger_now.attr,
105 	NULL,
106 };
107 
108 static const struct attribute_group iio_sysfs_trigger_attr_group = {
109 	.attrs = iio_sysfs_trigger_attrs,
110 };
111 
112 static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = {
113 	&iio_sysfs_trigger_attr_group,
114 	NULL
115 };
116 
117 static const struct iio_trigger_ops iio_sysfs_trigger_ops = {
118 	.owner = THIS_MODULE,
119 };
120 
iio_sysfs_trigger_probe(int id)121 static int iio_sysfs_trigger_probe(int id)
122 {
123 	struct iio_sysfs_trig *t;
124 	int ret;
125 	bool foundit = false;
126 	mutex_lock(&iio_syfs_trig_list_mut);
127 	list_for_each_entry(t, &iio_sysfs_trig_list, l)
128 		if (id == t->id) {
129 			foundit = true;
130 			break;
131 		}
132 	if (foundit) {
133 		ret = -EINVAL;
134 		goto out1;
135 	}
136 	t = kmalloc(sizeof(*t), GFP_KERNEL);
137 	if (t == NULL) {
138 		ret = -ENOMEM;
139 		goto out1;
140 	}
141 	t->id = id;
142 	t->trig = iio_allocate_trigger("sysfstrig%d", id);
143 	if (!t->trig) {
144 		ret = -ENOMEM;
145 		goto free_t;
146 	}
147 
148 	t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
149 	t->trig->ops = &iio_sysfs_trigger_ops;
150 	t->trig->dev.parent = &iio_sysfs_trig_dev;
151 
152 	ret = iio_trigger_register(t->trig);
153 	if (ret)
154 		goto out2;
155 	list_add(&t->l, &iio_sysfs_trig_list);
156 	__module_get(THIS_MODULE);
157 	mutex_unlock(&iio_syfs_trig_list_mut);
158 	return 0;
159 
160 out2:
161 	iio_put_trigger(t->trig);
162 free_t:
163 	kfree(t);
164 out1:
165 	mutex_unlock(&iio_syfs_trig_list_mut);
166 	return ret;
167 }
168 
iio_sysfs_trigger_remove(int id)169 static int iio_sysfs_trigger_remove(int id)
170 {
171 	bool foundit = false;
172 	struct iio_sysfs_trig *t;
173 	mutex_lock(&iio_syfs_trig_list_mut);
174 	list_for_each_entry(t, &iio_sysfs_trig_list, l)
175 		if (id == t->id) {
176 			foundit = true;
177 			break;
178 		}
179 	if (!foundit) {
180 		mutex_unlock(&iio_syfs_trig_list_mut);
181 		return -EINVAL;
182 	}
183 
184 	iio_trigger_unregister(t->trig);
185 	iio_free_trigger(t->trig);
186 
187 	list_del(&t->l);
188 	kfree(t);
189 	module_put(THIS_MODULE);
190 	mutex_unlock(&iio_syfs_trig_list_mut);
191 	return 0;
192 }
193 
194 
iio_sysfs_trig_init(void)195 static int __init iio_sysfs_trig_init(void)
196 {
197 	device_initialize(&iio_sysfs_trig_dev);
198 	dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
199 	return device_add(&iio_sysfs_trig_dev);
200 }
201 module_init(iio_sysfs_trig_init);
202 
iio_sysfs_trig_exit(void)203 static void __exit iio_sysfs_trig_exit(void)
204 {
205 	device_unregister(&iio_sysfs_trig_dev);
206 }
207 module_exit(iio_sysfs_trig_exit);
208 
209 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
210 MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
211 MODULE_LICENSE("GPL v2");
212 MODULE_ALIAS("platform:iio-trig-sysfs");
213