linux/drivers/iio/trigger/iio-trig-sysfs.c
<<
>>
Prefs
   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 const struct iio_trigger_ops iio_sysfs_trigger_ops = {
 128};
 129
 130static 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
 173out2:
 174        iio_trigger_free(t->trig);
 175free_t:
 176        kfree(t);
 177out1:
 178        mutex_unlock(&iio_sysfs_trig_list_mut);
 179        return ret;
 180}
 181
 182static 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
 209static 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}
 215module_init(iio_sysfs_trig_init);
 216
 217static void __exit iio_sysfs_trig_exit(void)
 218{
 219        device_unregister(&iio_sysfs_trig_dev);
 220}
 221module_exit(iio_sysfs_trig_exit);
 222
 223MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
 224MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
 225MODULE_LICENSE("GPL v2");
 226MODULE_ALIAS("platform:iio-trig-sysfs");
 227