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 irq_work_sync(&t->work); 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 209 static 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 } 215 module_init(iio_sysfs_trig_init); 216 217 static void __exit iio_sysfs_trig_exit(void) 218 { 219 device_unregister(&iio_sysfs_trig_dev); 220 } 221 module_exit(iio_sysfs_trig_exit); 222 223 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); 224 MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem"); 225 MODULE_LICENSE("GPL v2"); 226 MODULE_ALIAS("platform:iio-trig-sysfs"); 227