1e64e7d5cSJonathan Cameron /* 2e64e7d5cSJonathan Cameron * Copyright 2011 Analog Devices Inc. 3e64e7d5cSJonathan Cameron * 4e64e7d5cSJonathan Cameron * Licensed under the GPL-2. 5e64e7d5cSJonathan Cameron * 6e64e7d5cSJonathan Cameron */ 7e64e7d5cSJonathan Cameron 8e64e7d5cSJonathan Cameron #include <linux/kernel.h> 9e64e7d5cSJonathan Cameron #include <linux/module.h> 10e64e7d5cSJonathan Cameron #include <linux/platform_device.h> 11e64e7d5cSJonathan Cameron #include <linux/slab.h> 12e64e7d5cSJonathan Cameron #include <linux/list.h> 13e64e7d5cSJonathan Cameron #include <linux/irq_work.h> 14e64e7d5cSJonathan Cameron 15e64e7d5cSJonathan Cameron #include <linux/iio/iio.h> 16e64e7d5cSJonathan Cameron #include <linux/iio/trigger.h> 17e64e7d5cSJonathan Cameron 18e64e7d5cSJonathan Cameron struct iio_sysfs_trig { 19e64e7d5cSJonathan Cameron struct iio_trigger *trig; 20e64e7d5cSJonathan Cameron struct irq_work work; 21e64e7d5cSJonathan Cameron int id; 22e64e7d5cSJonathan Cameron struct list_head l; 23e64e7d5cSJonathan Cameron }; 24e64e7d5cSJonathan Cameron 25e64e7d5cSJonathan Cameron static LIST_HEAD(iio_sysfs_trig_list); 26*10a485c5SDenis CIOCCA static DEFINE_MUTEX(iio_sysfs_trig_list_mut); 27e64e7d5cSJonathan Cameron 28e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_probe(int id); 29e64e7d5cSJonathan Cameron static ssize_t iio_sysfs_trig_add(struct device *dev, 30e64e7d5cSJonathan Cameron struct device_attribute *attr, 31e64e7d5cSJonathan Cameron const char *buf, 32e64e7d5cSJonathan Cameron size_t len) 33e64e7d5cSJonathan Cameron { 34e64e7d5cSJonathan Cameron int ret; 35e64e7d5cSJonathan Cameron unsigned long input; 36e64e7d5cSJonathan Cameron 37ddeb64f3SJingoo Han ret = kstrtoul(buf, 10, &input); 38e64e7d5cSJonathan Cameron if (ret) 39e64e7d5cSJonathan Cameron return ret; 40e64e7d5cSJonathan Cameron ret = iio_sysfs_trigger_probe(input); 41e64e7d5cSJonathan Cameron if (ret) 42e64e7d5cSJonathan Cameron return ret; 43e64e7d5cSJonathan Cameron return len; 44e64e7d5cSJonathan Cameron } 45e64e7d5cSJonathan Cameron static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add); 46e64e7d5cSJonathan Cameron 47e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_remove(int id); 48e64e7d5cSJonathan Cameron static ssize_t iio_sysfs_trig_remove(struct device *dev, 49e64e7d5cSJonathan Cameron struct device_attribute *attr, 50e64e7d5cSJonathan Cameron const char *buf, 51e64e7d5cSJonathan Cameron size_t len) 52e64e7d5cSJonathan Cameron { 53e64e7d5cSJonathan Cameron int ret; 54e64e7d5cSJonathan Cameron unsigned long input; 55e64e7d5cSJonathan Cameron 56ddeb64f3SJingoo Han ret = kstrtoul(buf, 10, &input); 57e64e7d5cSJonathan Cameron if (ret) 58e64e7d5cSJonathan Cameron return ret; 59e64e7d5cSJonathan Cameron ret = iio_sysfs_trigger_remove(input); 60e64e7d5cSJonathan Cameron if (ret) 61e64e7d5cSJonathan Cameron return ret; 62e64e7d5cSJonathan Cameron return len; 63e64e7d5cSJonathan Cameron } 64e64e7d5cSJonathan Cameron 65e64e7d5cSJonathan Cameron static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove); 66e64e7d5cSJonathan Cameron 67e64e7d5cSJonathan Cameron static struct attribute *iio_sysfs_trig_attrs[] = { 68e64e7d5cSJonathan Cameron &dev_attr_add_trigger.attr, 69e64e7d5cSJonathan Cameron &dev_attr_remove_trigger.attr, 70e64e7d5cSJonathan Cameron NULL, 71e64e7d5cSJonathan Cameron }; 72e64e7d5cSJonathan Cameron 73e64e7d5cSJonathan Cameron static const struct attribute_group iio_sysfs_trig_group = { 74e64e7d5cSJonathan Cameron .attrs = iio_sysfs_trig_attrs, 75e64e7d5cSJonathan Cameron }; 76e64e7d5cSJonathan Cameron 77e64e7d5cSJonathan Cameron static const struct attribute_group *iio_sysfs_trig_groups[] = { 78e64e7d5cSJonathan Cameron &iio_sysfs_trig_group, 79e64e7d5cSJonathan Cameron NULL 80e64e7d5cSJonathan Cameron }; 81e64e7d5cSJonathan Cameron 82e64e7d5cSJonathan Cameron 83e64e7d5cSJonathan Cameron /* Nothing to actually do upon release */ 84e64e7d5cSJonathan Cameron static void iio_trigger_sysfs_release(struct device *dev) 85e64e7d5cSJonathan Cameron { 86e64e7d5cSJonathan Cameron } 87e64e7d5cSJonathan Cameron 88e64e7d5cSJonathan Cameron static struct device iio_sysfs_trig_dev = { 89e64e7d5cSJonathan Cameron .bus = &iio_bus_type, 90e64e7d5cSJonathan Cameron .groups = iio_sysfs_trig_groups, 91e64e7d5cSJonathan Cameron .release = &iio_trigger_sysfs_release, 92e64e7d5cSJonathan Cameron }; 93e64e7d5cSJonathan Cameron 94e64e7d5cSJonathan Cameron static void iio_sysfs_trigger_work(struct irq_work *work) 95e64e7d5cSJonathan Cameron { 96e64e7d5cSJonathan Cameron struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig, 97e64e7d5cSJonathan Cameron work); 98e64e7d5cSJonathan Cameron 99e64e7d5cSJonathan Cameron iio_trigger_poll(trig->trig, 0); 100e64e7d5cSJonathan Cameron } 101e64e7d5cSJonathan Cameron 102e64e7d5cSJonathan Cameron static ssize_t iio_sysfs_trigger_poll(struct device *dev, 103e64e7d5cSJonathan Cameron struct device_attribute *attr, const char *buf, size_t count) 104e64e7d5cSJonathan Cameron { 105e64e7d5cSJonathan Cameron struct iio_trigger *trig = to_iio_trigger(dev); 106e64e7d5cSJonathan Cameron struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig); 107e64e7d5cSJonathan Cameron 108e64e7d5cSJonathan Cameron irq_work_queue(&sysfs_trig->work); 109e64e7d5cSJonathan Cameron 110e64e7d5cSJonathan Cameron return count; 111e64e7d5cSJonathan Cameron } 112e64e7d5cSJonathan Cameron 113e64e7d5cSJonathan Cameron static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll); 114e64e7d5cSJonathan Cameron 115e64e7d5cSJonathan Cameron static struct attribute *iio_sysfs_trigger_attrs[] = { 116e64e7d5cSJonathan Cameron &dev_attr_trigger_now.attr, 117e64e7d5cSJonathan Cameron NULL, 118e64e7d5cSJonathan Cameron }; 119e64e7d5cSJonathan Cameron 120e64e7d5cSJonathan Cameron static const struct attribute_group iio_sysfs_trigger_attr_group = { 121e64e7d5cSJonathan Cameron .attrs = iio_sysfs_trigger_attrs, 122e64e7d5cSJonathan Cameron }; 123e64e7d5cSJonathan Cameron 124e64e7d5cSJonathan Cameron static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = { 125e64e7d5cSJonathan Cameron &iio_sysfs_trigger_attr_group, 126e64e7d5cSJonathan Cameron NULL 127e64e7d5cSJonathan Cameron }; 128e64e7d5cSJonathan Cameron 129e64e7d5cSJonathan Cameron static const struct iio_trigger_ops iio_sysfs_trigger_ops = { 130e64e7d5cSJonathan Cameron .owner = THIS_MODULE, 131e64e7d5cSJonathan Cameron }; 132e64e7d5cSJonathan Cameron 133e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_probe(int id) 134e64e7d5cSJonathan Cameron { 135e64e7d5cSJonathan Cameron struct iio_sysfs_trig *t; 136e64e7d5cSJonathan Cameron int ret; 137e64e7d5cSJonathan Cameron bool foundit = false; 138*10a485c5SDenis CIOCCA mutex_lock(&iio_sysfs_trig_list_mut); 139e64e7d5cSJonathan Cameron list_for_each_entry(t, &iio_sysfs_trig_list, l) 140e64e7d5cSJonathan Cameron if (id == t->id) { 141e64e7d5cSJonathan Cameron foundit = true; 142e64e7d5cSJonathan Cameron break; 143e64e7d5cSJonathan Cameron } 144e64e7d5cSJonathan Cameron if (foundit) { 145e64e7d5cSJonathan Cameron ret = -EINVAL; 146e64e7d5cSJonathan Cameron goto out1; 147e64e7d5cSJonathan Cameron } 148e64e7d5cSJonathan Cameron t = kmalloc(sizeof(*t), GFP_KERNEL); 149e64e7d5cSJonathan Cameron if (t == NULL) { 150e64e7d5cSJonathan Cameron ret = -ENOMEM; 151e64e7d5cSJonathan Cameron goto out1; 152e64e7d5cSJonathan Cameron } 153e64e7d5cSJonathan Cameron t->id = id; 154e64e7d5cSJonathan Cameron t->trig = iio_trigger_alloc("sysfstrig%d", id); 155e64e7d5cSJonathan Cameron if (!t->trig) { 156e64e7d5cSJonathan Cameron ret = -ENOMEM; 157e64e7d5cSJonathan Cameron goto free_t; 158e64e7d5cSJonathan Cameron } 159e64e7d5cSJonathan Cameron 160e64e7d5cSJonathan Cameron t->trig->dev.groups = iio_sysfs_trigger_attr_groups; 161e64e7d5cSJonathan Cameron t->trig->ops = &iio_sysfs_trigger_ops; 162e64e7d5cSJonathan Cameron t->trig->dev.parent = &iio_sysfs_trig_dev; 163e64e7d5cSJonathan Cameron iio_trigger_set_drvdata(t->trig, t); 164e64e7d5cSJonathan Cameron 165e64e7d5cSJonathan Cameron init_irq_work(&t->work, iio_sysfs_trigger_work); 166e64e7d5cSJonathan Cameron 167e64e7d5cSJonathan Cameron ret = iio_trigger_register(t->trig); 168e64e7d5cSJonathan Cameron if (ret) 169e64e7d5cSJonathan Cameron goto out2; 170e64e7d5cSJonathan Cameron list_add(&t->l, &iio_sysfs_trig_list); 171e64e7d5cSJonathan Cameron __module_get(THIS_MODULE); 172*10a485c5SDenis CIOCCA mutex_unlock(&iio_sysfs_trig_list_mut); 173e64e7d5cSJonathan Cameron return 0; 174e64e7d5cSJonathan Cameron 175e64e7d5cSJonathan Cameron out2: 176e64e7d5cSJonathan Cameron iio_trigger_put(t->trig); 177e64e7d5cSJonathan Cameron free_t: 178e64e7d5cSJonathan Cameron kfree(t); 179e64e7d5cSJonathan Cameron out1: 180*10a485c5SDenis CIOCCA mutex_unlock(&iio_sysfs_trig_list_mut); 181e64e7d5cSJonathan Cameron return ret; 182e64e7d5cSJonathan Cameron } 183e64e7d5cSJonathan Cameron 184e64e7d5cSJonathan Cameron static int iio_sysfs_trigger_remove(int id) 185e64e7d5cSJonathan Cameron { 186e64e7d5cSJonathan Cameron bool foundit = false; 187e64e7d5cSJonathan Cameron struct iio_sysfs_trig *t; 188*10a485c5SDenis CIOCCA mutex_lock(&iio_sysfs_trig_list_mut); 189e64e7d5cSJonathan Cameron list_for_each_entry(t, &iio_sysfs_trig_list, l) 190e64e7d5cSJonathan Cameron if (id == t->id) { 191e64e7d5cSJonathan Cameron foundit = true; 192e64e7d5cSJonathan Cameron break; 193e64e7d5cSJonathan Cameron } 194e64e7d5cSJonathan Cameron if (!foundit) { 195*10a485c5SDenis CIOCCA mutex_unlock(&iio_sysfs_trig_list_mut); 196e64e7d5cSJonathan Cameron return -EINVAL; 197e64e7d5cSJonathan Cameron } 198e64e7d5cSJonathan Cameron 199e64e7d5cSJonathan Cameron iio_trigger_unregister(t->trig); 200e64e7d5cSJonathan Cameron iio_trigger_free(t->trig); 201e64e7d5cSJonathan Cameron 202e64e7d5cSJonathan Cameron list_del(&t->l); 203e64e7d5cSJonathan Cameron kfree(t); 204e64e7d5cSJonathan Cameron module_put(THIS_MODULE); 205*10a485c5SDenis CIOCCA mutex_unlock(&iio_sysfs_trig_list_mut); 206e64e7d5cSJonathan Cameron return 0; 207e64e7d5cSJonathan Cameron } 208e64e7d5cSJonathan Cameron 209e64e7d5cSJonathan Cameron 210e64e7d5cSJonathan Cameron static int __init iio_sysfs_trig_init(void) 211e64e7d5cSJonathan Cameron { 212e64e7d5cSJonathan Cameron device_initialize(&iio_sysfs_trig_dev); 213e64e7d5cSJonathan Cameron dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger"); 214e64e7d5cSJonathan Cameron return device_add(&iio_sysfs_trig_dev); 215e64e7d5cSJonathan Cameron } 216e64e7d5cSJonathan Cameron module_init(iio_sysfs_trig_init); 217e64e7d5cSJonathan Cameron 218e64e7d5cSJonathan Cameron static void __exit iio_sysfs_trig_exit(void) 219e64e7d5cSJonathan Cameron { 220e64e7d5cSJonathan Cameron device_unregister(&iio_sysfs_trig_dev); 221e64e7d5cSJonathan Cameron } 222e64e7d5cSJonathan Cameron module_exit(iio_sysfs_trig_exit); 223e64e7d5cSJonathan Cameron 224e64e7d5cSJonathan Cameron MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 225e64e7d5cSJonathan Cameron MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem"); 226e64e7d5cSJonathan Cameron MODULE_LICENSE("GPL v2"); 227e64e7d5cSJonathan Cameron MODULE_ALIAS("platform:iio-trig-sysfs"); 228