226 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 NULL78};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 NULL125};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");226