Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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(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}
 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(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
 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);
 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(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
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(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(t->trig, t);
157
158	t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work);
159
160	ret = iio_trigger_register(t->trig);
161	if (ret)
162		goto err_free_trig;
163	list_add(&t->l, &iio_sysfs_trig_list);
164	__module_get(THIS_MODULE);
165	mutex_unlock(&iio_sysfs_trig_list_mut);
166	return 0;
167
168err_free_trig:
169	iio_trigger_free(t->trig);
170err_free_sys_trig:
171	kfree(t);
172err_unlock:
173	mutex_unlock(&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(&iio_sysfs_trig_list_mut);
189		return -EINVAL;
190	}
191
192	iio_trigger_unregister(t->trig);
193	irq_work_sync(&t->work);
194	iio_trigger_free(t->trig);
195
196	list_del(&t->l);
197	kfree(t);
198	module_put(THIS_MODULE);
199	mutex_unlock(&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(&iio_sysfs_trig_dev);
208	dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
209	ret = device_add(&iio_sysfs_trig_dev);
210	if (ret)
211		put_device(&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(&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");