[PATCH v3 2/3] iio: trigger: Add support for highres timer trigger type

From: Daniel Baluta
Date: Mon Apr 06 2015 - 10:17:39 EST


This patch adds a new trigger type - the high resoluton timer ("hrtimer")
under /config/iio/triggers/ and also creates a module that implements hrtimer
trigger type functionality.

A new IIO trigger is allocated when a new directory is created under
/config/iio/triggers/. The name of the trigger is the same as the dir
name. Each trigger will have a "delay" attribute representing the delay
between two successive IIO trigger_poll calls.

Signed-off-by: Marten Svanfeldt <marten@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Lars-Peter Clausen <lars@xxxxxxxxxx>
Signed-off-by: Daniel Baluta <daniel.baluta@xxxxxxxxx>
---
Changes since v2:
* none
Changes since v1:
* replaced sampling_frequency with delay to make the in kernel
processing easier
* implemented hrtimer driver using the new API proposed in patch 1

drivers/iio/industrialio-configfs.c | 164 +++++++++++++++++++++++++++++++
drivers/iio/trigger/Kconfig | 9 ++
drivers/iio/trigger/Makefile | 2 +
drivers/iio/trigger/iio-trig-hrtimer.c | 154 +++++++++++++++++++++++++++++
include/linux/iio/iio_configfs_trigger.h | 1 +
5 files changed, 330 insertions(+)
create mode 100644 drivers/iio/trigger/iio-trig-hrtimer.c

diff --git a/drivers/iio/industrialio-configfs.c b/drivers/iio/industrialio-configfs.c
index d8ffd76..a0e5d24 100644
--- a/drivers/iio/industrialio-configfs.c
+++ b/drivers/iio/industrialio-configfs.c
@@ -71,7 +71,170 @@ struct iio_configfs_trigger_type *iio_get_configfs_trigger_type(int type)
return t;
}

+struct iio_trigger_item {
+ struct config_item item;
+ struct iio_configfs_trigger_info *trigger_info;
+};
+
+static
+inline struct iio_trigger_item *to_iio_trigger_item(struct config_item *item)
+{
+ if (!item)
+ return NULL;
+ return container_of(item, struct iio_trigger_item, item);
+}
+
+CONFIGFS_ATTR_STRUCT(iio_trigger_item);
+
+#define IIO_TRIGGER_ITEM_ATTR(_name, _mode, _show, _store) \
+struct iio_trigger_item_attribute iio_trigger_item_attr_##_name = \
+ __CONFIGFS_ATTR(_name, _mode, _show, _store)
+
+static
+ssize_t iio_trigger_item_get_delay(struct iio_trigger_item *item, char *page)
+{
+ unsigned long delay;
+ int ret;
+
+ struct iio_configfs_trigger_type *t = item->trigger_info->trigger_type;
+
+ ret = t->trigger_ops->get_delay(item->trigger_info, &delay);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(page, PAGE_SIZE, "%lu\n", delay);
+}
+
+static
+ssize_t iio_trigger_item_set_delay(struct iio_trigger_item *item,
+ const char *page,
+ size_t count)
+{
+ int ret;
+ unsigned long delay;
+ struct iio_configfs_trigger_type *t = item->trigger_info->trigger_type;
+
+ ret = kstrtoul(page, 10, &delay);
+ if (ret < 0)
+ return ret;
+
+ ret = t->trigger_ops->set_delay(item->trigger_info, delay);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+IIO_TRIGGER_ITEM_ATTR(delay, S_IRUGO | S_IWUSR,
+ iio_trigger_item_get_delay,
+ iio_trigger_item_set_delay);
+
+static struct configfs_attribute *iio_trigger_item_attrs[] = {
+ &iio_trigger_item_attr_delay.attr,
+ NULL
+};
+
+CONFIGFS_ATTR_OPS(iio_trigger_item);
+
+static struct configfs_item_operations iio_trigger_item_ops = {
+ .show_attribute = iio_trigger_item_attr_show,
+ .store_attribute = iio_trigger_item_attr_store,
+};
+
+static struct config_item_type iio_trigger_item_type;
+
+static struct config_item *iio_hrtimer_make_item(struct config_group *group,
+ const char *name)
+{
+ struct iio_trigger_item *trigger_item;
+ struct iio_configfs_trigger_info *trigger_info;
+ struct iio_configfs_trigger_type *trigger_type;
+ int ret;
+
+ trigger_item = kzalloc(sizeof(*trigger_item), GFP_KERNEL);
+ if (!trigger_item)
+ return ERR_PTR(-ENOMEM);
+ trigger_info = kzalloc(sizeof(*trigger_info), GFP_KERNEL);
+ if (!trigger_info) {
+ ret = -ENOMEM;
+ goto out_free_trigger_item;
+ }
+
+ trigger_info->name = kstrdup(name, GFP_KERNEL);
+ if (!trigger_info->name) {
+ ret = -ENOMEM;
+ goto out_free_trigger_info;
+ }
+
+ trigger_type = iio_get_configfs_trigger_type(IIO_TRIGGER_TYPE_HRTIMER);
+ if (!trigger_type) {
+ pr_err("Unable to get hrtimer trigger!\n");
+ ret = -ENODEV;
+ goto out_free_trigger_name;
+ }
+ ret = trigger_type->trigger_ops->probe(trigger_info);
+ if (ret < 0)
+ goto out_error_module_put;
+
+ trigger_info->trigger_type = trigger_type;
+ trigger_item->trigger_info = trigger_info;
+
+ config_item_init_type_name(&trigger_item->item, name,
+ &iio_trigger_item_type);
+
+ return &trigger_item->item;
+out_error_module_put:
+ module_put(trigger_type->owner);
+out_free_trigger_name:
+ kfree(trigger_info->name);
+out_free_trigger_info:
+ kfree(trigger_info);
+out_free_trigger_item:
+ kfree(trigger_item);
+ return ERR_PTR(ret);
+}
+
+static void iio_hrtimer_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct iio_trigger_item *trigger_item;
+ struct iio_configfs_trigger_type *trigger_type;
+
+ trigger_item = container_of(item, struct iio_trigger_item, item);
+ trigger_type = trigger_item->trigger_info->trigger_type;
+
+ trigger_type->trigger_ops->remove(trigger_item->trigger_info);
+ module_put(trigger_type->owner);
+ kfree(trigger_item->trigger_info->name);
+ kfree(trigger_item->trigger_info);
+ kfree(trigger_item);
+}
+
+static struct configfs_group_operations iio_triggers_hrtimer_group_ops = {
+ .make_item = iio_hrtimer_make_item,
+ .drop_item = iio_hrtimer_drop_item,
+};
+
+static struct config_item_type iio_trigger_item_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_item_ops = &iio_trigger_item_ops,
+ .ct_attrs = iio_trigger_item_attrs,
+};
+
+static struct config_item_type iio_triggers_hrtimer_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_group_ops = &iio_triggers_hrtimer_group_ops,
+};
+
+static struct config_group iio_triggers_hrtimer_group = {
+ .cg_item = {
+ .ci_namebuf = "hrtimer",
+ .ci_type = &iio_triggers_hrtimer_type,
+ },
+};
+
static struct config_group *iio_triggers_default_groups[] = {
+ &iio_triggers_hrtimer_group,
NULL
};

@@ -109,6 +272,7 @@ static struct configfs_subsystem iio_configfs_subsys = {

static int __init iio_configfs_init(void)
{
+ config_group_init(&iio_triggers_hrtimer_group);
config_group_init(&iio_triggers_group);
config_group_init(&iio_configfs_subsys.su_group);

diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index 7999612..e21688e 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -5,6 +5,15 @@

menu "Triggers - standalone"

+config IIO_HRTIMER_TRIGGER
+ tristate "High resolution timer trigger"
+ depends on IIO_CONFIGFS
+ help
+ Provides a frequency based IIO trigger using hrtimers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-hrtimer.
+
config IIO_INTERRUPT_TRIGGER
tristate "Generic interrupt trigger"
help
diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
index 0694dae..fe06eb5 100644
--- a/drivers/iio/trigger/Makefile
+++ b/drivers/iio/trigger/Makefile
@@ -3,5 +3,7 @@
#

# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c
new file mode 100644
index 0000000..0acde8d
--- /dev/null
+++ b/drivers/iio/trigger/iio-trig-hrtimer.c
@@ -0,0 +1,154 @@
+/**
+ * The industrial I/O periodic hrtimer trigger driver
+ *
+ * Copyright (C) Intuitive Aerial AB
+ * Written by Marten Svanfeldt, marten@xxxxxxxxxxxxxxxxxxx
+ * Copyright (C) 2012, Analog Device Inc.
+ * Author: Lars-Peter Clausen <lars@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/iio_configfs_trigger.h>
+
+/* default delay in ns (100Hz) */
+#define HRTIMER_TRIGGER_DEFAULT_DELAY 100000000
+
+struct iio_hrtimer_trig_info {
+ struct iio_configfs_trigger_info *trigger_info;
+ struct hrtimer timer;
+ unsigned long delay;
+};
+
+static enum hrtimer_restart iio_trig_hrtimer_trig(struct hrtimer *timer)
+{
+ struct iio_hrtimer_trig_info *trig_info;
+
+ trig_info = container_of(timer, struct iio_hrtimer_trig_info, timer);
+
+ hrtimer_forward_now(timer, ns_to_ktime(trig_info->delay));
+ iio_trigger_poll(trig_info->trigger_info->trigger);
+
+ return HRTIMER_RESTART;
+}
+
+static int iio_trig_hrtimer_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_hrtimer_trig_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(trig);
+
+ if (state)
+ hrtimer_start(&trig_info->timer, ns_to_ktime(trig_info->delay),
+ HRTIMER_MODE_REL);
+ else
+ hrtimer_cancel(&trig_info->timer);
+
+ return 0;
+}
+
+int iio_trig_hrtimer_get_delay(struct iio_configfs_trigger_info *t,
+ unsigned long *delay)
+{
+ struct iio_hrtimer_trig_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(t->trigger);
+ *delay = trig_info->delay;
+
+ return 0;
+}
+
+int iio_trig_hrtimer_set_delay(struct iio_configfs_trigger_info *t,
+ unsigned long delay)
+{
+ struct iio_hrtimer_trig_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(t->trigger);
+
+ if (!delay)
+ return -EINVAL;
+
+ trig_info->delay = delay;
+
+ return 0;
+}
+
+static const struct iio_trigger_ops iio_hrtimer_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = iio_trig_hrtimer_set_state,
+};
+
+static int iio_trig_hrtimer_probe(struct iio_configfs_trigger_info *t)
+{
+ struct iio_hrtimer_trig_info *trig_info;
+ int ret;
+
+ trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+ if (!trig_info)
+ return -ENOMEM;
+
+ trig_info->trigger_info = t;
+
+ t->trigger = iio_trigger_alloc("%s", t->name);
+ if (!t->trigger)
+ return -ENOMEM;
+
+ iio_trigger_set_drvdata(t->trigger, trig_info);
+ t->trigger->ops = &iio_hrtimer_trigger_ops;
+
+ hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ trig_info->timer.function = iio_trig_hrtimer_trig;
+
+ trig_info->delay = HRTIMER_TRIGGER_DEFAULT_DELAY;
+
+ ret = iio_trigger_register(t->trigger);
+ if (ret)
+ goto err_free_trigger;
+
+ return 0;
+err_free_trigger:
+ iio_trigger_free(t->trigger);
+
+ return ret;
+}
+
+static int iio_trig_hrtimer_remove(struct iio_configfs_trigger_info *t)
+{
+ struct iio_hrtimer_trig_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(t->trigger);
+
+ iio_trigger_unregister(t->trigger);
+ hrtimer_cancel(&trig_info->timer);
+ iio_trigger_free(t->trigger);
+
+ return 0;
+}
+
+struct iio_configfs_trigger_ops hrtimer_trigger_ops = {
+ .get_delay = iio_trig_hrtimer_get_delay,
+ .set_delay = iio_trig_hrtimer_set_delay,
+ .probe = iio_trig_hrtimer_probe,
+ .remove = iio_trig_hrtimer_remove,
+};
+
+struct iio_configfs_trigger_type iio_configfs_hrtimer = {
+ .type = IIO_TRIGGER_TYPE_HRTIMER,
+ .owner = THIS_MODULE,
+ .trigger_ops = &hrtimer_trigger_ops,
+};
+
+module_iio_configfs_trigger_driver(iio_configfs_hrtimer);
+
+MODULE_AUTHOR("Marten Svanfeldt <marten@xxxxxxxxxxxxxxxxxxx>");
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@xxxxxxxxx>");
+MODULE_DESCRIPTION("Periodic hrtimer trigger for the IIO subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/iio_configfs_trigger.h b/include/linux/iio/iio_configfs_trigger.h
index 75e76f2..6233733 100644
--- a/include/linux/iio/iio_configfs_trigger.h
+++ b/include/linux/iio/iio_configfs_trigger.h
@@ -10,6 +10,7 @@
unregister_configfs_trigger)

enum {
+ IIO_TRIGGER_TYPE_HRTIMER = 0,
IIO_TRIGGER_TYPE_MAX,
};

--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/