diff --git a/triggers/Makefile b/triggers/Makefile index 2729569764dec6586225805d543a62824a20e1a1..ecd25a0f47c97654348ec77eb653c3479461f225 100644 --- a/triggers/Makefile +++ b/triggers/Makefile @@ -3,6 +3,7 @@ LINUX ?= /lib/modules/$(shell uname -r)/build EXTRA_CFLAGS += -I$(obj)/../include/ obj-m = zio-trig-timer.o +obj-m += zio-trig-irq.o all: $(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules diff --git a/triggers/zio-trig-irq.c b/triggers/zio-trig-irq.c new file mode 100644 index 0000000000000000000000000000000000000000..ba6bcbe649abca1bd19c785697438d37bbd07482 --- /dev/null +++ b/triggers/zio-trig-irq.c @@ -0,0 +1,204 @@ +/* Alessandro Rubini for CERN, 2011, GNU GPLv2 or later */ + +/* + * This is a trigger based on an external IRQ. You can specify the IRQ + * number or the GPIO number -- then the associated IRQ is used + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> + +#include <linux/zio.h> +#include <linux/zio-sysfs.h> +#include <linux/zio-buffer.h> +#include <linux/zio-trigger.h> + +static int zti_irq = -1; +static int zti_gpio = -1; +module_param_named(irq, zti_irq, int, 0444); +module_param_named(gpio, zti_gpio, int, 0444); + +enum zti_attrs { + ZTI_ATTR_NSAMPLES = 0, + ZTI_ATTR_IRQ, + ZTI_ATTR_GPIO, +}; + +static DEFINE_ZATTR_STD(TRIG, zti_std_attr) = { + ZATTR_REG(trig, ZATTR_TRIG_NSAMPLES, S_IRUGO | S_IWUGO, + ZTI_ATTR_NSAMPLES, 16), +}; + +static struct zio_attribute zti_ext_attr[] = { + ZATTR_EXT_REG("irq", S_IRUGO, ZTI_ATTR_IRQ, -1), + ZATTR_EXT_REG("gpio", S_IRUGO, ZTI_ATTR_GPIO, -1), +}; +int zti_conf_set (struct kobject *kobj, struct zio_attribute *zattr, + uint32_t usr_val) +{ + struct zio_ti *ti = to_zio_ti(kobj); + + pr_debug("%s:%d\n", __func__, __LINE__); + zattr->value = usr_val; + switch (zattr->priv.addr) { + case ZTI_ATTR_NSAMPLES: + ti->current_ctrl->nsamples = usr_val; + break; + /* other attributes are read-only */ + default: + pr_err("%s: unknown \"addr\" for configuration\n", __func__); + return -EINVAL; + } + return 0; +} + +struct zio_sys_operations zti_s_ops = { + .conf_set = zti_conf_set, +}; + +irqreturn_t zti_handler(int irq, void *dev_id) +{ + struct zio_ti *ti = dev_id; + + /* When a trigger fires, we must prepare our control and timestamp */ + getnstimeofday(&ti->tstamp); + zio_fire_trigger(ti); + return IRQ_HANDLED; +} + +/* + * The trigger operations are the core of a trigger type + */ +static int zti_push_block(struct zio_ti *ti, struct zio_channel *chan, + struct zio_block *block) +{ + /* software triggers must store pending stuff in chan->t_priv */ + pr_debug("%s:%d\n", __func__, __LINE__); + + if (chan->t_priv) + return -EBUSY; + chan->t_priv = block; + return 0; +} + +static int zti_config(struct zio_ti *ti, struct zio_control *ctrl) +{ + /* FIXME: config is not supported yet */ + + pr_debug("%s:%d\n", __func__, __LINE__); + return 0; +} + +static struct zio_ti *zti_create(struct zio_trigger_type *trig, + struct zio_cset *cset, + struct zio_control *ctrl, fmode_t flags) +{ + struct zio_ti *ti; + + int ret; + pr_debug("%s:%d\n", __func__, __LINE__); + + ti = kzalloc(sizeof(*ti), GFP_KERNEL); + if (!ti) + return ERR_PTR(-ENOMEM); + + /* The current control is already filled: just set nsamples */ + ctrl->nsamples = zti_std_attr[ZATTR_TRIG_NSAMPLES].value; + ti->current_ctrl = ctrl; + + ret = request_irq(zti_irq, zti_handler, IRQF_SHARED + | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + KBUILD_MODNAME, ti); + if (ret < 0) { + kfree(ti); + return ERR_PTR(ret); + } + return ti; +} + +static void zti_destroy(struct zio_ti *ti) +{ + pr_debug("%s:%d\n", __func__, __LINE__); + free_irq(zti_irq, &ti); + kfree(ti); +} + +static const struct zio_trigger_operations zti_trigger_ops = { + .push_block = zti_push_block, + .pull_block = NULL, + .data_done = zio_generic_data_done, + .config = zti_config, + .create = zti_create, + .destroy = zti_destroy, +}; + +static struct zio_trigger_type zti_trigger = { + .owner = THIS_MODULE, + .zattr_set = { + .std_zattr = zti_std_attr, + .ext_zattr = zti_ext_attr, + .n_ext_attr = ARRAY_SIZE(zti_ext_attr), + }, + .s_op = &zti_s_ops, + .t_op = &zti_trigger_ops, + .f_op = NULL, /* we use buffer fops */ +}; + +/* + * A validation function, called at insmod and at parameter change + */ +static int zti_validate(int irq, int gpio) +{ + int ret = 0; + + if (irq != -1 && gpio != -1) { + pr_err("%s: only set irq or gpio, not both\n", KBUILD_MODNAME); + return -EINVAL; + } + if (irq == -1 && gpio == -1) { + pr_err("%s: please set irq or gpio\n", KBUILD_MODNAME); + return -EINVAL; + } + if (gpio != -1) { + irq = gpio_to_irq(gpio); + if (irq >= 0) { + ret = gpio_request(gpio, KBUILD_MODNAME); + } else { + ret = irq; + } + } + if (ret < 0) { + pr_err("%s: invalid irq/gpio (%i/%i)\n", KBUILD_MODNAME, + gpio, irq); + return ret; + } + zti_irq = irq; /* used at trigger_create time */ + return 0; +} + +/* + * init and exit + */ +static int __init zti_init(void) +{ + int ret = zti_validate(zti_irq, zti_gpio); + if (ret) + return ret; + return zio_register_trig(&zti_trigger, "irq"); +} + +static void __exit zti_exit(void) +{ + zio_unregister_trig(&zti_trigger); + if (zti_gpio) + gpio_free(zti_gpio); +} + +module_init(zti_init); +module_exit(zti_exit); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("GPL");