linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2/3] staging:iio:dummy Add event support + fake event generator
  2011-09-20 15:58 [PATCH 0/3 V2] staging:iio:Documentation via dummy driver Jonathan Cameron
@ 2011-09-20 15:58 ` Jonathan Cameron
  2011-09-21  9:59   ` Jonathan Cameron
  0 siblings, 1 reply; 6+ messages in thread
From: Jonathan Cameron @ 2011-09-20 15:58 UTC (permalink / raw)
  To: linux-iio
  Cc: manuel.stahl, jonathan.kunkee, michael.hennerich,
	Jonathan Cameron

The event generator is not very pretty but does the job and
allows this driver to look a lot more like a normal driver
than it otherwise would.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/staging/iio/Kconfig                   |   20 ++-
 drivers/staging/iio/Makefile                  |    6 +-
 drivers/staging/iio/iio_core.h                |    6 +-
 drivers/staging/iio/iio_dummy_evgen.c         |  217 +++++++++++++++++++++++++
 drivers/staging/iio/iio_dummy_evgen.h         |    2 +
 drivers/staging/iio/iio_simple_dummy.c        |   50 ++++---
 drivers/staging/iio/iio_simple_dummy.h        |   81 +++++++++
 drivers/staging/iio/iio_simple_dummy_events.c |  190 ++++++++++++++++++++++
 8 files changed, 543 insertions(+), 29 deletions(-)

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 0accc21..75128a0 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -70,12 +70,26 @@ source "drivers/staging/iio/meter/Kconfig"
 source "drivers/staging/iio/resolver/Kconfig"
 source "drivers/staging/iio/trigger/Kconfig"
 
+config IIO_DUMMY_EVGEN
+       tristate
+
 config IIO_SIMPLE_DUMMY
        tristate "An example driver with no hardware requirements"
+       select IIO_SIMPLE_DUMMY_EVGEN if IIO_SIMPLE_DUMMY_EVENTS
        help
-	Driver intended mainly as documentation for how to write
-	a driver. May also be useful for testing userspace code
-	without hardward.
+	 Driver intended mainly as documentation for how to write
+	 a driver. May also be useful for testing userspace code
+	 without hardward.
+
+if IIO_SIMPLE_DUMMY
+
+config IIO_SIMPLE_DUMMY_EVENTS
+       boolean "Event generation support"
+       select IIO_DUMMY_EVGEN
+       help
+         Add some dummy events to the simple dummy driver.
+
 
+endif # IIO_SIMPLE_DUMMY
 
 endif # IIO
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 014d8f1..75aacfd 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -10,7 +10,11 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 
-obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_simple_dummy.o
+obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
+iio_dummy-y := iio_simple_dummy.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
+
+obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
 
 obj-y += accel/
 obj-y += adc/
diff --git a/drivers/staging/iio/iio_core.h b/drivers/staging/iio/iio_core.h
index dde9e3e..0743f39 100644
--- a/drivers/staging/iio/iio_core.h
+++ b/drivers/staging/iio/iio_core.h
@@ -33,7 +33,7 @@ int __iio_add_chan_devattr(const char *postfix,
 #ifdef CONFIG_IIO_BUFFER
 struct poll_table_struct;
 
-void iio_chrdev_buffer_open(struct iio_dev *indio_dev);
+int iio_chrdev_buffer_open(struct iio_dev *indio_dev);
 void iio_chrdev_buffer_release(struct iio_dev *indio_dev);
 
 unsigned int iio_buffer_poll(struct file *filp,
@@ -47,8 +47,8 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
 
 #else
 
-static inline void iio_chrdev_buffer_open(struct iio_dev *indio_dev)
-{}
+static inline int iio_chrdev_buffer_open(struct iio_dev *indio_dev)
+{ return -EINVAL; }
 static inline void iio_chrdev_buffer_release(struct iio_dev *indio_dev)
 {}
 
diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c
new file mode 100644
index 0000000..da657d1
--- /dev/null
+++ b/drivers/staging/iio/iio_dummy_evgen.c
@@ -0,0 +1,217 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Companion module to the iio simple dummy example driver.
+ * The purpose of this is to generate 'fake' event interrupts thus
+ * allowing that driver's code to be as close as possible to that of
+ * a normal driver talking to hardware.  The approach used here
+ * is not intended to be general and just happens to work for this
+ * particular use case.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+
+#include "iio_dummy_evgen.h"
+#include "iio.h"
+#include "sysfs.h"
+
+/* Fiddly bit of faking and irq without hardware */
+#define IIO_EVENTGEN_NO 10
+/**
+ * struct iio_dummy_evgen - evgen state
+ * @chip: irq chip we are faking
+ * @base: base of irq range
+ * @enabled: mask of which irqs are enabled
+ * @inuse: mask of which irqs actually have anyone connected
+ * @lock: protect the evgen state
+ */
+struct iio_dummy_eventgen {
+	struct irq_chip chip;
+	int base;
+	bool enabled[IIO_EVENTGEN_NO];
+	bool inuse[IIO_EVENTGEN_NO];
+	struct mutex lock;
+};
+
+/* We can only ever have one instance of this 'device' */
+static struct iio_dummy_eventgen *iio_evgen;
+static const char *iio_evgen_name = "iio_dummy_evgen";
+
+static void iio_dummy_event_irqmask(struct irq_data *d)
+{
+	struct irq_chip *chip = irq_data_get_irq_chip(d);
+	struct iio_dummy_eventgen *evgen =
+		container_of(chip, struct iio_dummy_eventgen, chip);
+
+	evgen->enabled[d->irq - evgen->base] = false;
+}
+
+static void iio_dummy_event_irqunmask(struct irq_data *d)
+{
+	struct irq_chip *chip = irq_data_get_irq_chip(d);
+	struct iio_dummy_eventgen *evgen =
+		container_of(chip, struct iio_dummy_eventgen, chip);
+
+	evgen->enabled[d->irq - evgen->base] = true;
+}
+
+static int iio_dummy_evgen_create(void)
+{
+	int ret, i;
+
+	iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
+	if (iio_evgen == NULL)
+		return -ENOMEM;
+
+	iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0);
+	if (iio_evgen->base < 0) {
+		ret = iio_evgen->base;
+		kfree(iio_evgen);
+		return ret;
+	}
+	iio_evgen->chip.name = iio_evgen_name;
+	iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask;
+	iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask;
+	for (i = 0; i < IIO_EVENTGEN_NO; i++) {
+		irq_set_chip(iio_evgen->base + i, &iio_evgen->chip);
+		irq_set_handler(iio_evgen->base + i, &handle_simple_irq);
+		irq_modify_status(iio_evgen->base + i,
+				  IRQ_NOREQUEST | IRQ_NOAUTOEN,
+				  IRQ_NOPROBE);
+	}
+	mutex_init(&iio_evgen->lock);
+	return 0;
+}
+
+/**
+ * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
+ *
+ * This function will give a free allocated irq to a client device.
+ * That irq can then be caused to 'fire' by using the associated sysfs file.
+ */
+int iio_dummy_evgen_get_irq(void)
+{
+	int i, ret = 0;
+	mutex_lock(&iio_evgen->lock);
+	for (i = 0; i < IIO_EVENTGEN_NO; i++)
+		if (iio_evgen->inuse[i] == false) {
+			ret = iio_evgen->base + i;
+			iio_evgen->inuse[i] = true;
+			break;
+		}
+	mutex_unlock(&iio_evgen->lock);
+	if (i == IIO_EVENTGEN_NO)
+		return -ENOMEM;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
+
+/**
+ * iio_dummy_evgen_release_irq() - give the irq back.
+ * @irq: irq being returned to the pool
+ *
+ * Used by client driver instances to give the irqs back when they disconnect
+ */
+int iio_dummy_evgen_release_irq(int irq)
+{
+	mutex_lock(&iio_evgen->lock);
+	iio_evgen->inuse[irq - iio_evgen->base] = false;
+	mutex_unlock(&iio_evgen->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
+
+static void iio_dummy_evgen_free(void)
+{
+	irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO);
+	kfree(iio_evgen);
+}
+
+static void iio_evgen_release(struct device *dev)
+{
+	iio_dummy_evgen_free();
+}
+
+static ssize_t iio_evgen_poke(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf,
+			      size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	if (iio_evgen->enabled[this_attr->address])
+		handle_nested_irq(iio_evgen->base + this_attr->address);
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
+static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
+static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
+static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
+static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
+static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
+static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
+static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
+static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
+static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
+
+static struct attribute *iio_evgen_attrs[] = {
+	&iio_dev_attr_poke_ev0.dev_attr.attr,
+	&iio_dev_attr_poke_ev1.dev_attr.attr,
+	&iio_dev_attr_poke_ev2.dev_attr.attr,
+	&iio_dev_attr_poke_ev3.dev_attr.attr,
+	&iio_dev_attr_poke_ev4.dev_attr.attr,
+	&iio_dev_attr_poke_ev5.dev_attr.attr,
+	&iio_dev_attr_poke_ev6.dev_attr.attr,
+	&iio_dev_attr_poke_ev7.dev_attr.attr,
+	&iio_dev_attr_poke_ev8.dev_attr.attr,
+	&iio_dev_attr_poke_ev9.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group iio_evgen_group = {
+	.attrs = iio_evgen_attrs,
+};
+
+static const struct attribute_group *iio_evgen_groups[] = {
+	&iio_evgen_group,
+	NULL
+};
+
+static struct device iio_evgen_dev = {
+	.bus = &iio_bus_type,
+	.groups = iio_evgen_groups,
+	.release = &iio_evgen_release,
+};
+static __init int iio_dummy_evgen_init(void)
+{
+	int ret = iio_dummy_evgen_create();
+	if (ret < 0)
+		return ret;
+	device_initialize(&iio_evgen_dev);
+	dev_set_name(&iio_evgen_dev, "iio_evgen");
+	return device_add(&iio_evgen_dev);
+}
+module_init(iio_dummy_evgen_init);
+
+static __exit void iio_dummy_evgen_exit(void)
+{
+	device_unregister(&iio_evgen_dev);
+}
+module_exit(iio_dummy_evgen_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("IIO dummy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h
new file mode 100644
index 0000000..d8845e2
--- /dev/null
+++ b/drivers/staging/iio/iio_dummy_evgen.h
@@ -0,0 +1,2 @@
+int iio_dummy_evgen_get_irq(void);
+int iio_dummy_evgen_release_irq(int irq);
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
index 463043e..d16c6a8 100644
--- a/drivers/staging/iio/iio_simple_dummy.c
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -20,6 +20,8 @@
 #include <linux/moduleparam.h>
 
 #include "iio.h"
+#include "sysfs.h"
+#include "iio_simple_dummy.h"
 
 /*
  * A few elements needed to fake a bus for this driver
@@ -53,26 +55,6 @@ static const struct iio_dummy_accel_calibscale dummy_scales[] = {
 	{ 733, 13, 0x9 }, /* 733.00013 */
 };
 
-/**
- * struct iio_dummy_state - device instance specific state.
- * @dac_val:			cache for dac value
- * @single_ended_adc_val:	cache for single ended adc value
- * @differential_adc_val:	cache for differential adc value
- * @accel_val:			cache for acceleration value
- * @accel_calibbias:		cache for acceleration calibbias
- * @accel_calibscale:		cache for acceleration calibscale
- * @lock:			lock to ensure state is consistent
- */
-struct iio_dummy_state {
-	int dac_val;
-	int single_ended_adc_val;
-	int differential_adc_val[2];
-	int accel_val;
-	int accel_calibbias;
-	const struct iio_dummy_accel_calibscale *accel_calibscale;
-	struct mutex lock;
-};
-
 /*
  * iio_dummy_channels - Description of available channels
  *
@@ -101,6 +83,14 @@ static struct iio_chan_spec iio_dummy_channels[] = {
 		 */
 		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
 
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+		/*
+		 * simple event - triggered when value rises above
+		 * a threshold
+		 */
+		.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH,
+					 IIO_EV_DIR_RISING),
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
 	},
 	/* Differential ADC channel in_voltage1-voltage2_raw etc*/
 	{
@@ -296,6 +286,12 @@ static const struct iio_info iio_dummy_info = {
 	.driver_module = THIS_MODULE,
 	.read_raw = &iio_dummy_read_raw,
 	.write_raw = &iio_dummy_write_raw,
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+	.read_event_config = &iio_simple_dummy_read_event_config,
+	.write_event_config = &iio_simple_dummy_write_event_config,
+	.read_event_value = &iio_simple_dummy_read_event_value,
+	.write_event_value = &iio_simple_dummy_write_event_value,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
 };
 
 /**
@@ -392,11 +388,16 @@ static int __devinit iio_dummy_probe(int index)
 	/* Specify that device provides sysfs type interfaces */
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
-	ret = iio_device_register(indio_dev);
+	ret = iio_simple_dummy_events_register(indio_dev);
 	if (ret < 0)
 		goto error_free_device;
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto error_unregister_events;
 
 	return 0;
+error_unregister_events:
+	iio_simple_dummy_events_unregister(indio_dev);
 error_free_device:
 	/* Note free device should only be called, before registration
 	 * has succeeded. */
@@ -413,6 +414,7 @@ error_ret:
  */
 static int iio_dummy_remove(int index)
 {
+	int ret;
 	/*
 	 * Get a pointer to the device instance iio_dev structure
 	 * from the bus subsystem. E.g.
@@ -423,10 +425,14 @@ static int iio_dummy_remove(int index)
 
 	/* Device specific code to power down etc */
 
+	ret = iio_simple_dummy_events_unregister(indio_dev);
+	if (ret)
+		goto error_ret;
 	/* Unregister the device and free all structures */
 	iio_device_unregister(indio_dev);
 
-	return 0;
+error_ret:
+	return ret;
 }
 
 /**
diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h
new file mode 100644
index 0000000..998fd1f
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy.h
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Join together the various functionality of iio_simple_dummy driver
+ */
+
+#include <linux/kernel.h>
+
+struct iio_dummy_accel_calibscale;
+
+/**
+ * struct iio_dummy_state - device instance specific state.
+ * @dac_val:			cache for dac value
+ * @single_ended_adc_val:	cache for single ended adc value
+ * @differential_adc_val:	cache for differential adc value
+ * @accel_val:			cache for acceleration value
+ * @accel_calibbias:		cache for acceleration calibbias
+ * @accel_calibscale:		cache for acceleration calibscale
+ * @lock:			lock to ensure state is consistent
+ * @event_irq:			irq number for event line (faked)
+ * @event_val:			cache for event theshold value
+ * @event_en:			cache of whether event is enabled
+ */
+struct iio_dummy_state {
+	int dac_val;
+	int single_ended_adc_val;
+	int differential_adc_val[2];
+	int accel_val;
+	int accel_calibbias;
+	const struct iio_dummy_accel_calibscale *accel_calibscale;
+	struct mutex lock;
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+	int event_irq;
+	int event_val;
+	bool event_en;
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+};
+
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+
+struct iio_dev;
+
+int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
+				       u64 event_code);
+
+int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
+					u64 event_code,
+					int state);
+
+int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
+				      u64 event_code,
+				      int *val);
+
+int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
+				       u64 event_code,
+				       int val);
+
+int iio_simple_dummy_events_register(struct iio_dev *indio_dev);
+int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev);
+
+#else /* Stubs for when events are disabled at compile time */
+
+static inline int
+iio_simple_dummy_events_register(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+static inline int
+iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
+
+
diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c
new file mode 100644
index 0000000..9f00cff
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy_events.c
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Event handling elements of industrial I/O reference driver.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "iio_simple_dummy.h"
+
+/* Evgen 'fakes' interrupt events for this example */
+#include "iio_dummy_evgen.h"
+
+/**
+ * iio_simple_dummy_read_event_config() - is event enabled?
+ * @indio_dev: the device instance data
+ * @event_code: event code of the event being queried
+ *
+ * This function would normally query the relevant registers or a cache to
+ * discover if the event generation is enabled on the device.
+ */
+int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
+				       u64 event_code)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	return st->event_en;
+}
+
+/**
+ * iio_simple_dummy_write_event_config() - set whether event is enabled
+ * @indio_dev: the device instance data
+ * @event_code: event code of event being enabled/disabled
+ * @state: whether to enable or disable the device.
+ *
+ * This function would normally set the relevant registers on the devices
+ * so that it generates the specified event. Here it just sets up a cached
+ * value.
+ */
+int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
+					u64 event_code,
+					int state)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	/*
+	 *  Deliberately over the top code splitting to illustrate
+	 * how this is done when multiple events exist.
+	 */
+	switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
+	case IIO_VOLTAGE:
+		switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) {
+		case IIO_EV_TYPE_THRESH:
+			if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
+			    IIO_EV_DIR_RISING)
+				st->event_en = state;
+			else
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * iio_simple_dummy_read_event_value() - get value associated with event
+ * @indio_dev: device instance specific data
+ * @event_code: event code for the event whose value is being queried
+ * @val: value for the event code.
+ *
+ * Many devices provide a large set of events of which only a subset may
+ * be enabled at a time, with value registers whose meaning changes depending
+ * on the event enabled. This often means that the driver must cache the values
+ * associated with each possible events so that the right value is in place when
+ * the enabled event is changed.
+ */
+int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
+				      u64 event_code,
+				      int *val)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	*val = st->event_val;
+
+	return 0;
+}
+
+/**
+ * iio_simple_dummy_write_event_value() - set value associate with event
+ * @indio_dev: device instance specific data
+ * @event_code: event code for the event whose value is being set
+ * @val: the value to be set.
+ */
+int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
+				       u64 event_code,
+				       int val)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	st->event_val = val;
+
+	return 0;
+}
+
+/**
+ * iio_simple_dummy_event_handler() - identify and pass on event
+ * @irq: irq of event line
+ * @private: pointer to device instance state.
+ *
+ * This handler is responsible for querying the device to find out what
+ * event occured and for then pushing that event towards userspace.
+ * Here only one event occurs so we push that directly on with locally
+ * grabbed timestamp.
+ */
+static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	iio_push_event(indio_dev,
+		       IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
+				      IIO_EV_DIR_RISING,
+				      IIO_EV_TYPE_THRESH, 0, 0, 0),
+		       iio_get_time_ns());
+	return IRQ_HANDLED;
+}
+
+/**
+ * iio_simple_dummy_events_register() - setup interrupt handling for events
+ * @indio_dev: device instance data
+ *
+ * This function requests the threaded interrupt to handle the events.
+ * Normally the irq is a hardware interrupt and the number comes
+ * from board configuration files.  Here we get it from a companion
+ * module that fakes the interrupt for us. Note that module in
+ * no way forms part of this example. Just assume that events magically
+ * appear via the provided interrupt.
+ */
+int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+	int ret;
+
+	/* Fire up event source - normally not present */
+	st->event_irq = iio_dummy_evgen_get_irq();
+	if (st->event_irq < 0) {
+		ret = st->event_irq;
+		goto error_ret;
+	}
+	ret = request_threaded_irq(st->event_irq,
+				   NULL,
+				   &iio_simple_dummy_event_handler,
+				   IRQF_ONESHOT,
+				   "iio_simple_event",
+				   indio_dev);
+	if (ret < 0)
+		goto error_free_evgen;
+	return 0;
+
+error_free_evgen:
+	iio_dummy_evgen_release_irq(st->event_irq);
+error_ret:
+	return ret;
+}
+
+/**
+ * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove
+ * @indio_dev: device instance data
+ */
+int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	free_irq(st->event_irq, indio_dev);
+	/* Not part of normal driver */
+	iio_dummy_evgen_release_irq(st->event_irq);
+
+	return 0;
+}
-- 
1.7.3.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/3] staging:iio:dummy Add event support + fake event generator
  2011-09-20 15:58 ` [PATCH 2/3] staging:iio:dummy Add event support + fake event generator Jonathan Cameron
@ 2011-09-21  9:59   ` Jonathan Cameron
  0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2011-09-21  9:59 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-iio, manuel.stahl, jonathan.kunkee, michael.hennerich

On 09/20/11 16:58, Jonathan Cameron wrote:
> The event generator is not very pretty but does the job and
> allows this driver to look a lot more like a normal driver
> than it otherwise would.
Clearly the iio_core changes have no place in here.  Those should
have been in a different patch entirely.  So please ignore them
in any review.
> 
> Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/Kconfig                   |   20 ++-
>  drivers/staging/iio/Makefile                  |    6 +-
>  drivers/staging/iio/iio_core.h                |    6 +-
>  drivers/staging/iio/iio_dummy_evgen.c         |  217 +++++++++++++++++++++++++
>  drivers/staging/iio/iio_dummy_evgen.h         |    2 +
>  drivers/staging/iio/iio_simple_dummy.c        |   50 ++++---
>  drivers/staging/iio/iio_simple_dummy.h        |   81 +++++++++
>  drivers/staging/iio/iio_simple_dummy_events.c |  190 ++++++++++++++++++++++
>  8 files changed, 543 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
> index 0accc21..75128a0 100644
> --- a/drivers/staging/iio/Kconfig
> +++ b/drivers/staging/iio/Kconfig
> @@ -70,12 +70,26 @@ source "drivers/staging/iio/meter/Kconfig"
>  source "drivers/staging/iio/resolver/Kconfig"
>  source "drivers/staging/iio/trigger/Kconfig"
>  
> +config IIO_DUMMY_EVGEN
> +       tristate
> +
>  config IIO_SIMPLE_DUMMY
>         tristate "An example driver with no hardware requirements"
> +       select IIO_SIMPLE_DUMMY_EVGEN if IIO_SIMPLE_DUMMY_EVENTS
>         help
> -	Driver intended mainly as documentation for how to write
> -	a driver. May also be useful for testing userspace code
> -	without hardward.
> +	 Driver intended mainly as documentation for how to write
> +	 a driver. May also be useful for testing userspace code
> +	 without hardward.
> +
> +if IIO_SIMPLE_DUMMY
> +
> +config IIO_SIMPLE_DUMMY_EVENTS
> +       boolean "Event generation support"
> +       select IIO_DUMMY_EVGEN
> +       help
> +         Add some dummy events to the simple dummy driver.
> +
>  
> +endif # IIO_SIMPLE_DUMMY
>  
>  endif # IIO
> diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
> index 014d8f1..75aacfd 100644
> --- a/drivers/staging/iio/Makefile
> +++ b/drivers/staging/iio/Makefile
> @@ -10,7 +10,11 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>  obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
>  obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>  
> -obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_simple_dummy.o
> +obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
> +iio_dummy-y := iio_simple_dummy.o
> +iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
> +
> +obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
>  
>  obj-y += accel/
>  obj-y += adc/
> diff --git a/drivers/staging/iio/iio_core.h b/drivers/staging/iio/iio_core.h
> index dde9e3e..0743f39 100644
> --- a/drivers/staging/iio/iio_core.h
> +++ b/drivers/staging/iio/iio_core.h
> @@ -33,7 +33,7 @@ int __iio_add_chan_devattr(const char *postfix,
>  #ifdef CONFIG_IIO_BUFFER
>  struct poll_table_struct;
>  
> -void iio_chrdev_buffer_open(struct iio_dev *indio_dev);
> +int iio_chrdev_buffer_open(struct iio_dev *indio_dev);
>  void iio_chrdev_buffer_release(struct iio_dev *indio_dev);
>  
>  unsigned int iio_buffer_poll(struct file *filp,
> @@ -47,8 +47,8 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
>  
>  #else
>  
> -static inline void iio_chrdev_buffer_open(struct iio_dev *indio_dev)
> -{}
> +static inline int iio_chrdev_buffer_open(struct iio_dev *indio_dev)
> +{ return -EINVAL; }
>  static inline void iio_chrdev_buffer_release(struct iio_dev *indio_dev)
>  {}
>  
> diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c
> new file mode 100644
> index 0000000..da657d1
> --- /dev/null
> +++ b/drivers/staging/iio/iio_dummy_evgen.c
> @@ -0,0 +1,217 @@
> +/**
> + * Copyright (c) 2011 Jonathan Cameron
> + *
> + * 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.
> + *
> + * Companion module to the iio simple dummy example driver.
> + * The purpose of this is to generate 'fake' event interrupts thus
> + * allowing that driver's code to be as close as possible to that of
> + * a normal driver talking to hardware.  The approach used here
> + * is not intended to be general and just happens to work for this
> + * particular use case.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mutex.h>
> +#include <linux/module.h>
> +#include <linux/sysfs.h>
> +
> +#include "iio_dummy_evgen.h"
> +#include "iio.h"
> +#include "sysfs.h"
> +
> +/* Fiddly bit of faking and irq without hardware */
> +#define IIO_EVENTGEN_NO 10
> +/**
> + * struct iio_dummy_evgen - evgen state
> + * @chip: irq chip we are faking
> + * @base: base of irq range
> + * @enabled: mask of which irqs are enabled
> + * @inuse: mask of which irqs actually have anyone connected
> + * @lock: protect the evgen state
> + */
> +struct iio_dummy_eventgen {
> +	struct irq_chip chip;
> +	int base;
> +	bool enabled[IIO_EVENTGEN_NO];
> +	bool inuse[IIO_EVENTGEN_NO];
> +	struct mutex lock;
> +};
> +
> +/* We can only ever have one instance of this 'device' */
> +static struct iio_dummy_eventgen *iio_evgen;
> +static const char *iio_evgen_name = "iio_dummy_evgen";
> +
> +static void iio_dummy_event_irqmask(struct irq_data *d)
> +{
> +	struct irq_chip *chip = irq_data_get_irq_chip(d);
> +	struct iio_dummy_eventgen *evgen =
> +		container_of(chip, struct iio_dummy_eventgen, chip);
> +
> +	evgen->enabled[d->irq - evgen->base] = false;
> +}
> +
> +static void iio_dummy_event_irqunmask(struct irq_data *d)
> +{
> +	struct irq_chip *chip = irq_data_get_irq_chip(d);
> +	struct iio_dummy_eventgen *evgen =
> +		container_of(chip, struct iio_dummy_eventgen, chip);
> +
> +	evgen->enabled[d->irq - evgen->base] = true;
> +}
> +
> +static int iio_dummy_evgen_create(void)
> +{
> +	int ret, i;
> +
> +	iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
> +	if (iio_evgen == NULL)
> +		return -ENOMEM;
> +
> +	iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0);
> +	if (iio_evgen->base < 0) {
> +		ret = iio_evgen->base;
> +		kfree(iio_evgen);
> +		return ret;
> +	}
> +	iio_evgen->chip.name = iio_evgen_name;
> +	iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask;
> +	iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask;
> +	for (i = 0; i < IIO_EVENTGEN_NO; i++) {
> +		irq_set_chip(iio_evgen->base + i, &iio_evgen->chip);
> +		irq_set_handler(iio_evgen->base + i, &handle_simple_irq);
> +		irq_modify_status(iio_evgen->base + i,
> +				  IRQ_NOREQUEST | IRQ_NOAUTOEN,
> +				  IRQ_NOPROBE);
> +	}
> +	mutex_init(&iio_evgen->lock);
> +	return 0;
> +}
> +
> +/**
> + * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
> + *
> + * This function will give a free allocated irq to a client device.
> + * That irq can then be caused to 'fire' by using the associated sysfs file.
> + */
> +int iio_dummy_evgen_get_irq(void)
> +{
> +	int i, ret = 0;
> +	mutex_lock(&iio_evgen->lock);
> +	for (i = 0; i < IIO_EVENTGEN_NO; i++)
> +		if (iio_evgen->inuse[i] == false) {
> +			ret = iio_evgen->base + i;
> +			iio_evgen->inuse[i] = true;
> +			break;
> +		}
> +	mutex_unlock(&iio_evgen->lock);
> +	if (i == IIO_EVENTGEN_NO)
> +		return -ENOMEM;
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
> +
> +/**
> + * iio_dummy_evgen_release_irq() - give the irq back.
> + * @irq: irq being returned to the pool
> + *
> + * Used by client driver instances to give the irqs back when they disconnect
> + */
> +int iio_dummy_evgen_release_irq(int irq)
> +{
> +	mutex_lock(&iio_evgen->lock);
> +	iio_evgen->inuse[irq - iio_evgen->base] = false;
> +	mutex_unlock(&iio_evgen->lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
> +
> +static void iio_dummy_evgen_free(void)
> +{
> +	irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO);
> +	kfree(iio_evgen);
> +}
> +
> +static void iio_evgen_release(struct device *dev)
> +{
> +	iio_dummy_evgen_free();
> +}
> +
> +static ssize_t iio_evgen_poke(struct device *dev,
> +			      struct device_attribute *attr,
> +			      const char *buf,
> +			      size_t len)
> +{
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +
> +	if (iio_evgen->enabled[this_attr->address])
> +		handle_nested_irq(iio_evgen->base + this_attr->address);
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
> +static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
> +static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
> +static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
> +static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
> +static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
> +static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
> +static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
> +static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
> +static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
> +
> +static struct attribute *iio_evgen_attrs[] = {
> +	&iio_dev_attr_poke_ev0.dev_attr.attr,
> +	&iio_dev_attr_poke_ev1.dev_attr.attr,
> +	&iio_dev_attr_poke_ev2.dev_attr.attr,
> +	&iio_dev_attr_poke_ev3.dev_attr.attr,
> +	&iio_dev_attr_poke_ev4.dev_attr.attr,
> +	&iio_dev_attr_poke_ev5.dev_attr.attr,
> +	&iio_dev_attr_poke_ev6.dev_attr.attr,
> +	&iio_dev_attr_poke_ev7.dev_attr.attr,
> +	&iio_dev_attr_poke_ev8.dev_attr.attr,
> +	&iio_dev_attr_poke_ev9.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group iio_evgen_group = {
> +	.attrs = iio_evgen_attrs,
> +};
> +
> +static const struct attribute_group *iio_evgen_groups[] = {
> +	&iio_evgen_group,
> +	NULL
> +};
> +
> +static struct device iio_evgen_dev = {
> +	.bus = &iio_bus_type,
> +	.groups = iio_evgen_groups,
> +	.release = &iio_evgen_release,
> +};
> +static __init int iio_dummy_evgen_init(void)
> +{
> +	int ret = iio_dummy_evgen_create();
> +	if (ret < 0)
> +		return ret;
> +	device_initialize(&iio_evgen_dev);
> +	dev_set_name(&iio_evgen_dev, "iio_evgen");
> +	return device_add(&iio_evgen_dev);
> +}
> +module_init(iio_dummy_evgen_init);
> +
> +static __exit void iio_dummy_evgen_exit(void)
> +{
> +	device_unregister(&iio_evgen_dev);
> +}
> +module_exit(iio_dummy_evgen_exit);
> +
> +MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
> +MODULE_DESCRIPTION("IIO dummy driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h
> new file mode 100644
> index 0000000..d8845e2
> --- /dev/null
> +++ b/drivers/staging/iio/iio_dummy_evgen.h
> @@ -0,0 +1,2 @@
> +int iio_dummy_evgen_get_irq(void);
> +int iio_dummy_evgen_release_irq(int irq);
> diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
> index 463043e..d16c6a8 100644
> --- a/drivers/staging/iio/iio_simple_dummy.c
> +++ b/drivers/staging/iio/iio_simple_dummy.c
> @@ -20,6 +20,8 @@
>  #include <linux/moduleparam.h>
>  
>  #include "iio.h"
> +#include "sysfs.h"
> +#include "iio_simple_dummy.h"
>  
>  /*
>   * A few elements needed to fake a bus for this driver
> @@ -53,26 +55,6 @@ static const struct iio_dummy_accel_calibscale dummy_scales[] = {
>  	{ 733, 13, 0x9 }, /* 733.00013 */
>  };
>  
> -/**
> - * struct iio_dummy_state - device instance specific state.
> - * @dac_val:			cache for dac value
> - * @single_ended_adc_val:	cache for single ended adc value
> - * @differential_adc_val:	cache for differential adc value
> - * @accel_val:			cache for acceleration value
> - * @accel_calibbias:		cache for acceleration calibbias
> - * @accel_calibscale:		cache for acceleration calibscale
> - * @lock:			lock to ensure state is consistent
> - */
> -struct iio_dummy_state {
> -	int dac_val;
> -	int single_ended_adc_val;
> -	int differential_adc_val[2];
> -	int accel_val;
> -	int accel_calibbias;
> -	const struct iio_dummy_accel_calibscale *accel_calibscale;
> -	struct mutex lock;
> -};
> -
>  /*
>   * iio_dummy_channels - Description of available channels
>   *
> @@ -101,6 +83,14 @@ static struct iio_chan_spec iio_dummy_channels[] = {
>  		 */
>  		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
>  
> +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
> +		/*
> +		 * simple event - triggered when value rises above
> +		 * a threshold
> +		 */
> +		.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> +					 IIO_EV_DIR_RISING),
> +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
>  	},
>  	/* Differential ADC channel in_voltage1-voltage2_raw etc*/
>  	{
> @@ -296,6 +286,12 @@ static const struct iio_info iio_dummy_info = {
>  	.driver_module = THIS_MODULE,
>  	.read_raw = &iio_dummy_read_raw,
>  	.write_raw = &iio_dummy_write_raw,
> +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
> +	.read_event_config = &iio_simple_dummy_read_event_config,
> +	.write_event_config = &iio_simple_dummy_write_event_config,
> +	.read_event_value = &iio_simple_dummy_read_event_value,
> +	.write_event_value = &iio_simple_dummy_write_event_value,
> +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
>  };
>  
>  /**
> @@ -392,11 +388,16 @@ static int __devinit iio_dummy_probe(int index)
>  	/* Specify that device provides sysfs type interfaces */
>  	indio_dev->modes = INDIO_DIRECT_MODE;
>  
> -	ret = iio_device_register(indio_dev);
> +	ret = iio_simple_dummy_events_register(indio_dev);
>  	if (ret < 0)
>  		goto error_free_device;
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0)
> +		goto error_unregister_events;
>  
>  	return 0;
> +error_unregister_events:
> +	iio_simple_dummy_events_unregister(indio_dev);
>  error_free_device:
>  	/* Note free device should only be called, before registration
>  	 * has succeeded. */
> @@ -413,6 +414,7 @@ error_ret:
>   */
>  static int iio_dummy_remove(int index)
>  {
> +	int ret;
>  	/*
>  	 * Get a pointer to the device instance iio_dev structure
>  	 * from the bus subsystem. E.g.
> @@ -423,10 +425,14 @@ static int iio_dummy_remove(int index)
>  
>  	/* Device specific code to power down etc */
>  
> +	ret = iio_simple_dummy_events_unregister(indio_dev);
> +	if (ret)
> +		goto error_ret;
>  	/* Unregister the device and free all structures */
>  	iio_device_unregister(indio_dev);
>  
> -	return 0;
> +error_ret:
> +	return ret;
>  }
>  
>  /**
> diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h
> new file mode 100644
> index 0000000..998fd1f
> --- /dev/null
> +++ b/drivers/staging/iio/iio_simple_dummy.h
> @@ -0,0 +1,81 @@
> +/**
> + * Copyright (c) 2011 Jonathan Cameron
> + *
> + * 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.
> + *
> + * Join together the various functionality of iio_simple_dummy driver
> + */
> +
> +#include <linux/kernel.h>
> +
> +struct iio_dummy_accel_calibscale;
> +
> +/**
> + * struct iio_dummy_state - device instance specific state.
> + * @dac_val:			cache for dac value
> + * @single_ended_adc_val:	cache for single ended adc value
> + * @differential_adc_val:	cache for differential adc value
> + * @accel_val:			cache for acceleration value
> + * @accel_calibbias:		cache for acceleration calibbias
> + * @accel_calibscale:		cache for acceleration calibscale
> + * @lock:			lock to ensure state is consistent
> + * @event_irq:			irq number for event line (faked)
> + * @event_val:			cache for event theshold value
> + * @event_en:			cache of whether event is enabled
> + */
> +struct iio_dummy_state {
> +	int dac_val;
> +	int single_ended_adc_val;
> +	int differential_adc_val[2];
> +	int accel_val;
> +	int accel_calibbias;
> +	const struct iio_dummy_accel_calibscale *accel_calibscale;
> +	struct mutex lock;
> +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
> +	int event_irq;
> +	int event_val;
> +	bool event_en;
> +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
> +};
> +
> +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
> +
> +struct iio_dev;
> +
> +int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
> +				       u64 event_code);
> +
> +int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
> +					u64 event_code,
> +					int state);
> +
> +int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
> +				      u64 event_code,
> +				      int *val);
> +
> +int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
> +				       u64 event_code,
> +				       int val);
> +
> +int iio_simple_dummy_events_register(struct iio_dev *indio_dev);
> +int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev);
> +
> +#else /* Stubs for when events are disabled at compile time */
> +
> +static inline int
> +iio_simple_dummy_events_register(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +};
> +
> +static inline int
> +iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +};
> +
> +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
> +
> +
> diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c
> new file mode 100644
> index 0000000..9f00cff
> --- /dev/null
> +++ b/drivers/staging/iio/iio_simple_dummy_events.c
> @@ -0,0 +1,190 @@
> +/**
> + * Copyright (c) 2011 Jonathan Cameron
> + *
> + * 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.
> + *
> + * Event handling elements of industrial I/O reference driver.
> + */
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +
> +#include "iio.h"
> +#include "sysfs.h"
> +#include "iio_simple_dummy.h"
> +
> +/* Evgen 'fakes' interrupt events for this example */
> +#include "iio_dummy_evgen.h"
> +
> +/**
> + * iio_simple_dummy_read_event_config() - is event enabled?
> + * @indio_dev: the device instance data
> + * @event_code: event code of the event being queried
> + *
> + * This function would normally query the relevant registers or a cache to
> + * discover if the event generation is enabled on the device.
> + */
> +int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
> +				       u64 event_code)
> +{
> +	struct iio_dummy_state *st = iio_priv(indio_dev);
> +
> +	return st->event_en;
> +}
> +
> +/**
> + * iio_simple_dummy_write_event_config() - set whether event is enabled
> + * @indio_dev: the device instance data
> + * @event_code: event code of event being enabled/disabled
> + * @state: whether to enable or disable the device.
> + *
> + * This function would normally set the relevant registers on the devices
> + * so that it generates the specified event. Here it just sets up a cached
> + * value.
> + */
> +int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
> +					u64 event_code,
> +					int state)
> +{
> +	struct iio_dummy_state *st = iio_priv(indio_dev);
> +
> +	/*
> +	 *  Deliberately over the top code splitting to illustrate
> +	 * how this is done when multiple events exist.
> +	 */
> +	switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
> +	case IIO_VOLTAGE:
> +		switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) {
> +		case IIO_EV_TYPE_THRESH:
> +			if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
> +			    IIO_EV_DIR_RISING)
> +				st->event_en = state;
> +			else
> +				return -EINVAL;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iio_simple_dummy_read_event_value() - get value associated with event
> + * @indio_dev: device instance specific data
> + * @event_code: event code for the event whose value is being queried
> + * @val: value for the event code.
> + *
> + * Many devices provide a large set of events of which only a subset may
> + * be enabled at a time, with value registers whose meaning changes depending
> + * on the event enabled. This often means that the driver must cache the values
> + * associated with each possible events so that the right value is in place when
> + * the enabled event is changed.
> + */
> +int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
> +				      u64 event_code,
> +				      int *val)
> +{
> +	struct iio_dummy_state *st = iio_priv(indio_dev);
> +
> +	*val = st->event_val;
> +
> +	return 0;
> +}
> +
> +/**
> + * iio_simple_dummy_write_event_value() - set value associate with event
> + * @indio_dev: device instance specific data
> + * @event_code: event code for the event whose value is being set
> + * @val: the value to be set.
> + */
> +int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
> +				       u64 event_code,
> +				       int val)
> +{
> +	struct iio_dummy_state *st = iio_priv(indio_dev);
> +
> +	st->event_val = val;
> +
> +	return 0;
> +}
> +
> +/**
> + * iio_simple_dummy_event_handler() - identify and pass on event
> + * @irq: irq of event line
> + * @private: pointer to device instance state.
> + *
> + * This handler is responsible for querying the device to find out what
> + * event occured and for then pushing that event towards userspace.
> + * Here only one event occurs so we push that directly on with locally
> + * grabbed timestamp.
> + */
> +static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	iio_push_event(indio_dev,
> +		       IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
> +				      IIO_EV_DIR_RISING,
> +				      IIO_EV_TYPE_THRESH, 0, 0, 0),
> +		       iio_get_time_ns());
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * iio_simple_dummy_events_register() - setup interrupt handling for events
> + * @indio_dev: device instance data
> + *
> + * This function requests the threaded interrupt to handle the events.
> + * Normally the irq is a hardware interrupt and the number comes
> + * from board configuration files.  Here we get it from a companion
> + * module that fakes the interrupt for us. Note that module in
> + * no way forms part of this example. Just assume that events magically
> + * appear via the provided interrupt.
> + */
> +int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
> +{
> +	struct iio_dummy_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	/* Fire up event source - normally not present */
> +	st->event_irq = iio_dummy_evgen_get_irq();
> +	if (st->event_irq < 0) {
> +		ret = st->event_irq;
> +		goto error_ret;
> +	}
> +	ret = request_threaded_irq(st->event_irq,
> +				   NULL,
> +				   &iio_simple_dummy_event_handler,
> +				   IRQF_ONESHOT,
> +				   "iio_simple_event",
> +				   indio_dev);
> +	if (ret < 0)
> +		goto error_free_evgen;
> +	return 0;
> +
> +error_free_evgen:
> +	iio_dummy_evgen_release_irq(st->event_irq);
> +error_ret:
> +	return ret;
> +}
> +
> +/**
> + * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove
> + * @indio_dev: device instance data
> + */
> +int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
> +{
> +	struct iio_dummy_state *st = iio_priv(indio_dev);
> +
> +	free_irq(st->event_irq, indio_dev);
> +	/* Not part of normal driver */
> +	iio_dummy_evgen_release_irq(st->event_irq);
> +
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 0/3] staging:iio:dummy (example) driver.
@ 2011-10-14 15:34 Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 1/3] staging:iio:Documentation Simple dummy driver to explain the basics Jonathan Cameron
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Jonathan Cameron @ 2011-10-14 15:34 UTC (permalink / raw)
  To: greg; +Cc: linux-iio, Jonathan Cameron

Dependent on: staging:iio: two fixes (patch 1 only).

Hi Greg,

This driver is meant to mainly act as documentation although it is useful
in its own right for testing interfaces etc on devices with no hardware.

This version is slightly modified from last posting (fixed the freeing
of struct iio_dev as with all other drivers).

Some general positive feedback and seeing as it can't actually break
any real hardware seems sensible to get it in place asap.  Once merged
I'll clear out a few of the documentation files that it supercedes.

It's an odd category where only newbies are interested enough to read
the code and none of them are confident enough to sign off on it.
Oh well.

There may be a better way of faking the interupts than is done here, but
attempts to extract suggestions from Thomas Gleixner + wider world
failed.

Thanks,

Jonathan

Previous descriptions:

Hi All,

This has v2 of the dummy driver introduction to start with,
main changes are responses to Manuels review and a kernel doc
fixes (as I remembered the format wrong whilst writing the
original code - oops).

The second patch is somewhat evil in that it uses an additional
helper module purely to fake event 'interupts'.  This is the best
option I have come up with for how to write a 'standard' looking
driver that will work without hardware.  It's hacky, but I don't
really want to spend much time on it if it will simply work.

The code in the actual dummy driver is pretty simple and standard.
Note I have glossed over having a top half to the interrupt as
the evgen driver can't run one as currently stands.  We talked
a while ago about a workaround for that in the core, but nothing
is in place as yet.

The third patch puts basic example buffered support in place.
I haven't provided a trigger because that would require either
extending the nasty evgen module, or writing another.

Whilst it is decidely 'unusual' we do have a hardware free trigger
in the iio-trig-sysfs.  I'd not advise people look at that one
to get an idea of how a normal one works though.

So at the end of this, the weak point is the lack of an example
trigger, but those vary so much anyway I inclined to tell people
to look for a driver that happens to do roughly what their hardware
needs and copy that!

What do people think?

I propose to drop the 3 files I moaned about the other day from
the documentation directory in favour of a short description
of what this driver does but only once this is in place.

Thanks,

Jonathan

p.s. found a few bugs in the test prog, so I'll send a series
out cleaning those up soonish.


Jonathan Cameron (3):
  staging:iio:Documentation Simple dummy driver to explain the basics
  staging:iio:dummy Add event support + fake event generator
  staging:iio:dummy Add buffered reading support

 drivers/staging/iio/Kconfig                   |   27 ++
 drivers/staging/iio/Makefile                  |    7 +
 drivers/staging/iio/iio_dummy_evgen.c         |  217 ++++++++++
 drivers/staging/iio/iio_dummy_evgen.h         |    2 +
 drivers/staging/iio/iio_simple_dummy.c        |  545 +++++++++++++++++++++++++
 drivers/staging/iio/iio_simple_dummy.h        |  108 +++++
 drivers/staging/iio/iio_simple_dummy_buffer.c |  206 ++++++++++
 drivers/staging/iio/iio_simple_dummy_events.c |  190 +++++++++
 8 files changed, 1302 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/iio_dummy_evgen.c
 create mode 100644 drivers/staging/iio/iio_dummy_evgen.h
 create mode 100644 drivers/staging/iio/iio_simple_dummy.c
 create mode 100644 drivers/staging/iio/iio_simple_dummy.h
 create mode 100644 drivers/staging/iio/iio_simple_dummy_buffer.c
 create mode 100644 drivers/staging/iio/iio_simple_dummy_events.c

-- 
1.7.7


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] staging:iio:Documentation Simple dummy driver to explain the basics
  2011-10-14 15:34 [PATCH 0/3] staging:iio:dummy (example) driver Jonathan Cameron
@ 2011-10-14 15:34 ` Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 2/3] staging:iio:dummy Add event support + fake event generator Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 3/3] staging:iio:dummy Add buffered reading support Jonathan Cameron
  2 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2011-10-14 15:34 UTC (permalink / raw)
  To: greg; +Cc: linux-iio, Jonathan Cameron

The documenation explaining how to go about writing a driver is lagging
horribly, so here is another approach; an actual driver with
lots of explanatory comments.

Note it is currently minimal in that there are no events and no
buffer.  With care they can probably be added in additional files
without messing up the clarity of what we have here.

V2: Addressed some of Manuel Stahl's feedback.

Fixed up kernel doc.
Added more general description.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/staging/iio/Kconfig            |    8 +
 drivers/staging/iio/Makefile           |    2 +
 drivers/staging/iio/iio_simple_dummy.c |  481 ++++++++++++++++++++++++++++++++
 3 files changed, 491 insertions(+), 0 deletions(-)

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index db4a79f..0accc21 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -70,4 +70,12 @@ source "drivers/staging/iio/meter/Kconfig"
 source "drivers/staging/iio/resolver/Kconfig"
 source "drivers/staging/iio/trigger/Kconfig"
 
+config IIO_SIMPLE_DUMMY
+       tristate "An example driver with no hardware requirements"
+       help
+	Driver intended mainly as documentation for how to write
+	a driver. May also be useful for testing userspace code
+	without hardward.
+
+
 endif # IIO
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 788397d..014d8f1 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -10,6 +10,8 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 
+obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_simple_dummy.o
+
 obj-y += accel/
 obj-y += adc/
 obj-y += addac/
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
new file mode 100644
index 0000000..314a4a6
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -0,0 +1,481 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * A reference industrial I/O driver to illustrate the functionality available.
+ *
+ * There are numerous real drivers to illustrate the finer points.
+ * The purpose of this driver is to provide a driver with far more comments
+ * and explanatory notes than any 'real' driver would have.
+ * Anyone starting out writing an IIO driver should first make sure they
+ * understand all of this driver except those bits specifically marked
+ * as being present to allow us to 'fake' the presence of hardware.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include "iio.h"
+
+/*
+ * A few elements needed to fake a bus for this driver
+ * Note instances parmeter controls how many of these
+ * dummy devices are registered.
+ */
+static unsigned instances = 1;
+module_param(instances, int, 0);
+
+/* Pointer array used to fake bus elements */
+static struct iio_dev **iio_dummy_devs;
+
+/* Fake a name for the part number, usually obtained from the id table */
+static const char *iio_dummy_part_number = "iio_dummy_part_no";
+
+/**
+ * struct iio_dummy_accel_calibscale - realworld to register mapping
+ * @val: first value in read_raw - here integer part.
+ * @val2: second value in read_raw etc - here micro part.
+ * @regval: register value - magic device specific numbers.
+ */
+struct iio_dummy_accel_calibscale {
+	int val;
+	int val2;
+	int regval; /* what would be written to hardware */
+};
+
+static const struct iio_dummy_accel_calibscale dummy_scales[] = {
+	{ 0, 100, 0x8 }, /* 0.000100 */
+	{ 0, 133, 0x7 }, /* 0.000133 */
+	{ 733, 13, 0x9 }, /* 733.00013 */
+};
+
+/**
+ * struct iio_dummy_state - device instance specific state.
+ * @dac_val:			cache for dac value
+ * @single_ended_adc_val:	cache for single ended adc value
+ * @differential_adc_val:	cache for differential adc value
+ * @accel_val:			cache for acceleration value
+ * @accel_calibbias:		cache for acceleration calibbias
+ * @accel_calibscale:		cache for acceleration calibscale
+ * @lock:			lock to ensure state is consistent
+ */
+struct iio_dummy_state {
+	int dac_val;
+	int single_ended_adc_val;
+	int differential_adc_val[2];
+	int accel_val;
+	int accel_calibbias;
+	const struct iio_dummy_accel_calibscale *accel_calibscale;
+	struct mutex lock;
+};
+
+/*
+ * iio_dummy_channels - Description of available channels
+ *
+ * This array of structures tells the IIO core about what the device
+ * actually provides for a given channel.
+ */
+static struct iio_chan_spec iio_dummy_channels[] = {
+	/* indexed ADC channel in_voltage0_raw etc */
+	{
+		.type = IIO_VOLTAGE,
+		/* Channel has a numeric index of 0 */
+		.indexed = 1,
+		.channel = 0,
+		/* What other information is available? */
+		.info_mask =
+		/*
+		 * in_voltage0_offset
+		 * Offset for userspace to apply prior to scale
+		 * when converting to standard units (microvolts)
+		 */
+		(1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+		/*
+		 * in_voltage0_scale
+		 * Multipler for userspace to apply post offset
+		 * when converting to standard units (microvolts)
+		 */
+		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+
+	},
+	/* Differential ADC channel in_voltage1-voltage2_raw etc*/
+	{
+		.type = IIO_VOLTAGE,
+		.differential = 1,
+		/*
+		 * Indexing for differential channels uses channel
+		 * for the positive part, channel2 for the negative.
+		 */
+		.indexed = 1,
+		.channel = 1,
+		.channel2 = 2,
+		.info_mask =
+		/*
+		 * in_voltage-voltage_scale
+		 * Shared version of scale - shared by differential
+		 * input channels of type IIO_VOLTAGE.
+		 */
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+	},
+	/* Differential ADC channel in_voltage3-voltage4_raw etc*/
+	{
+		.type = IIO_VOLTAGE,
+		.differential = 1,
+		.indexed = 1,
+		.channel = 3,
+		.channel2 = 4,
+		.info_mask =
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+	},
+	/* DAC channel out_voltage0_raw */
+	{
+		.type = IIO_VOLTAGE,
+		.output = 1,
+		.indexed = 1,
+		.channel = 0,
+	},
+	/*
+	 * 'modified' (i.e. axis specified) acceleration channel
+	 * in_accel_z_raw
+	 */
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		/* Channel 2 is use for modifiers */
+		.channel2 = IIO_MOD_X,
+		.info_mask =
+		/*
+		 * Internal bias correction value. Applied
+		 * by the hardware or driver prior to userspace
+		 * seeing the readings. Typically part of hardware
+		 * calibration.
+		 */
+		(1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE),
+	},
+};
+
+/**
+ * iio_dummy_read_raw() - data read function.
+ * @indio_dev:	the struct iio_dev associated with this device instance
+ * @chan:	the channel whose data is to be read
+ * @val:	first element of returned value (typically INT)
+ * @val2:	second element of returned value (typically MICRO)
+ * @mask:	what we actually want to read. 0 is the channel, everything else
+ *		is as per the info_mask in iio_chan_spec.
+ */
+static int iio_dummy_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val,
+			      int *val2,
+			      long mask)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+	int ret = -EINVAL;
+
+	mutex_lock(&st->lock);
+	switch (mask) {
+	case 0: /* magic value - channel value read */
+		switch (chan->type) {
+		case IIO_VOLTAGE:
+			if (chan->output) {
+				/* Set integer part to cached value */
+				*val = st->dac_val;
+				ret = IIO_VAL_INT;
+			} else if (chan->differential) {
+				if (chan->channel == 1)
+					*val = st->differential_adc_val[0];
+				else
+					*val = st->differential_adc_val[1];
+				ret = IIO_VAL_INT;
+			} else {
+				*val = st->single_ended_adc_val;
+				ret = IIO_VAL_INT;
+			}
+			break;
+		case IIO_ACCEL:
+			*val = st->accel_val;
+			ret = IIO_VAL_INT;
+			break;
+		default:
+			break;
+		}
+		break;
+	case (1 << IIO_CHAN_INFO_OFFSET_SEPARATE):
+		/* only single ended adc -> 7 */
+		*val = 7;
+		ret = IIO_VAL_INT;
+		break;
+	case (1 << IIO_CHAN_INFO_SCALE_SEPARATE):
+		/* only single ended adc -> 0.001333 */
+		*val = 0;
+		*val2 = 1333;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+		/* all differential adc channels -> 0.000001344 */
+		*val = 0;
+		*val2 = 1344;
+		ret = IIO_VAL_INT_PLUS_NANO;
+		break;
+	case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+		/* only the acceleration axis - read from cache */
+		*val = st->accel_calibbias;
+		ret = IIO_VAL_INT;
+		break;
+	case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
+		*val = st->accel_calibscale->val;
+		*val2 = st->accel_calibscale->val2;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+/**
+ * iio_dummy_write_raw() - data write function.
+ * @indio_dev:	the struct iio_dev associated with this device instance
+ * @chan:	the channel whose data is to be read
+ * @val:	first element of returned value (typically INT)
+ * @val2:	second element of returned value (typically MICRO)
+ * @mask:	what we actually want to read. 0 is the channel, everything else
+ *		is as per the info_mask in iio_chan_spec.
+ *
+ * Note that all raw writes are assumed IIO_VAL_INT and info mask elements
+ * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt
+ * in struct iio_info is provided by the driver.
+ */
+static int iio_dummy_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	int i;
+	int ret = 0;
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	switch (mask) {
+	case 0:
+		if (chan->output == 0)
+			return -EINVAL;
+
+		/* Locking not required as writing single value */
+		mutex_lock(&st->lock);
+		st->dac_val = val;
+		mutex_unlock(&st->lock);
+		return 0;
+	case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+		mutex_lock(&st->lock);
+		/* Compare against table - hard matching here */
+		for (i = 0; i < ARRAY_SIZE(dummy_scales); i++)
+			if (val == dummy_scales[i].val &&
+			    val2 == dummy_scales[i].val2)
+				break;
+		if (i == ARRAY_SIZE(dummy_scales))
+			ret = -EINVAL;
+		else
+			st->accel_calibscale = &dummy_scales[i];
+		mutex_unlock(&st->lock);
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * Device type specific information.
+ */
+static const struct iio_info iio_dummy_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &iio_dummy_read_raw,
+	.write_raw = &iio_dummy_write_raw,
+};
+
+/**
+ * iio_dummy_init_device() - device instance specific init
+ * @indio_dev: the iio device structure
+ *
+ * Most drivers have one of these to set up default values,
+ * reset the device to known state etc.
+ */
+static int iio_dummy_init_device(struct iio_dev *indio_dev)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	st->dac_val = 0;
+	st->single_ended_adc_val = 73;
+	st->differential_adc_val[0] = 33;
+	st->differential_adc_val[1] = -34;
+	st->accel_val = 34;
+	st->accel_calibbias = -7;
+	st->accel_calibscale = &dummy_scales[0];
+
+	return 0;
+}
+
+/**
+ * iio_dummy_probe() - device instance probe
+ * @index: an id number for this instance.
+ *
+ * Arguments are bus type specific.
+ * I2C: iio_dummy_probe(struct i2c_client *client,
+ *                      const struct i2c_device_id *id)
+ * SPI: iio_dummy_probe(struct spi_device *spi)
+ */
+static int __devinit iio_dummy_probe(int index)
+{
+	int ret;
+	struct iio_dev *indio_dev;
+	struct iio_dummy_state *st;
+
+	/*
+	 * Allocate an IIO device.
+	 *
+	 * This structure contains all generic state
+	 * information about the device instance.
+	 * It also has a region (accessed by iio_priv()
+	 * for chip specific state information.
+	 */
+	indio_dev = iio_allocate_device(sizeof(*st));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	st = iio_priv(indio_dev);
+	mutex_init(&st->lock);
+
+	iio_dummy_init_device(indio_dev);
+	/*
+	 * With hardware: Set the parent device.
+	 * indio_dev->dev.parent = &spi->dev;
+	 * indio_dev->dev.parent = &client->dev;
+	 */
+
+	 /*
+	 * Make the iio_dev struct available to remove function.
+	 * Bus equivalents
+	 * i2c_set_clientdata(client, indio_dev);
+	 * spi_set_drvdata(spi, indio_dev);
+	 */
+	iio_dummy_devs[index] = indio_dev;
+
+
+	/*
+	 * Set the device name.
+	 *
+	 * This is typically a part number and obtained from the module
+	 * id table.
+	 * e.g. for i2c and spi:
+	 *    indio_dev->name = id->name;
+	 *    indio_dev->name = spi_get_device_id(spi)->name;
+	 */
+	indio_dev->name = iio_dummy_part_number;
+
+	/* Provide description of available channels */
+	indio_dev->channels = iio_dummy_channels;
+	indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels);
+
+	/*
+	 * Provide device type specific interface functions and
+	 * constant data.
+	 */
+	indio_dev->info = &iio_dummy_info;
+
+	/* Specify that device provides sysfs type interfaces */
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto error_free_device;
+
+	return 0;
+error_free_device:
+	/* Note free device should only be called, before registration
+	 * has succeeded. */
+	iio_free_device(indio_dev);
+error_ret:
+	return ret;
+}
+
+/**
+ * iio_dummy_remove() - device instance removal function
+ * @index: device index.
+ *
+ * Parameters follow those of iio_dummy_probe for buses.
+ */
+static int iio_dummy_remove(int index)
+{
+	/*
+	 * Get a pointer to the device instance iio_dev structure
+	 * from the bus subsystem. E.g.
+	 * struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	 * struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	 */
+	struct iio_dev *indio_dev = iio_dummy_devs[index];
+
+	/* Unregister the device */
+	iio_device_unregister(indio_dev);
+
+	/* Device specific code to power down etc */
+
+	/* Free all structures */
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+/**
+ * iio_dummy_init() -  device driver registration
+ *
+ * Varies depending on bus type of the device. As there is no device
+ * here, call probe directly. For information on device registration
+ * i2c:
+ * Documentation/i2c/writing-clients
+ * spi:
+ * Documentation/spi/spi-summary
+ */
+static __init int iio_dummy_init(void)
+{
+	int i, ret;
+	if (instances > 10) {
+		instances = 1;
+		return -EINVAL;
+	}
+	/* Fake a bus */
+	iio_dummy_devs = kzalloc(sizeof(*iio_dummy_devs)*instances, GFP_KERNEL);
+	/* Here we have no actual device so call probe */
+	for (i = 0; i < instances; i++) {
+		ret = iio_dummy_probe(i);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+module_init(iio_dummy_init);
+
+/**
+ * iio_dummy_exit() - device driver removal
+ *
+ * Varies depending on bus type of the device.
+ * As there is no device here, call remove directly.
+ */
+static __exit void iio_dummy_exit(void)
+{
+	int i;
+	for (i = 0; i < instances; i++)
+		iio_dummy_remove(i);
+	kfree(iio_dummy_devs);
+}
+module_exit(iio_dummy_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("IIO dummy driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.7

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] staging:iio:dummy Add event support + fake event generator
  2011-10-14 15:34 [PATCH 0/3] staging:iio:dummy (example) driver Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 1/3] staging:iio:Documentation Simple dummy driver to explain the basics Jonathan Cameron
@ 2011-10-14 15:34 ` Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 3/3] staging:iio:dummy Add buffered reading support Jonathan Cameron
  2 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2011-10-14 15:34 UTC (permalink / raw)
  To: greg; +Cc: linux-iio, Jonathan Cameron

The event generator is not very pretty but does the job and
allows this driver to look a lot more like a normal driver
than it otherwise would.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/staging/iio/Kconfig                   |   20 ++-
 drivers/staging/iio/Makefile                  |    6 +-
 drivers/staging/iio/iio_dummy_evgen.c         |  217 +++++++++++++++++++++++++
 drivers/staging/iio/iio_dummy_evgen.h         |    2 +
 drivers/staging/iio/iio_simple_dummy.c        |   53 ++++---
 drivers/staging/iio/iio_simple_dummy.h        |   81 +++++++++
 drivers/staging/iio/iio_simple_dummy_events.c |  190 ++++++++++++++++++++++
 7 files changed, 543 insertions(+), 26 deletions(-)

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 0accc21..75128a0 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -70,12 +70,26 @@ source "drivers/staging/iio/meter/Kconfig"
 source "drivers/staging/iio/resolver/Kconfig"
 source "drivers/staging/iio/trigger/Kconfig"
 
+config IIO_DUMMY_EVGEN
+       tristate
+
 config IIO_SIMPLE_DUMMY
        tristate "An example driver with no hardware requirements"
+       select IIO_SIMPLE_DUMMY_EVGEN if IIO_SIMPLE_DUMMY_EVENTS
        help
-	Driver intended mainly as documentation for how to write
-	a driver. May also be useful for testing userspace code
-	without hardward.
+	 Driver intended mainly as documentation for how to write
+	 a driver. May also be useful for testing userspace code
+	 without hardward.
+
+if IIO_SIMPLE_DUMMY
+
+config IIO_SIMPLE_DUMMY_EVENTS
+       boolean "Event generation support"
+       select IIO_DUMMY_EVGEN
+       help
+         Add some dummy events to the simple dummy driver.
+
 
+endif # IIO_SIMPLE_DUMMY
 
 endif # IIO
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 014d8f1..75aacfd 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -10,7 +10,11 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 
-obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_simple_dummy.o
+obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
+iio_dummy-y := iio_simple_dummy.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
+
+obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
 
 obj-y += accel/
 obj-y += adc/
diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c
new file mode 100644
index 0000000..da657d1
--- /dev/null
+++ b/drivers/staging/iio/iio_dummy_evgen.c
@@ -0,0 +1,217 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Companion module to the iio simple dummy example driver.
+ * The purpose of this is to generate 'fake' event interrupts thus
+ * allowing that driver's code to be as close as possible to that of
+ * a normal driver talking to hardware.  The approach used here
+ * is not intended to be general and just happens to work for this
+ * particular use case.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+
+#include "iio_dummy_evgen.h"
+#include "iio.h"
+#include "sysfs.h"
+
+/* Fiddly bit of faking and irq without hardware */
+#define IIO_EVENTGEN_NO 10
+/**
+ * struct iio_dummy_evgen - evgen state
+ * @chip: irq chip we are faking
+ * @base: base of irq range
+ * @enabled: mask of which irqs are enabled
+ * @inuse: mask of which irqs actually have anyone connected
+ * @lock: protect the evgen state
+ */
+struct iio_dummy_eventgen {
+	struct irq_chip chip;
+	int base;
+	bool enabled[IIO_EVENTGEN_NO];
+	bool inuse[IIO_EVENTGEN_NO];
+	struct mutex lock;
+};
+
+/* We can only ever have one instance of this 'device' */
+static struct iio_dummy_eventgen *iio_evgen;
+static const char *iio_evgen_name = "iio_dummy_evgen";
+
+static void iio_dummy_event_irqmask(struct irq_data *d)
+{
+	struct irq_chip *chip = irq_data_get_irq_chip(d);
+	struct iio_dummy_eventgen *evgen =
+		container_of(chip, struct iio_dummy_eventgen, chip);
+
+	evgen->enabled[d->irq - evgen->base] = false;
+}
+
+static void iio_dummy_event_irqunmask(struct irq_data *d)
+{
+	struct irq_chip *chip = irq_data_get_irq_chip(d);
+	struct iio_dummy_eventgen *evgen =
+		container_of(chip, struct iio_dummy_eventgen, chip);
+
+	evgen->enabled[d->irq - evgen->base] = true;
+}
+
+static int iio_dummy_evgen_create(void)
+{
+	int ret, i;
+
+	iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
+	if (iio_evgen == NULL)
+		return -ENOMEM;
+
+	iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0);
+	if (iio_evgen->base < 0) {
+		ret = iio_evgen->base;
+		kfree(iio_evgen);
+		return ret;
+	}
+	iio_evgen->chip.name = iio_evgen_name;
+	iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask;
+	iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask;
+	for (i = 0; i < IIO_EVENTGEN_NO; i++) {
+		irq_set_chip(iio_evgen->base + i, &iio_evgen->chip);
+		irq_set_handler(iio_evgen->base + i, &handle_simple_irq);
+		irq_modify_status(iio_evgen->base + i,
+				  IRQ_NOREQUEST | IRQ_NOAUTOEN,
+				  IRQ_NOPROBE);
+	}
+	mutex_init(&iio_evgen->lock);
+	return 0;
+}
+
+/**
+ * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
+ *
+ * This function will give a free allocated irq to a client device.
+ * That irq can then be caused to 'fire' by using the associated sysfs file.
+ */
+int iio_dummy_evgen_get_irq(void)
+{
+	int i, ret = 0;
+	mutex_lock(&iio_evgen->lock);
+	for (i = 0; i < IIO_EVENTGEN_NO; i++)
+		if (iio_evgen->inuse[i] == false) {
+			ret = iio_evgen->base + i;
+			iio_evgen->inuse[i] = true;
+			break;
+		}
+	mutex_unlock(&iio_evgen->lock);
+	if (i == IIO_EVENTGEN_NO)
+		return -ENOMEM;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
+
+/**
+ * iio_dummy_evgen_release_irq() - give the irq back.
+ * @irq: irq being returned to the pool
+ *
+ * Used by client driver instances to give the irqs back when they disconnect
+ */
+int iio_dummy_evgen_release_irq(int irq)
+{
+	mutex_lock(&iio_evgen->lock);
+	iio_evgen->inuse[irq - iio_evgen->base] = false;
+	mutex_unlock(&iio_evgen->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
+
+static void iio_dummy_evgen_free(void)
+{
+	irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO);
+	kfree(iio_evgen);
+}
+
+static void iio_evgen_release(struct device *dev)
+{
+	iio_dummy_evgen_free();
+}
+
+static ssize_t iio_evgen_poke(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf,
+			      size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	if (iio_evgen->enabled[this_attr->address])
+		handle_nested_irq(iio_evgen->base + this_attr->address);
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
+static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
+static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
+static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
+static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
+static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
+static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
+static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
+static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
+static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
+
+static struct attribute *iio_evgen_attrs[] = {
+	&iio_dev_attr_poke_ev0.dev_attr.attr,
+	&iio_dev_attr_poke_ev1.dev_attr.attr,
+	&iio_dev_attr_poke_ev2.dev_attr.attr,
+	&iio_dev_attr_poke_ev3.dev_attr.attr,
+	&iio_dev_attr_poke_ev4.dev_attr.attr,
+	&iio_dev_attr_poke_ev5.dev_attr.attr,
+	&iio_dev_attr_poke_ev6.dev_attr.attr,
+	&iio_dev_attr_poke_ev7.dev_attr.attr,
+	&iio_dev_attr_poke_ev8.dev_attr.attr,
+	&iio_dev_attr_poke_ev9.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group iio_evgen_group = {
+	.attrs = iio_evgen_attrs,
+};
+
+static const struct attribute_group *iio_evgen_groups[] = {
+	&iio_evgen_group,
+	NULL
+};
+
+static struct device iio_evgen_dev = {
+	.bus = &iio_bus_type,
+	.groups = iio_evgen_groups,
+	.release = &iio_evgen_release,
+};
+static __init int iio_dummy_evgen_init(void)
+{
+	int ret = iio_dummy_evgen_create();
+	if (ret < 0)
+		return ret;
+	device_initialize(&iio_evgen_dev);
+	dev_set_name(&iio_evgen_dev, "iio_evgen");
+	return device_add(&iio_evgen_dev);
+}
+module_init(iio_dummy_evgen_init);
+
+static __exit void iio_dummy_evgen_exit(void)
+{
+	device_unregister(&iio_evgen_dev);
+}
+module_exit(iio_dummy_evgen_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("IIO dummy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h
new file mode 100644
index 0000000..d8845e2
--- /dev/null
+++ b/drivers/staging/iio/iio_dummy_evgen.h
@@ -0,0 +1,2 @@
+int iio_dummy_evgen_get_irq(void);
+int iio_dummy_evgen_release_irq(int irq);
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
index 314a4a6..c5119a0 100644
--- a/drivers/staging/iio/iio_simple_dummy.c
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -20,6 +20,8 @@
 #include <linux/moduleparam.h>
 
 #include "iio.h"
+#include "sysfs.h"
+#include "iio_simple_dummy.h"
 
 /*
  * A few elements needed to fake a bus for this driver
@@ -53,26 +55,6 @@ static const struct iio_dummy_accel_calibscale dummy_scales[] = {
 	{ 733, 13, 0x9 }, /* 733.00013 */
 };
 
-/**
- * struct iio_dummy_state - device instance specific state.
- * @dac_val:			cache for dac value
- * @single_ended_adc_val:	cache for single ended adc value
- * @differential_adc_val:	cache for differential adc value
- * @accel_val:			cache for acceleration value
- * @accel_calibbias:		cache for acceleration calibbias
- * @accel_calibscale:		cache for acceleration calibscale
- * @lock:			lock to ensure state is consistent
- */
-struct iio_dummy_state {
-	int dac_val;
-	int single_ended_adc_val;
-	int differential_adc_val[2];
-	int accel_val;
-	int accel_calibbias;
-	const struct iio_dummy_accel_calibscale *accel_calibscale;
-	struct mutex lock;
-};
-
 /*
  * iio_dummy_channels - Description of available channels
  *
@@ -101,6 +83,14 @@ static struct iio_chan_spec iio_dummy_channels[] = {
 		 */
 		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
 
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+		/*
+		 * simple event - triggered when value rises above
+		 * a threshold
+		 */
+		.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH,
+					 IIO_EV_DIR_RISING),
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
 	},
 	/* Differential ADC channel in_voltage1-voltage2_raw etc*/
 	{
@@ -296,6 +286,12 @@ static const struct iio_info iio_dummy_info = {
 	.driver_module = THIS_MODULE,
 	.read_raw = &iio_dummy_read_raw,
 	.write_raw = &iio_dummy_write_raw,
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+	.read_event_config = &iio_simple_dummy_read_event_config,
+	.write_event_config = &iio_simple_dummy_write_event_config,
+	.read_event_value = &iio_simple_dummy_read_event_value,
+	.write_event_value = &iio_simple_dummy_write_event_value,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
 };
 
 /**
@@ -392,11 +388,16 @@ static int __devinit iio_dummy_probe(int index)
 	/* Specify that device provides sysfs type interfaces */
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
-	ret = iio_device_register(indio_dev);
+	ret = iio_simple_dummy_events_register(indio_dev);
 	if (ret < 0)
 		goto error_free_device;
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto error_unregister_events;
 
 	return 0;
+error_unregister_events:
+	iio_simple_dummy_events_unregister(indio_dev);
 error_free_device:
 	/* Note free device should only be called, before registration
 	 * has succeeded. */
@@ -413,6 +414,7 @@ error_ret:
  */
 static int iio_dummy_remove(int index)
 {
+	int ret;
 	/*
 	 * Get a pointer to the device instance iio_dev structure
 	 * from the bus subsystem. E.g.
@@ -421,15 +423,22 @@ static int iio_dummy_remove(int index)
 	 */
 	struct iio_dev *indio_dev = iio_dummy_devs[index];
 
+
 	/* Unregister the device */
 	iio_device_unregister(indio_dev);
 
 	/* Device specific code to power down etc */
 
+
+	ret = iio_simple_dummy_events_unregister(indio_dev);
+	if (ret)
+		goto error_ret;
+
 	/* Free all structures */
 	iio_free_device(indio_dev);
 
-	return 0;
+error_ret:
+	return ret;
 }
 
 /**
diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h
new file mode 100644
index 0000000..998fd1f
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy.h
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Join together the various functionality of iio_simple_dummy driver
+ */
+
+#include <linux/kernel.h>
+
+struct iio_dummy_accel_calibscale;
+
+/**
+ * struct iio_dummy_state - device instance specific state.
+ * @dac_val:			cache for dac value
+ * @single_ended_adc_val:	cache for single ended adc value
+ * @differential_adc_val:	cache for differential adc value
+ * @accel_val:			cache for acceleration value
+ * @accel_calibbias:		cache for acceleration calibbias
+ * @accel_calibscale:		cache for acceleration calibscale
+ * @lock:			lock to ensure state is consistent
+ * @event_irq:			irq number for event line (faked)
+ * @event_val:			cache for event theshold value
+ * @event_en:			cache of whether event is enabled
+ */
+struct iio_dummy_state {
+	int dac_val;
+	int single_ended_adc_val;
+	int differential_adc_val[2];
+	int accel_val;
+	int accel_calibbias;
+	const struct iio_dummy_accel_calibscale *accel_calibscale;
+	struct mutex lock;
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+	int event_irq;
+	int event_val;
+	bool event_en;
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+};
+
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+
+struct iio_dev;
+
+int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
+				       u64 event_code);
+
+int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
+					u64 event_code,
+					int state);
+
+int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
+				      u64 event_code,
+				      int *val);
+
+int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
+				       u64 event_code,
+				       int val);
+
+int iio_simple_dummy_events_register(struct iio_dev *indio_dev);
+int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev);
+
+#else /* Stubs for when events are disabled at compile time */
+
+static inline int
+iio_simple_dummy_events_register(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+static inline int
+iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
+
+
diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c
new file mode 100644
index 0000000..9f00cff
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy_events.c
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Event handling elements of industrial I/O reference driver.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "iio_simple_dummy.h"
+
+/* Evgen 'fakes' interrupt events for this example */
+#include "iio_dummy_evgen.h"
+
+/**
+ * iio_simple_dummy_read_event_config() - is event enabled?
+ * @indio_dev: the device instance data
+ * @event_code: event code of the event being queried
+ *
+ * This function would normally query the relevant registers or a cache to
+ * discover if the event generation is enabled on the device.
+ */
+int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
+				       u64 event_code)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	return st->event_en;
+}
+
+/**
+ * iio_simple_dummy_write_event_config() - set whether event is enabled
+ * @indio_dev: the device instance data
+ * @event_code: event code of event being enabled/disabled
+ * @state: whether to enable or disable the device.
+ *
+ * This function would normally set the relevant registers on the devices
+ * so that it generates the specified event. Here it just sets up a cached
+ * value.
+ */
+int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
+					u64 event_code,
+					int state)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	/*
+	 *  Deliberately over the top code splitting to illustrate
+	 * how this is done when multiple events exist.
+	 */
+	switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
+	case IIO_VOLTAGE:
+		switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) {
+		case IIO_EV_TYPE_THRESH:
+			if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
+			    IIO_EV_DIR_RISING)
+				st->event_en = state;
+			else
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * iio_simple_dummy_read_event_value() - get value associated with event
+ * @indio_dev: device instance specific data
+ * @event_code: event code for the event whose value is being queried
+ * @val: value for the event code.
+ *
+ * Many devices provide a large set of events of which only a subset may
+ * be enabled at a time, with value registers whose meaning changes depending
+ * on the event enabled. This often means that the driver must cache the values
+ * associated with each possible events so that the right value is in place when
+ * the enabled event is changed.
+ */
+int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
+				      u64 event_code,
+				      int *val)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	*val = st->event_val;
+
+	return 0;
+}
+
+/**
+ * iio_simple_dummy_write_event_value() - set value associate with event
+ * @indio_dev: device instance specific data
+ * @event_code: event code for the event whose value is being set
+ * @val: the value to be set.
+ */
+int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
+				       u64 event_code,
+				       int val)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	st->event_val = val;
+
+	return 0;
+}
+
+/**
+ * iio_simple_dummy_event_handler() - identify and pass on event
+ * @irq: irq of event line
+ * @private: pointer to device instance state.
+ *
+ * This handler is responsible for querying the device to find out what
+ * event occured and for then pushing that event towards userspace.
+ * Here only one event occurs so we push that directly on with locally
+ * grabbed timestamp.
+ */
+static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	iio_push_event(indio_dev,
+		       IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
+				      IIO_EV_DIR_RISING,
+				      IIO_EV_TYPE_THRESH, 0, 0, 0),
+		       iio_get_time_ns());
+	return IRQ_HANDLED;
+}
+
+/**
+ * iio_simple_dummy_events_register() - setup interrupt handling for events
+ * @indio_dev: device instance data
+ *
+ * This function requests the threaded interrupt to handle the events.
+ * Normally the irq is a hardware interrupt and the number comes
+ * from board configuration files.  Here we get it from a companion
+ * module that fakes the interrupt for us. Note that module in
+ * no way forms part of this example. Just assume that events magically
+ * appear via the provided interrupt.
+ */
+int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+	int ret;
+
+	/* Fire up event source - normally not present */
+	st->event_irq = iio_dummy_evgen_get_irq();
+	if (st->event_irq < 0) {
+		ret = st->event_irq;
+		goto error_ret;
+	}
+	ret = request_threaded_irq(st->event_irq,
+				   NULL,
+				   &iio_simple_dummy_event_handler,
+				   IRQF_ONESHOT,
+				   "iio_simple_event",
+				   indio_dev);
+	if (ret < 0)
+		goto error_free_evgen;
+	return 0;
+
+error_free_evgen:
+	iio_dummy_evgen_release_irq(st->event_irq);
+error_ret:
+	return ret;
+}
+
+/**
+ * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove
+ * @indio_dev: device instance data
+ */
+int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
+{
+	struct iio_dummy_state *st = iio_priv(indio_dev);
+
+	free_irq(st->event_irq, indio_dev);
+	/* Not part of normal driver */
+	iio_dummy_evgen_release_irq(st->event_irq);
+
+	return 0;
+}
-- 
1.7.7


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] staging:iio:dummy Add buffered reading support
  2011-10-14 15:34 [PATCH 0/3] staging:iio:dummy (example) driver Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 1/3] staging:iio:Documentation Simple dummy driver to explain the basics Jonathan Cameron
  2011-10-14 15:34 ` [PATCH 2/3] staging:iio:dummy Add event support + fake event generator Jonathan Cameron
@ 2011-10-14 15:34 ` Jonathan Cameron
  2 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2011-10-14 15:34 UTC (permalink / raw)
  To: greg; +Cc: linux-iio, Jonathan Cameron

Very simple buffered reading.  Did not provide a trigger as
the sysfs trigger already meets that requirement.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/staging/iio/Kconfig                   |    5 +
 drivers/staging/iio/Makefile                  |    1 +
 drivers/staging/iio/iio_simple_dummy.c        |   73 ++++++++-
 drivers/staging/iio/iio_simple_dummy.h        |   27 ++++
 drivers/staging/iio/iio_simple_dummy_buffer.c |  206 +++++++++++++++++++++++++
 5 files changed, 303 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 75128a0..09cf580 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -89,6 +89,11 @@ config IIO_SIMPLE_DUMMY_EVENTS
        help
          Add some dummy events to the simple dummy driver.
 
+config IIO_SIMPLE_DUMMY_BUFFER
+       boolean "Buffered capture support"
+       depends on IIO_KFIFO_BUF
+       help
+         Add buffered data capture to the simple dummy driver.
 
 endif # IIO_SIMPLE_DUMMY
 
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 75aacfd..eaa07b0 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
 iio_dummy-y := iio_simple_dummy.o
 iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o
 
 obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
 
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
index c5119a0..af0c992 100644
--- a/drivers/staging/iio/iio_simple_dummy.c
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -21,6 +21,7 @@
 
 #include "iio.h"
 #include "sysfs.h"
+#include "buffer_generic.h"
 #include "iio_simple_dummy.h"
 
 /*
@@ -82,7 +83,14 @@ static struct iio_chan_spec iio_dummy_channels[] = {
 		 * when converting to standard units (microvolts)
 		 */
 		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
-
+		/* The ordering of elements in the buffer via an enum */
+		.scan_index = voltage0,
+		.scan_type = { /* Description of storage in buffer */
+			.sign = 'u', /* unsigned */
+			.realbits = 13, /* 13 bits */
+			.storagebits = 16, /* 16 bits used for storage */
+			.shift = 0, /* zero shift */
+		},
 #ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
 		/*
 		 * simple event - triggered when value rises above
@@ -110,6 +118,13 @@ static struct iio_chan_spec iio_dummy_channels[] = {
 		 * input channels of type IIO_VOLTAGE.
 		 */
 		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.scan_index = diffvoltage1m2,
+		.scan_type = { /* Description of storage in buffer */
+			.sign = 's', /* signed */
+			.realbits = 12, /* 12 bits */
+			.storagebits = 16, /* 16 bits used for storage */
+			.shift = 0, /* zero shift */
+		},
 	},
 	/* Differential ADC channel in_voltage3-voltage4_raw etc*/
 	{
@@ -120,13 +135,13 @@ static struct iio_chan_spec iio_dummy_channels[] = {
 		.channel2 = 4,
 		.info_mask =
 		(1 << IIO_CHAN_INFO_SCALE_SHARED),
-	},
-	/* DAC channel out_voltage0_raw */
-	{
-		.type = IIO_VOLTAGE,
-		.output = 1,
-		.indexed = 1,
-		.channel = 0,
+		.scan_index = diffvoltage3m4,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 11,
+			.storagebits = 16,
+			.shift = 0,
+		},
 	},
 	/*
 	 * 'modified' (i.e. axis specified) acceleration channel
@@ -145,6 +160,25 @@ static struct iio_chan_spec iio_dummy_channels[] = {
 		 * calibration.
 		 */
 		(1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE),
+		.scan_index = accelx,
+		.scan_type = { /* Description of storage in buffer */
+			.sign = 's', /* signed */
+			.realbits = 16, /* 12 bits */
+			.storagebits = 16, /* 16 bits used for storage */
+			.shift = 0, /* zero shift */
+		},
+	},
+	/*
+	 * Convenience macro for timestamps. 4 is the index in
+	 * the buffer.
+	 */
+	IIO_CHAN_SOFT_TIMESTAMP(4),
+	/* DAC channel out_voltage0_raw */
+	{
+		.type = IIO_VOLTAGE,
+		.output = 1,
+		.indexed = 1,
+		.channel = 0,
 	},
 };
 
@@ -391,11 +425,29 @@ static int __devinit iio_dummy_probe(int index)
 	ret = iio_simple_dummy_events_register(indio_dev);
 	if (ret < 0)
 		goto error_free_device;
-	ret = iio_device_register(indio_dev);
+
+	/* Configure buffered capture support. */
+	ret = iio_simple_dummy_configure_buffer(indio_dev);
 	if (ret < 0)
 		goto error_unregister_events;
 
+	/*
+	 * Register the channels with the buffer, but avoid the output
+	 * channel being registered by reducing the number of channels by 1.
+	 */
+	ret = iio_buffer_register(indio_dev, iio_dummy_channels, 5);
+	if (ret < 0)
+		goto error_unconfigure_buffer;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto error_unregister_buffer;
+
 	return 0;
+error_unregister_buffer:
+	iio_buffer_unregister(indio_dev);
+error_unconfigure_buffer:
+	iio_simple_dummy_unconfigure_buffer(indio_dev);
 error_unregister_events:
 	iio_simple_dummy_events_unregister(indio_dev);
 error_free_device:
@@ -429,6 +481,9 @@ static int iio_dummy_remove(int index)
 
 	/* Device specific code to power down etc */
 
+	/* Buffered capture related cleanup */
+	iio_buffer_unregister(indio_dev);
+	iio_simple_dummy_unconfigure_buffer(indio_dev);
 
 	ret = iio_simple_dummy_events_unregister(indio_dev);
 	if (ret)
diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h
index 998fd1f..53975d9 100644
--- a/drivers/staging/iio/iio_simple_dummy.h
+++ b/drivers/staging/iio/iio_simple_dummy.h
@@ -78,4 +78,31 @@ iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
 
 #endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
 
+/**
+ * enum iio_simple_dummy_scan_elements - scan index enum
+ * @voltage0:		the single ended voltage channel
+ * @diffvoltage1m2:	first differential channel
+ * @diffvoltage3m4:	second differenial channel
+ * @accelx:		acceleration channel
+ *
+ * Enum provides convenient numbering for the scan index.
+ */
+enum iio_simple_dummy_scan_elements {
+	voltage0,
+	diffvoltage1m2,
+	diffvoltage3m4,
+	accelx,
+};
 
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER
+int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev);
+void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev);
+#else
+static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+static inline
+void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
+{};
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */
diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c
new file mode 100644
index 0000000..f0b36d2
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy_buffer.c
@@ -0,0 +1,206 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Buffer handling elements of industrial I/O reference driver.
+ * Uses the kfifo buffer.
+ *
+ * To test without hardware use the sysfs trigger.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/bitmap.h>
+
+#include "iio.h"
+#include "trigger_consumer.h"
+#include "kfifo_buf.h"
+
+#include "iio_simple_dummy.h"
+
+/* Some fake data */
+
+static const s16 fakedata[] = {
+	[voltage0] = 7,
+	[diffvoltage1m2] = -33,
+	[diffvoltage3m4] = -2,
+	[accelx] = 344,
+};
+/**
+ * iio_simple_dummy_trigger_h() - the trigger handler function
+ * @irq: the interrupt number
+ * @p: private data - always a pointer to the poll func.
+ *
+ * This is the guts of buffered capture. On a trigger event occuring,
+ * if the pollfunc is attached then this handler is called as a threaded
+ * interrupt (and hence may sleep). It is responsible for grabbing data
+ * from the device and pushing it into the associated buffer.
+ */
+static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct iio_buffer *buffer = indio_dev->buffer;
+	int len = 0;
+	/*
+	 * The datasize is obtained from the buffer. It was stored when
+	 * the preenable setup function was called.
+	 */
+	size_t datasize = buffer->access->get_bytes_per_datum(buffer);
+	u16 *data = kmalloc(datasize, GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
+
+	if (buffer->scan_count) {
+		/*
+		 * Three common options here:
+		 * hardware scans: certain combinations of channels make
+		 *   up a fast read.  The capture will consist of all of them.
+		 *   Hence we just call the grab data function and fill the
+		 *   buffer without processing.
+		 * sofware scans: can be considered to be random access
+		 *   so efficient reading is just a case of minimal bus
+		 *   transactions.
+		 * software culled hardware scans:
+		 *   occasionally a driver may process the nearest hardware
+		 *   scan to avoid storing elements that are not desired. This
+		 *   is the fidliest option by far.
+		 * Here lets pretend we have random access. And the values are
+		 * in the constant table fakedata.
+		 */
+		int i, j;
+		for (i = 0, j = 0; i < buffer->scan_count; i++) {
+			j = find_next_bit(buffer->scan_mask,
+					  indio_dev->masklength, j + 1);
+			/* random access read form the 'device' */
+			data[i] = fakedata[j];
+			len += 2;
+		}
+	}
+	/* Store a timestampe at an 8 byte boundary */
+	if (buffer->scan_timestamp)
+		*(s64 *)(((phys_addr_t)data + len
+				+ sizeof(s64) - 1) & ~(sizeof(s64) - 1))
+			= iio_get_time_ns();
+	buffer->access->store_to(buffer, (u8 *)data, pf->timestamp);
+
+	kfree(data);
+
+	/*
+	 * Tell the core we are done with this trigger and ready for the
+	 * next one.
+	 */
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = {
+	/*
+	 * iio_sw_buffer_preenable:
+	 * Generic function for equal sized ring elements + 64 bit timestamp
+	 * Assumes that any combination of channels can be enabled.
+	 * Typically replaced to implement restrictions on what combinations
+	 * can be captured (hardware scan modes).
+	 */
+	.preenable = &iio_sw_buffer_preenable,
+	/*
+	 * iio_triggered_buffer_postenable:
+	 * Generic function that simply attaches the pollfunc to the trigger.
+	 * Replace this to mess with hardware state before we attach the
+	 * trigger.
+	 */
+	.postenable = &iio_triggered_buffer_postenable,
+	/*
+	 * iio_triggered_buffer_predisable:
+	 * Generic function that simple detaches the pollfunc from the trigger.
+	 * Replace this to put hardware state back again after the trigger is
+	 * detached but before userspace knows we have disabled the ring.
+	 */
+	.predisable = &iio_triggered_buffer_predisable,
+};
+
+int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct iio_buffer *buffer;
+
+	/* Allocate a buffer to use - here a kfifo */
+	buffer = iio_kfifo_allocate(indio_dev);
+	if (buffer == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	indio_dev->buffer = buffer;
+	/* Tell the core how to access the buffer */
+	buffer->access = &kfifo_access_funcs;
+
+	/* Number of bytes per element */
+	buffer->bpe = 2;
+	/* Enable timestamps by default */
+	buffer->scan_timestamp = true;
+
+	/*
+	 * Tell the core what device type specific functions should
+	 * be run on either side of buffer capture enable / disable.
+	 */
+	buffer->setup_ops = &iio_simple_dummy_buffer_setup_ops;
+	buffer->owner = THIS_MODULE;
+
+	/*
+	 * Configure a polling function.
+	 * When a trigger event with this polling function connected
+	 * occurs, this function is run. Typically this grabs data
+	 * from the device.
+	 *
+	 * NULL for the top half. This is normally implemented only if we
+	 * either want to ping a capture now pin (no sleeping) or grab
+	 * a timestamp as close as possible to a data ready trigger firing.
+	 *
+	 * IRQF_ONESHOT ensures irqs are masked such that only one instance
+	 * of the handler can run at a time.
+	 *
+	 * "iio_simple_dummy_consumer%d" formatting string for the irq 'name'
+	 * as seen under /proc/interrupts. Remaining parameters as per printk.
+	 */
+	indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
+						 &iio_simple_dummy_trigger_h,
+						 IRQF_ONESHOT,
+						 indio_dev,
+						 "iio_simple_dummy_consumer%d",
+						 indio_dev->id);
+
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_free_buffer;
+	}
+
+	/*
+	 * Notify the core that this device is capable of buffered capture
+	 * driven by a trigger.
+	 */
+	indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+	return 0;
+
+error_free_buffer:
+	iio_kfifo_free(indio_dev->buffer);
+error_ret:
+	return ret;
+
+}
+
+/**
+ * iio_simple_dummy_unconfigure_buffer() - release buffer resources
+ * @indo_dev: device instance state
+ */
+void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
+{
+	iio_dealloc_pollfunc(indio_dev->pollfunc);
+	iio_kfifo_free(indio_dev->buffer);
+}
-- 
1.7.7

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2011-10-14 15:34 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-10-14 15:34 [PATCH 0/3] staging:iio:dummy (example) driver Jonathan Cameron
2011-10-14 15:34 ` [PATCH 1/3] staging:iio:Documentation Simple dummy driver to explain the basics Jonathan Cameron
2011-10-14 15:34 ` [PATCH 2/3] staging:iio:dummy Add event support + fake event generator Jonathan Cameron
2011-10-14 15:34 ` [PATCH 3/3] staging:iio:dummy Add buffered reading support Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2011-09-20 15:58 [PATCH 0/3 V2] staging:iio:Documentation via dummy driver Jonathan Cameron
2011-09-20 15:58 ` [PATCH 2/3] staging:iio:dummy Add event support + fake event generator Jonathan Cameron
2011-09-21  9:59   ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).