Skip to contentSkip to conversation

IndustrialIO subsystem (ADCs, accelerometers etc)

PATCH In review Jonathan Cameron 11 participants 36 messages Jul 23, 2008 – Jul 25, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover IndustrialIO subsystem (ADCs, accelerometers etc)

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

Key features of the subsystem include:

* Provision of sysfs access for direct reading from devices (similar to hwmon but without the buffering / update rate restrictions)

* Provision of chrdevs through which events may be passed to userspace in a similar fashion to the input subsystem. These events may be anything from hardware thresholds set on the sensor itself to sw / hw ring buffer event notifications (50% full etc).

* Provision of access via chrdevs to hardware ring buffers on devices that provide them.

* Software ring buffer support to allow semi regular capture of data form the device. Typically this will be driven from either datardy events, or from a periodic timer interrupt (to this end a very simple wrapper for periodic RTC's is included. This will move to more generic timer interfaces as and when they become available. For now available rtc's must be registered with the subsystem via the industrialio_register_ptimer function form within a board init.

* A set of sample drivers illustrating the main 'classes' of device. By classes I really mean devices that are interfaced with in a similar way.

The subsystem is now in a functional state with a small set of drivers:

Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
- Uses a periodic timer to provide ring buffer mode.
- All reads form these devices are scan modes so direct single element access is not provided.
- Monitor mode on max1363 is not yet supported (need to do a bit debugging of the board I have so as to be able to test this).

ST LIS3L02DQ - SPI accelerometer.
- Uses a datardy interrupt to driver a software ring buffer.
- Most functionality of this device is supported.

VTI SCA3000 (tested with an e05)
- Hardware ring buffer.

More drivers in preparation.

Next focus will be on cleaning up / implementing a more generic timer framework and allowing the system to partly run if not all dependencies are met (particularly availability of timers).

An initial set of patches will be attached to this thread shortly.

--
Jonathan Cameron

#1 lore raw

PATCH 1/4 Industrialio Core

From: Jonathan Cameron <[email protected]>

Industrialio subsystem core patch. This subsystem is intended to support the use of (initially) sensors within linux for the purposes of data capture and its use within control applications. The intention is to provide consistent interfaces (where it makes sense) with device control occuring through sysfs interfaces and provision of events to userspace via chrdevs. Currently software ring buffers are available if the sensor provides a data ready signal or a periodic rtc is available (and registered with the subsystem in board init code).

---
The periodic timer code is a temporary stop gap until a more generic subsystem becomes available.

The intention of publishing these patches is to generate feedback both at the high level of suggestions / comments on the general approach taken by the subsystem as a whole and at the low level of implementation details.

Areas that in my view need attention are the software ring buffer (particularly careful analysis of corner cases and efficiency of it as a storage method). Although none of the current drivers is capable of filling it in interrupt context, I can envision some hardware may need to and this will clearly require some changes. The overall layout of the interfaces (and indeed the code) needs some work, particularly with a view to cutting down the dependancies if a given driver doesn't need all of the systems functionality.

Additional test drivers will obviously assist in working out many of these issues and I hope to add several more over the comming weeks.

My sincerest thanks goes to anyone who takes the time to read through or test this patch set.

drivers/Kconfig                                       |    3
drivers/Makefile                                      |    1
drivers/industrialio/Kconfig                          |   19
drivers/industrialio/Makefile                         |    7
drivers/industrialio/industrialio-core.c              |  787 ++++++++++++++++++
drivers/industrialio/industrialio-ring.c              |  770 +++++++++++++++++
drivers/industrialio/industrialio-rtc.c               |  134 +++
drivers/industrialio/industrialio_ptimer_board_info.c |   44 +
include/linux/industrialio.h                          |  374 ++++++++
include/linux/industrialio_ptimer.h                   |   18
include/linux/industrialio_sysfs.h                    |  274 ++++++
11 files changed, 2431 insertions(+)

------ a/drivers/Kconfig 2008-07-13 22:51:29.000000000 +0100
+++ b/drivers/Kconfig 2008-07-14 17:26:34.000000000 +0100
@@ -101,4 +101,7 @@ source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"

source "drivers/xen/Kconfig"
+
+source "drivers/industrialio/Kconfig"
endmenu
+

drivers/Makefile +1 -0
@@ -62,6 +62,7 @@ obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-y				+= i2c/
+obj-y				+= industrialio/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_POWER_SUPPLY)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
drivers/industrialio/Kconfig +19 -0
@@ -0,0 +1,19 @@
+#
+# Industrial I/O subsytem configuration
+#
+
+menuconfig INDUSTRIALIO
+	tristate "Industrial I/O support"
+	---help---
+	  The industrial IO subsystem provides a unified framework for drivers
+	  for many different types of embedded sensors using a number of
+	  different phyiscal interfaces (i2c, spi etc). See
+	  Documentation/industrialio for more information.
+
+if INDUSTRIALIO
+
+config INDUSTRIALIO_PTIMER_BOARDINFO
+       boolean
+       default y
+
+endif
drivers/industrialio/Makefile +7 -0
@@ -0,0 +1,7 @@
+#
+# Makefile for the industrial I/O core.
+#
+industrialio-objs := industrialio-core.o industrialio-ring.o industrialio-rtc.o
+
+obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
+obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
include/linux/industrialio.h +374 -0

374 lines not on this page · raw message

include/linux/industrialio_ptimer.h +18 -0
@@ -0,0 +1,18 @@
+#ifndef _INDUSTRIALIO_PTIMER_H_
+#define _INDUSTRIALIO_PTIMER_H_
+
+#define IIO_PTIMER_NAME_SIZE 10
+
+
+struct ptimer_info {
+	char name[IIO_PTIMER_NAME_SIZE];
+};
+struct ptimer_info_listel {
+	struct list_head list;
+	bool inuse;
+	struct ptimer_info info;
+};
+
+extern int
+industrialio_register_ptimer(struct ptimer_info const *info, unsigned n);
+#endif
include/linux/industrialio_sysfs.h +274 -0

274 lines not on this page · raw message

drivers/industrialio/industrialio_ptimer_board_info.c +44 -0

44 lines not on this page · raw message

drivers/industrialio/industrialio-core.c +787 -0

787 lines not on this page · raw message

drivers/industrialio/industrialio-rtc.c +134 -0

134 lines not on this page · raw message

drivers/industrialio/industrialio-ring.c +770 -0

770 lines not on this page · raw message

#2 lore raw

Re: [Patch 1/4] Industrialio Core

Hi Jonathan,

Sorry, I didn't comment on the first version. So my comments for this version is below. Note, I only briefly looked in the code, thus the comments are really superficial.

So... this subsystem is _huge_. And so it needs documentation. Yes, the code is well commented, much thanks for this. But the documentation is needed, for example, to answer these simple questions:

- What userspace interface the subsystem provides (i.e. for use with userspace tools).
- What kernel interface the subsystem provides (i.e. for use with in-kernel drivers, for example touchscreens).
- What is iio_ring (as I see it, is one of basic concepts of this subsystem), I'd like to see description of its basic usage.
- What is struct iio_event_data, what is event's "id"? How it is used across subsystem?

There is a lot of questions more. Of course, half the questions I'll answer myself through reading the code... But you really need to describe basic ideas of 2431-LOC-subsystem.

Something like Documentation/gpio.txt would work.

On the subsystem itself. I would recommend you to split the industrialio_{core,rtc,ring,ptimer_board_info} into separate modules (if possible) and into separate patches (for better reviewing).

Few more comments below.

[...]

include/linux/industrialio.h line 87

Quoted 10 lines
include/linux/industrialio.h +10 -0
+
+#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) {	\
+		(ring_buf)->size = _bytes_per_datum;			\
+		(ring_buf)->length = _length;				\
+		(ring_buf)->loopcount = 0;				\
+		(ring_buf)->shared_ev_pointer.ev_p = 0;			\
+		(ring_buf)->shared_ev_pointer.lock =			\
+			__SPIN_LOCK_UNLOCKED((ring_buf)			\
+					     ->shared_ev_pointer->loc);	\
+	}

Is it possible to convert this into function?

Quoted 4 lines
include/linux/industrialio.h +4 -0
+#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) {	\
+		INIT_IIO_RING_BUFFER(&(ring)->buf,			\
+				     _bytes_per_datum,			\
+				     _length);				\

include/linux/industrialio.h line 102

Quoted 10 lines
include/linux/industrialio.h +10 -0
+		(ring)->read_p = 0;					\
+		(ring)->write_p = 0;					\
+		(ring)->last_written_p = 0;				\
+		(ring)->data = kmalloc(_length*(ring)->buf.size,	\
+				       GFP_KERNEL);			\
+		(ring)->use_count = 0;					\
+		(ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
+	}
+
+#define FREE_IIO_SW_RING_BUFFER(ring) kfree((ring)->data)

Ditto.

[...]

Quoted 22 lines
include/linux/industrialio.h +22 -0
+/* Vast majority of this is set by the industrialio subsystem on a
+ * call to iio_device_register. */
+/* TODO Macros to simplify setting the relevant stuff in the driver. */
+struct iio_dev {
+/* generic handling data used by ind io */
+	int id;
+/* device specific data */
+	void *dev_data;
+
+/* Modes the drivers supports */
+	int modes; /* Driver Set */
+	int currentmode;
+/* Direct sysfs related functionality */
+	struct device *sysfs_dev;
+	struct device *dev; /* Driver Set */
+	/* General attributes */
+	const struct attribute_group *attrs;
+
+/* Interrupt handling related */
+	struct module *driver_module;
+	int num_interrupt_lines; /* Driver Set */
+

include/linux/industrialio.h line 255

Quoted 10 lines
include/linux/industrialio.h +10 -0
+	struct iio_interrupt **interrupts;
+
+
+	/* Event control attributes */
+	struct attribute_group *event_attrs;
+	/* The character device related elements */
+	struct iio_event_interface *event_interfaces;
+
+/* Software Ring Buffer
+   - for now assuming only makes sense to have a single ring */

These comments are really hard to parse. Try to turn off syntax highlighting (if you use), and see the result. It is really unreadable.

So, I'd suggest to use kernel doc syntax to describe the fields.

For example, I very like how include/linux/mtd/nand.h is commented. It is in kernel doc, plus explains what fields are internal/driver-specific/e.t.c.

Quoted 27 lines
include/linux/industrialio.h +27 -0
+	int ring_dimension;
+	int ring_bytes_per_datum;
+	int ring_length;
+	struct iio_sw_ring_buffer *ring;
+	struct attribute_group *ring_attrs_group;
+	struct iio_ring_attr *ring_attrs;
+	/* enabling / disabling related functions.
+	 * post / pre refer to relative to the change of current_mode. */
+	int (*ring_preenable)(struct iio_dev *);
+	int (*ring_postenable)(struct iio_dev *);
+	int (*ring_predisable)(struct iio_dev *);
+	int (*ring_postdisable)(struct iio_dev *);
+	void (*ring_poll_func)(void *private_data);
+	struct iio_periodic  *ptimer;
+
+	/* Device state lock.
+	 * Used to prevent simultaneous changes to device state.
+	 * In here rather than modules as some ring buffer changes must occur
+	 * with this locked.*/
+	struct mutex			mlock;
+
+	/* Name used to allow releasing of the relevant ptimer on exit.
+	 * Ideally the ptimers will only be held when the driver is actually
+	 * using them, but for now they have one the whole time they are loaded.
+	 */
+	const char *ptimer_name;
+};

[...]

Quoted 107 lines
include/linux/industrialio_sysfs.h +104 -0
@@ -0,0 +1,274 @@
+/* The industrial I/O core
+ *
+ *Copyright (c) 2008 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.
+ *
+ * General attributes
+ */
+
+#ifndef _INDUSTRIAL_IO_SYSFS_H_
+#define _INDUSTRIAL_IO_SYSFS_H_
+
+#include <linux/industrialio.h>
+
+
+struct iio_event_attr {
+	struct device_attribute dev_attr;
+	int mask;
+	struct iio_event_handler_list *listel;
+};
+
+
+#define to_iio_event_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_event_attr, dev_attr)
+
+
+struct iio_chrdev_minor_attr {
+	struct device_attribute dev_attr;
+	int minor;
+};
+
+void
+__init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
+			   const char *name,
+			   struct module *owner,
+			   int id);
+
+
+#define to_iio_chrdev_minor_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_chrdev_minor_attr, dev_attr);
+
+struct iio_dev_attr {
+	struct device_attribute dev_attr;
+	int address;
+};
+
+
+#define to_iio_dev_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_dev_attr, dev_attr)
+
+/* Some attributes will be hard coded (device dependant) and not require an
+   address, in these cases pass a negative */
+#define IIO_ATTR(_name, _mode, _show, _store, _addr)	\
+	{ .dev_attr = __ATTR(_name, _mode, _show, _store),	\
+	  .address = _addr }
+
+#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr)	\
+	struct iio_dev_attr iio_dev_attr_##_name		\
+	= IIO_ATTR(_name, _mode, _show, _store, _addr)
+
+/* This may get broken down into separate files later */
+
+/* Generic attributes of onetype or another */
+
+/* Revision number for the device. As the form of this varies greatly from
+ * device to device, no particular form is specified. In most cases this will
+ * only be for information to the user, not to effect functionality etc.
+ */
+#define IIO_DEV_ATTR_REV(_show)			\
+	IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0)
+
+/* For devices with internal clocks - and possibly poling later */
+
+#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store)			\
+	IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0)
+
+#define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show)				\
+	IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0)
+
+/* ADC types of attibute */
+
+#define IIO_DEV_ATTR_AVAIL_SCAN_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_scan_modes, S_IRUGO, _show, NULL, 0)
+
+#define IIO_DEV_ATTR_SCAN_MODE(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(scan_mode, _mode, _show, _store, 0)
+
+#define IIO_DEV_ATTR_INPUT(_number, _show)				\
+	IIO_DEVICE_ATTR(in##_number, S_IRUGO, _show, NULL, _number)
+
+#define IIO_DEV_ATTR_SCAN(_show)		\
+	IIO_DEVICE_ATTR(scan, S_IRUGO, _show, NULL, 0);
+/* Accelerometer types of attribute */
+
+#define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \
+	IIO_DEVICE_ATTR(x_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr) \
+	IIO_DEVICE_ATTR(y_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr) \
+	IIO_DEVICE_ATTR(z_offset, _mode, _show, _store, _addr)

include/linux/industrialio_sysfs.h line 114

Quoted 10 lines
include/linux/industrialio_sysfs.h +10 -0
+
+#define IIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(x_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(y_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
+

Why such a generic subsystem should be aware of X/Y/Z? ADC can measure strength of wind, for example. But in fact it measures voltage and/or current. Yes, it is convenient to tell user what exactly chip is supposed to measure, but it is chip (and thus driver) specific, subsystem should only provides means to expose that information, but it should not be aware of what exactly we're measuring. At least that is how I imagine the ADC subsystem.

Thanks,

Signature · 3 lines
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2
#3 lore raw

Re: [spi-devel-general] [Patch 1/4] Industrialio Core

Anton Vorontsov wrote:
Hi Anton,

Quoted 19 lines

Sorry, I didn't comment on the first version. So my comments for this version is below. Note, I only briefly looked in the code, thus the comments are really superficial.

So... this subsystem is _huge_. And so it needs documentation. Yes, the code is well commented, much thanks for this. But the documentation is needed, for example, to answer these simple questions:

- What userspace interface the subsystem provides (i.e. for use with userspace tools).
- What kernel interface the subsystem provides (i.e. for use with in-kernel drivers, for example touchscreens).
- What is iio_ring (as I see it, is one of basic concepts of this subsystem), I'd like to see description of its basic usage.
- What is struct iio_event_data, what is event's "id"? How it is used across subsystem?

Quoted 5 lines

There is a lot of questions more. Of course, half the questions I'll answer myself through reading the code... But you really need to describe basic ideas of 2431-LOC-subsystem.

Something like Documentation/gpio.txt would work.

Agreed, sorry about that - docs are about half written and should probably have finished them off before I posted the patch. I'll try and get a first version cleaned up and posted today. Main issue at the moment is getting example code into a state where it isn't too dependent on the exact board setup I'm testing on.

Quoted 7 lines

On the subsystem itself. I would recommend you to split the industrialio_{core,rtc,ring,ptimer_board_info} into separate modules (if possible) and into separate patches (for better reviewing).

Few more comments below.

[...]

Quoted 12 lines

include/linux/industrialio.h line 87

Quoted 10 lines
include/linux/industrialio.h +10 -0
+
+#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) {	\
+		(ring_buf)->size = _bytes_per_datum;			\
+		(ring_buf)->length = _length;				\
+		(ring_buf)->loopcount = 0;				\
+		(ring_buf)->shared_ev_pointer.ev_p = 0;			\
+		(ring_buf)->shared_ev_pointer.lock =			\
+			__SPIN_LOCK_UNLOCKED((ring_buf)			\
+					     ->shared_ev_pointer->loc);	\
+	}

Is it possible to convert this into function?

Good point. No idea why I did it like that!

Quoted 14 lines

[...]

include/linux/industrialio.h line 255

Quoted 7 lines
include/linux/industrialio.h +7 -0
+	/* Event control attributes */
+	struct attribute_group *event_attrs;
+	/* The character device related elements */
+	struct iio_event_interface *event_interfaces;
+
+/* Software Ring Buffer
+   - for now assuming only makes sense to have a single ring */

These comments are really hard to parse. Try to turn off syntax highlighting (if you use), and see the result. It is really unreadable.

Quoted 5 lines

So, I'd suggest to use kernel doc syntax to describe the fields.

For example, I very like how include/linux/mtd/nand.h is commented. It is in kernel doc, plus explains what fields are internal/driver-specific/e.t.c.

Thanks for the pointer, I'll do that.

Quoted 11 lines

[...]

include/linux/industrialio_sysfs.h line 114

Quoted 2 lines
include/linux/industrialio_sysfs.h +2 -0
+	IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
+

Why such a generic subsystem should be aware of X/Y/Z? ADC can measure strength of wind, for example. But in fact it measures voltage and/or current. Yes, it is convenient to tell user what exactly chip is supposed to measure, but it is chip (and thus driver) specific, subsystem should only provides means to expose that information, but it should not be aware of what exactly we're measuring. At least that is how I imagine the ADC subsystem.

I'd argue this is actually connected to improving the usability of the system.

I would envision that the set of definitions (probably broken out into specific headers for different classes of device) and will grow considerably. If a particular parameter is available in say 2 drivers, or looks like it will be, then it will get moved to the main headers rather than being driver specific.

The basic advantages of doing it this way are concerned with user readability. If we know the device is an accelerometer rather than a general ADC is would be silly to have the user guessing which of the inputs are accelerations rather than say temperature readings. Clearly there are issues with definitions of axes on these devices but that will be something to clear up by clear documentation (pick a standard and stick to it!).

Clearly there are cases, for example where an accelerometer is attached via a generic ADC in which we don't have the inherent ability to identify the inputs from the generic adc driver. Given this is a common setup, there may well be some scope for custom drivers for any common sensor circuits or indeed providing a way specifying this in a board config.

It will be interesting to see how this pans out.

What I also forgot to mention alongside the patches is that each driver should be accompanied by a userspace header file providing conversion function to real world units as appropriate. This is clearly required to interpret the data coming out of the ring buffers (which will always be as raw as possible).

There is also the question of whether the outputs in sysfs of say acceleration should be converted to SI units or left as raw readings. For now I'm inclined to leave them raw as the calibration of such sensors within systems is fairly complex and often done as an offline process. However I'm definitely open to other opinions on this.

Thanks for taking a look and providing feedback.
--
Jonathan Cameron

#4 lore raw

Re: [spi-devel-general] [Patch 1/4] Industrialio Core

In reply to Jonathan Cameron
Quoted 130 lines · Jonathan Cameron

From: Jonathan Cameron <[email protected]>

Industrialio subsystem core patch. This subsystem is intended to support the use of (initially) sensors within linux for the purposes of data capture and its use within control applications. The intention is to provide consistent interfaces (where it makes sense) with device control occuring through sysfs interfaces and provision of events to userspace via chrdevs. Currently software ring buffers are available if the sensor provides a data ready signal or a periodic rtc is available (and registered with the subsystem in board init code).

---
The periodic timer code is a temporary stop gap until a more generic subsystem becomes available.

The intention of publishing these patches is to generate feedback both at the high level of suggestions / comments on the general approach taken by the subsystem as a whole and at the low level of implementation details.

Areas that in my view need attention are the software ring buffer (particularly careful analysis of corner cases and efficiency of it as a storage method). Although none of the current drivers is capable of filling it in interrupt context, I can envision some hardware may need to and this will clearly require some changes. The overall layout of the interfaces (and indeed the code) needs some work, particularly with a view to cutting down the dependancies if a given driver doesn't need all of the systems functionality.

Additional test drivers will obviously assist in working out many of these issues and I hope to add several more over the comming weeks.

My sincerest thanks goes to anyone who takes the time to read through or test this patch set.

drivers/Kconfig                                       |    3
drivers/Makefile                                      |    1
drivers/industrialio/Kconfig                          |   19
drivers/industrialio/Makefile                         |    7
drivers/industrialio/industrialio-core.c              |  787 ++++++++++++++++++
drivers/industrialio/industrialio-ring.c              |  770 +++++++++++++++++
drivers/industrialio/industrialio-rtc.c               |  134 +++
drivers/industrialio/industrialio_ptimer_board_info.c |   44 +
include/linux/industrialio.h                          |  374 ++++++++
include/linux/industrialio_ptimer.h                   |   18
include/linux/industrialio_sysfs.h                    |  274 ++++++
11 files changed, 2431 insertions(+)

------ a/drivers/Kconfig 2008-07-13 22:51:29.000000000 +0100
+++ b/drivers/Kconfig 2008-07-14 17:26:34.000000000 +0100

(quoted diff) +3 -0
@@ -101,4 +101,7 @@ source "drivers/auxdisplay/Kconfig"
 source "drivers/uio/Kconfig"
 
 source "drivers/xen/Kconfig"
+
+source "drivers/industrialio/Kconfig" 
 endmenu
+
drivers/Makefile +1 -0
@@ -62,6 +62,7 @@ obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-y				+= i2c/
+obj-y				+= industrialio/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_POWER_SUPPLY)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
drivers/industrialio/Kconfig +19 -0
@@ -0,0 +1,19 @@
+#
+# Industrial I/O subsytem configuration
+#
+
+menuconfig INDUSTRIALIO
+	tristate "Industrial I/O support"
+	---help---
+	  The industrial IO subsystem provides a unified framework for drivers
+	  for many different types of embedded sensors using a number of
+	  different phyiscal interfaces (i2c, spi etc). See
+	  Documentation/industrialio for more information.
+
+if INDUSTRIALIO
+
+config INDUSTRIALIO_PTIMER_BOARDINFO
+       boolean
+       default y
+
+endif
drivers/industrialio/Makefile +7 -0
@@ -0,0 +1,7 @@
+#
+# Makefile for the industrial I/O core.
+#
+industrialio-objs := industrialio-core.o industrialio-ring.o industrialio-rtc.o
+
+obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
+obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
include/linux/industrialio.h +28 -0
@@ -0,0 +1,374 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _INDUSTRIAL_IO_H_
+#define _INDUSTRIAL_IO_H_
+
+#include <linux/device.h>
+#include <linux/industrialio_sysfs.h>
+
+/* TODO LIST */
+/* Static device specific elements (conversion factors etc)
+   should be exported via sysfs
+   Break up this header - some drivers only want a fraction of this.
+*/
+
+
+/* Event interface flags */
+#define IIO_BUSY_BIT_POS 1
+
+
+struct iio_handler {
+	const struct file_operations *fops;

include/linux/industrialio.h line 38

Quoted 10 lines
include/linux/industrialio.h +10 -0
+	int id;
+	unsigned long flags;
+	void *private;
+};
+
+/* The actual event being pushed ot userspace */
+struct iio_event_data {
+	int id;
+	s64 timestamp;
+};

So is this an header for an event? Where is the data in this, this is confusing... Also, should we have a framework to produce an key/pair data, so that an single event can export multiple values from one event...

ie:

struct event_header {
	unsigned int 	id;
	unsigned int	nr_data;	/* number of event_data after */
	s64		timestamp;
struct event {
	struct event_header	header;
	struct event_data	data[0];
};

include/linux/industrialio.h line 46

Quoted 7 lines
include/linux/industrialio.h +7 -0
+
+struct iio_detected_event_list {
+	struct list_head list;
+	struct iio_event_data ev;
+	/* Part of shared event handling - (typicaly ring buffers) */
+	struct iio_shared_ev_pointer *shared_pointer;
+};

the naming of these structures is rather long.

include/linux/industrialio.h line 55

Quoted 8 lines
include/linux/industrialio.h +8 -0
+/* Requires high resolution timers */
+/*  TODO - provide alternative if not available? */
+static inline s64 iio_get_time_ns(void)
+{
+	struct timespec ts;
+	ktime_get_ts(&ts);
+	return timespec_to_ns(&ts);
+}

do we really need something that accurate? we should at-least have the option to remove the timestamp or choose something of lower accuracy?

Quoted 34 lines
include/linux/industrialio.h +34 -0
+struct iio_dev;
+
+/* Each device has one of these per interrupt */
+struct iio_event_handler_list {
+	struct list_head list;
+	int (*handler)(struct iio_dev *dev_io, int index, s64 timestamp,
+		       int no_test);
+	/* This element may be shared */
+	int refcount;
+};
+
+/* Wraps adding to lists and does reference counting to allowed shared
+ * handlers.
+ */
+int iio_add_event_to_list(struct iio_event_handler_list *list,
+			  struct iio_event_handler_list *el);
+
+int iio_remove_event_from_list(struct iio_event_handler_list *el);
+
+struct iio_sw_ring_buffer;
+struct iio_hw_ring_buffer;
+
+#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) {	\
+		(ring_buf)->size = _bytes_per_datum;			\
+		(ring_buf)->length = _length;				\
+		(ring_buf)->loopcount = 0;				\
+		(ring_buf)->shared_ev_pointer.ev_p = 0;			\
+		(ring_buf)->shared_ev_pointer.lock =			\
+			__SPIN_LOCK_UNLOCKED((ring_buf)			\
+					     ->shared_ev_pointer->loc);	\
+	}
+
+#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) {	\
+		INIT_IIO_RING_BUFFER(&(ring)->buf,			\

include/linux/industrialio.h line 100

Quoted 10 lines
include/linux/industrialio.h +10 -0
+				     _bytes_per_datum,			\
+				     _length);				\
+		(ring)->read_p = 0;					\
+		(ring)->write_p = 0;					\
+		(ring)->last_written_p = 0;				\
+		(ring)->data = kmalloc(_length*(ring)->buf.size,	\
+				       GFP_KERNEL);			\
+		(ring)->use_count = 0;					\
+		(ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
+	}

these should be inlined functions.

Quoted 21 lines
include/linux/industrialio.h +21 -0
+#define FREE_IIO_SW_RING_BUFFER(ring) kfree((ring)->data)
+
+
+
+int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
+		      unsigned char *data,
+		      s64 timestamp);
+
+/* Edge cases :
+   1) data at last_p is no longer valid - requires complete wrap around.
+   To detect, loop count has changed - if only by 1 then problem only
+   if current_lastp is equal to or greater than copy made at start.
+   If we have wrapped an entire int in this time (loopcount) then
+   something very very weird has occured!
+*/
+int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
+			       unsigned char *data);
+
+/* Up to the drivers to mark the ring whenever it must not change size
+ * and unmark when it may without problems */
+void iio_mark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring);

include/linux/industrialio.h line 132

Quoted 10 lines
include/linux/industrialio.h +10 -0
+
+void iio_unmark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring);
+
+int iio_request_sw_ring_buffer(int bytes_per_datum,
+			       int length,
+			       struct iio_sw_ring_buffer **ring,
+			       int id,
+			       struct module *owner,
+			       struct device *dev);
+

how about tying this to a driver, where you already know the owner and the dev?

Quoted 186 lines
include/linux/industrialio.h +186 -0
+int iio_request_update_sw_ring_buffer(struct iio_dev *dev_info, int id);
+
+void iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
+			     struct device *dev);
+
+int iio_request_hw_ring_buffer(int bytes_per_datum,
+			       int length,
+			       struct iio_hw_ring_buffer **ring,
+			       int id,
+			       struct module *owner,
+			       struct device *dev,
+			       const struct file_operations *fops,
+			       void *private);
+
+void iio_free_hw_ring_buffer(struct iio_hw_ring_buffer *ring,
+			     struct device *dev);
+
+/* Device operating modes */
+#define INDIO_DIRECT_MODE		0x01
+#define INDIO_RING_POLLED		0x02
+#define INDIO_RING_DATA_RDY		0x04
+#define INDIO_RING_HARDWARE_BUFFER	0x08
+
+struct iio_event_interface {
+	struct iio_handler			handler;
+	wait_queue_head_t			wait;
+	struct mutex				event_list_lock;
+	struct iio_detected_event_list		det_events;
+	int					max_events;
+	int					current_events;
+	/* Integer id, used to differentiate this one form any others */
+	int					id;
+	struct iio_chrdev_minor_attr		attr;
+	struct module				*owner;
+	void					*private;
+	/* used to store name for associated sysfs file */
+	char					_name[20];
+};
+
+struct iio_shared_ev_pointer {
+	struct iio_detected_event_list	*ev_p;
+	spinlock_t lock;
+};
+
+/* A general ring buffer structure
+ *  Intended to be completely lock free as we always want fills from
+ * the interrupt handler to not have to wait.  This obviously increases
+ * the possible time required to read from the buffer.  */
+struct iio_ring_buffer {
+	/* Number of datums */
+	int			length;
+	/* length of single datum - including timestamp if there */
+	int			size;
+	int			loopcount;
+	/* accessing the ring buffer */
+	char			*access_minor_name;
+	struct iio_chrdev_minor_attr	access_minor_attr;
+	struct iio_handler		access_handler;
+	/* events triggered by the ring buffer */
+	char			*event_minor_name;
+	struct iio_event_interface ev_int;
+	/* a fully shared output event ? wtf?*/
+	struct iio_shared_ev_pointer shared_ev_pointer;
+};
+
+int iio_put_ring_event(struct iio_ring_buffer *ring_buf,
+		       int event_code,
+		       s64 timestamp);
+
+int iio_put_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
+				    int event_code,
+				    s64 timestamp);
+
+struct iio_sw_ring_buffer {
+	struct iio_ring_buffer buf;
+	unsigned char		*data;
+	unsigned char		*read_p;
+	unsigned char		*write_p;
+	unsigned char		*last_written_p;
+	/* used to act as a point at which to signal an event */
+	unsigned char		*half_p;
+	int			use_count;
+	int			update_needed;
+	spinlock_t		use_lock;
+};
+
+struct iio_hw_ring_buffer {
+	struct iio_ring_buffer buf;
+	void *private;
+};
+
+/* Vast majority of this is set by the industrialio subsystem on a
+ * call to iio_device_register. */
+/* TODO Macros to simplify setting the relevant stuff in the driver. */
+struct iio_dev {
+/* generic handling data used by ind io */
+	int id;
+/* device specific data */
+	void *dev_data;
+
+/* Modes the drivers supports */
+	int modes; /* Driver Set */
+	int currentmode;
+/* Direct sysfs related functionality */
+	struct device *sysfs_dev;
+	struct device *dev; /* Driver Set */
+	/* General attributes */
+	const struct attribute_group *attrs;
+
+/* Interrupt handling related */
+	struct module *driver_module;
+	int num_interrupt_lines; /* Driver Set */
+
+	struct iio_interrupt **interrupts;
+
+
+	/* Event control attributes */
+	struct attribute_group *event_attrs;
+	/* The character device related elements */
+	struct iio_event_interface *event_interfaces;
+
+/* Software Ring Buffer
+   - for now assuming only makes sense to have a single ring */
+	int ring_dimension;
+	int ring_bytes_per_datum;
+	int ring_length;
+	struct iio_sw_ring_buffer *ring;
+	struct attribute_group *ring_attrs_group;
+	struct iio_ring_attr *ring_attrs;
+	/* enabling / disabling related functions.
+	 * post / pre refer to relative to the change of current_mode. */
+	int (*ring_preenable)(struct iio_dev *);
+	int (*ring_postenable)(struct iio_dev *);
+	int (*ring_predisable)(struct iio_dev *);
+	int (*ring_postdisable)(struct iio_dev *);
+	void (*ring_poll_func)(void *private_data);
+	struct iio_periodic  *ptimer;
+
+	/* Device state lock.
+	 * Used to prevent simultaneous changes to device state.
+	 * In here rather than modules as some ring buffer changes must occur
+	 * with this locked.*/
+	struct mutex			mlock;
+
+	/* Name used to allow releasing of the relevant ptimer on exit.
+	 * Ideally the ptimers will only be held when the driver is actually
+	 * using them, but for now they have one the whole time they are loaded.
+	 */
+	const char *ptimer_name;
+};
+
+int iio_device_register(struct iio_dev *dev_info);
+void iio_device_unregister(struct iio_dev *dev_info);
+
+/* Wrapper class used to allow easy specification of different line numbers */
+struct iio_interrupt {
+	struct iio_dev *dev_info;
+	int line_number;
+	int irq;
+	struct iio_event_handler_list ev_list;
+};
+
+irqreturn_t iio_interrupt_handler(int irq, void *_int_info);
+
+int iio_register_interrupt_line(unsigned int			irq,
+				struct iio_dev	*dev_info,
+				int				line_number,
+				unsigned long			type,
+				const char			*name);
+
+void iio_unregister_interrupt_line(struct iio_dev *dev_info,
+				   int line_number);
+
+
+/* Used to try inserting an event into the list for userspace reading via
+ * chrdev */
+int iio_put_event(struct iio_dev *dev_info,
+		  int ev_line,
+		  int ev_code,
+		  s64 timestamp);
+
+struct iio_work_cont {
+	struct work_struct ws;
+	struct work_struct ws_nocheck;
+	int address;
+	int mask;

include/linux/industrialio.h line 328

Quoted 10 lines
include/linux/industrialio.h +10 -0
+	void *st;
+};
+#define INIT_IIO_WORK_CONT(cont, _checkfunc, _nocheckfunc, _add, _mask, _st)\
+	do {								\
+	INIT_WORK(&(cont)->ws, _checkfunc);				\
+	INIT_WORK(&(cont)->ws_nocheck, _nocheckfunc);			\
+	(cont)->address = _add;						\
+	(cont)->mask = _mask;						\
+	(cont)->st = _st;						\
+	} while (0)

more nasty macros.

Quoted 2,080 lines
include/linux/industrialio.h +45 -0
+/* Ring buffer related */
+int iio_device_register_sw_ring(struct iio_dev *dev_info, int id);
+void iio_device_unregister_sw_ring(struct iio_dev *dev_info);
+
+int __iio_put_event(struct iio_event_interface *ev_int,
+		int ev_code,
+		s64 timestamp,
+		struct iio_shared_ev_pointer*
+		shared_pointer_p);
+void __iio_change_event(struct iio_detected_event_list *ev,
+			int ev_code,
+			s64 timestamp);
+
+int iio_setup_ev_int(struct iio_event_interface *ev_int,
+		     const char *name,
+		     struct module *owner,
+		     struct device *dev);
+
+void iio_free_ev_int(struct iio_event_interface *ev_int,  struct device *dev);
+
+int iio_allocate_chrdev(struct iio_handler *handler);
+void iio_deallocate_chrdev(struct iio_handler *handler);
+
+ssize_t iio_show_attr_minor(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+/* For now this is on the type of periodic timer used*/
+struct iio_periodic {
+	struct rtc_device *rtc;
+	int frequency;
+	struct rtc_task task;
+};
+
+int iio_ptimer_request_periodic_timer(char *name, struct iio_dev *indio_dev);
+void iio_ptimer_unrequest_periodic_timer(struct iio_dev *indio_dev);
+int iio_ptimer_set_freq(struct iio_periodic *ptimer, unsigned frequency);
+int iio_ptimer_irq_set_state(struct iio_dev *indio_dev, bool state);
+
+/* Board registration is handled by contents of
+ * industrialio_ptimer_board_info.c
+ */
+extern struct mutex industrialio_ptimer_board_lock;
+extern struct list_head industrialio_ptimer_board_info_list;
+#endif /* _INDUSTRIAL_IO_H_ */
include/linux/industrialio_ptimer.h +18 -0
@@ -0,0 +1,18 @@
+#ifndef _INDUSTRIALIO_PTIMER_H_
+#define _INDUSTRIALIO_PTIMER_H_
+
+#define IIO_PTIMER_NAME_SIZE 10
+
+
+struct ptimer_info {
+	char name[IIO_PTIMER_NAME_SIZE];
+};
+struct ptimer_info_listel {
+	struct list_head list;
+	bool inuse;
+	struct ptimer_info info;
+};
+
+extern int
+industrialio_register_ptimer(struct ptimer_info const *info, unsigned n);
+#endif
include/linux/industrialio_sysfs.h +274 -0
@@ -0,0 +1,274 @@
+/* The industrial I/O core
+ *
+ *Copyright (c) 2008 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.
+ *
+ * General attributes
+ */
+
+#ifndef _INDUSTRIAL_IO_SYSFS_H_
+#define _INDUSTRIAL_IO_SYSFS_H_
+
+#include <linux/industrialio.h>
+
+
+struct iio_event_attr {
+	struct device_attribute dev_attr;
+	int mask;
+	struct iio_event_handler_list *listel;
+};
+
+
+#define to_iio_event_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_event_attr, dev_attr)
+
+
+struct iio_chrdev_minor_attr {
+	struct device_attribute dev_attr;
+	int minor;
+};
+
+void
+__init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
+			   const char *name,
+			   struct module *owner,
+			   int id);
+
+
+#define to_iio_chrdev_minor_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_chrdev_minor_attr, dev_attr);
+
+struct iio_dev_attr {
+	struct device_attribute dev_attr;
+	int address;
+};
+
+
+#define to_iio_dev_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_dev_attr, dev_attr)
+
+/* Some attributes will be hard coded (device dependant) and not require an
+   address, in these cases pass a negative */
+#define IIO_ATTR(_name, _mode, _show, _store, _addr)	\
+	{ .dev_attr = __ATTR(_name, _mode, _show, _store),	\
+	  .address = _addr }
+
+#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr)	\
+	struct iio_dev_attr iio_dev_attr_##_name		\
+	= IIO_ATTR(_name, _mode, _show, _store, _addr)
+
+/* This may get broken down into separate files later */
+
+/* Generic attributes of onetype or another */
+
+/* Revision number for the device. As the form of this varies greatly from
+ * device to device, no particular form is specified. In most cases this will
+ * only be for information to the user, not to effect functionality etc.
+ */
+#define IIO_DEV_ATTR_REV(_show)			\
+	IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0)
+
+/* For devices with internal clocks - and possibly poling later */
+
+#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store)			\
+	IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0)
+
+#define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show)				\
+	IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0)
+
+/* ADC types of attibute */
+
+#define IIO_DEV_ATTR_AVAIL_SCAN_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_scan_modes, S_IRUGO, _show, NULL, 0)
+
+#define IIO_DEV_ATTR_SCAN_MODE(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(scan_mode, _mode, _show, _store, 0)
+
+#define IIO_DEV_ATTR_INPUT(_number, _show)				\
+	IIO_DEVICE_ATTR(in##_number, S_IRUGO, _show, NULL, _number)
+
+#define IIO_DEV_ATTR_SCAN(_show)		\
+	IIO_DEVICE_ATTR(scan, S_IRUGO, _show, NULL, 0);
+/* Accelerometer types of attribute */
+
+#define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \
+	IIO_DEVICE_ATTR(x_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr) \
+	IIO_DEVICE_ATTR(y_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr) \
+	IIO_DEVICE_ATTR(z_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(x_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(y_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
+
+
+/* The actual device readings are always going to be read only */
+#define IIO_DEV_ATTR_ACCEL_X(_show, _addr)			\
+	IIO_DEVICE_ATTR(x, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y(_show, _addr)			\
+	IIO_DEVICE_ATTR(y, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z(_show, _addr)			\
+	IIO_DEVICE_ATTR(z, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_TEMP(_show)					\
+	IIO_DEVICE_ATTR(temp, S_IRUGO, _show, NULL, 0)
+/* Thresholds are somewhat chip dependent - may need quite a few defs here */
+/* For unified thesholds (shared across all directions */
+#define IIO_DEV_ATTR_ACCEL_THRESH(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_X(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh_x, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_Y(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh_y, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_Z(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh_z, _mode, _show, _store, _addr)
+
+/* This is an event attr in some case and a dev in others - FIX*/
+#define IIO_DEV_ATTR_SW_RING_ENABLE(_show, _store)			\
+	IIO_DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
+
+/* Hardware ring buffer related attributes */
+#define IIO_DEV_ATTR_HW_RING_ENABLE(_show, _store)			\
+	IIO_DEVICE_ATTR(hw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
+
+#define IIO_DEV_ATTR_RING_BPS(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ring_bps, _mode, _show, _store, 0)
+
+/* Bits per sample */
+#define IIO_DEV_ATTR_RING_BPS_AVAILABLE(_show)				\
+	IIO_DEVICE_ATTR(ring_bps_available, S_IRUGO, _show, NULL, 0)
+
+/* Events that the device may generate */
+
+#define IIO_EVENT_SH(_name, _handler)					\
+	static struct iio_event_handler_list				\
+	iio_event_##_name = {						\
+		.handler = _handler,					\
+		.refcount = 0,						\
+	};
+#define IIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask)	\
+	static struct iio_event_attr				\
+	iio_event_attr_##_name					\
+	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store),\
+	    .mask = _mask,\
+	    .listel = &_ev_list };
+
+/*FIXME use the above to define this */
+#define IIO_EVENT_ATTR(_name, _show, _store, _mask, _handler)	\
+	static struct iio_event_handler_list				\
+	iio_event_##_name = {				\
+		.handler = _handler,					\
+	};								\
+	static struct							\
+	iio_event_attr						\
+	iio_event_attr_##_name					\
+	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store), \
+	    .mask = _mask,						\
+	    .listel = &iio_event_##_name };		\
+/*FIXME, add line number to the above?*/
+
+/* In most of these cases, this actually corresponds to something with a
+   value attached */
+
+/* For some devices you can select whether all conditions or any condition
+   must be met for interrupt generation */
+#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_DATA_RDY  100
+
+/* Threshold pass events */
+#define IIO_EVENT_ATTR_ACCEL_X_HIGH(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(x_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_ACCEL_X_HIGH 1
+
+/* Shared handler version */
+#define IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(x_high, _evlist, _show, _store, _mask)
+
+
+#define IIO_EVENT_ATTR_ACCEL_Y_HIGH(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(y_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(y_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_HIGH 2
+
+#define IIO_EVENT_ATTR_ACCEL_Z_HIGH(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(z_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(z_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_HIGH 3
+
+#define IIO_EVENT_ATTR_ACCEL_X_LOW(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(x_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_X_LOW_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(x_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_X_LOW 4
+
+#define IIO_EVENT_ATTR_ACCEL_Y_LOW(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(y_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(y_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_LOW 5
+
+#define IIO_EVENT_ATTR_ACCEL_Z_LOW(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(z_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(z_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_LOW 6
+
+#define IIO_EVENT_ATTR_FREE_FALL_DETECT(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(free_fall, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(free_fall, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_FREE_FALL 7
+
+/* These may be software or hardware events depending on type of ring buffer */
+
+#define IIO_EVENT_ATTR_RING_50_FULL(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(ring_50_full, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_RING_50_FULL_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ring_50_full, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_RING_75_FULL_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ring_75_full, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_SW_RING_ENABLE(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(sw_ring_enable, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_RING_50_FULL 100
+#define IIO_EVENT_CODE_RING_75_FULL 101
+#define IIO_EVENT_CODE_RING_100_FULL 102
+/* HOW TO HANDLE COMPOSITE EVENTS? */
+
+#endif /* _INDUSTRIAL_IO_SYSFS_H_ */
drivers/industrialio/industrialio_ptimer_board_info.c +44 -0
@@ -0,0 +1,44 @@
+/* The industrial I/O periodic timer registration code
+ *
+ * Copyright (c) 2008 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/industrialio_ptimer.h>
+
+LIST_HEAD(industrialio_ptimer_board_info_list);
+EXPORT_SYMBOL_GPL(industrialio_ptimer_board_info_list);
+
+DEFINE_MUTEX(industrialio_ptimer_board_lock);
+EXPORT_SYMBOL_GPL(industrialio_ptimer_board_lock);
+
+
+int __init
+industrialio_register_ptimer(struct ptimer_info const *info, unsigned n)
+{
+	int i;
+	struct ptimer_info_listel *pi;
+
+	mutex_lock(&industrialio_ptimer_board_lock);
+	for (i = 0; i < n; i++) {
+		pi = kzalloc(sizeof(*pi), GFP_KERNEL);
+		if (!pi) {
+			mutex_unlock(&industrialio_ptimer_board_lock);
+			return -ENOMEM;
+		}
+		strncpy(pi->info.name, info[i].name, IIO_PTIMER_NAME_SIZE);
+		list_add_tail(&pi->list, &industrialio_ptimer_board_info_list);
+	}
+	mutex_unlock(&industrialio_ptimer_board_lock);
+
+	return 0;
+}
drivers/industrialio/industrialio-core.c +787 -0
@@ -0,0 +1,787 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008 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.
+ *
+ * Based on elements of hwmon and input subsystems.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/kdev_t.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+
+#include <linux/industrialio.h>
+#include <linux/industrialio_ptimer.h>
+
+MODULE_AUTHOR("Jonathan Cameron <[email protected]>");
+MODULE_DESCRIPTION("Industrial I/O core");
+MODULE_LICENSE("GPL");
+
+#define IIO_ID_PREFIX "industrialio"
+#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
+#define IIO_MAJOR 244
+
+/* Integer id  - used to assign each registered device a unique id*/
+static DEFINE_IDR(iio_idr);
+static DEFINE_SPINLOCK(iio_idr_lock);
+
+struct class iio_class = {
+	.name = "industrialio",
+};
+
+/* Struct used to maintain internal state about industrialio.
+ * This will be used to handle the character device accesses
+ * and redirect them to the relevant driver.
+ * Will reduce this to the included table if nothing else comes
+ * up that should go in here!
+ */
+struct __iio_state {
+	/* All initially set to NULL in init */
+	struct iio_handler *fhs[256];
+};
+
+static struct __iio_state iio_state;
+static DEFINE_SPINLOCK(iio_state_lock);
+
+/* Used to escalate shared event.
+ * Currently this is only used with ring buffer events.
+ */
+void __iio_change_event(struct iio_detected_event_list *ev,
+			int ev_code,
+			s64 timestamp)
+{
+	ev->ev.id = ev_code;
+	ev->ev.timestamp = timestamp;
+}
+
+/* Used both in the interrupt line put events and the ring buffer ones */
+ int
+__iio_put_event(struct iio_event_interface *ev_int,
+		int ev_code,
+		s64 timestamp,
+		struct iio_shared_ev_pointer*
+		shared_pointer_p)
+{
+	struct iio_detected_event_list *ev;
+	int ret;
+
+	/* Does anyone care? */
+	if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) {
+		if (ev_int->current_events == ev_int->max_events)
+			return 0;
+		ev = kmalloc(sizeof(struct iio_detected_event_list),
+			     GFP_KERNEL);
+		if (ev == NULL) {
+			ret = -ENOMEM;
+			goto error_ret;
+		}
+		ev->ev.id = ev_code;
+		ev->ev.timestamp = timestamp;
+		if (shared_pointer_p != NULL) {
+			ev->shared_pointer = shared_pointer_p;
+			shared_pointer_p->ev_p = ev;
+		} else
+			ev->shared_pointer = NULL;
+
+		mutex_lock(&ev_int->event_list_lock);
+		list_add_tail(&ev->list, &ev_int->det_events.list);
+		mutex_unlock(&ev_int->event_list_lock);
+
+		ev_int->current_events++;
+		wake_up_interruptible(&ev_int->wait);
+	}
+
+	return 0;
+error_ret:
+	return ret;
+
+}
+
+int iio_put_event(struct iio_dev *dev_info,
+		  int ev_line,
+		  int ev_code,
+		  s64 timestamp)
+{
+	return __iio_put_event(&dev_info->event_interfaces[ev_line],
+			       ev_code, timestamp, NULL);
+}
+EXPORT_SYMBOL(iio_put_event);
+
+/* Confirming the validity of supplied irq is left to drivers.*/
+int iio_register_interrupt_line(unsigned int irq,
+					 struct iio_dev *dev_info,
+					 int line_number,
+					 unsigned long type,
+					 const char *name)
+{
+	int ret;
+
+	dev_info->interrupts[line_number] =
+		kmalloc(sizeof(struct iio_interrupt), GFP_KERNEL);
+	if (dev_info->interrupts[line_number] == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	INIT_LIST_HEAD(&dev_info->interrupts[line_number]->ev_list.list);
+	dev_info->interrupts[line_number]->line_number = line_number;
+	dev_info->interrupts[line_number]->irq = irq;
+	dev_info->interrupts[line_number]->dev_info = dev_info;
+
+	/* Possibly only request on demand?
+	 * Can see this may complicate the handling of interrupts.
+	 * However, with this approach we end up handling lots of
+	 * events no-one cares about.*/
+	ret = request_irq(irq,
+			  &iio_interrupt_handler,
+			  type,
+			  name,
+			  dev_info->interrupts[line_number]);
+	if (ret < 0)
+		goto error_ret;
+
+	return 0;
+
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(iio_register_interrupt_line);
+
+/* Before this runs the interrupt generator must have been disabled */
+void iio_unregister_interrupt_line(struct iio_dev *dev_info,
+					    int line_number)
+{
+	/* make sure the interrupt handlers are all done */
+	flush_scheduled_work();
+	free_irq(dev_info->interrupts[line_number]->irq,
+		 dev_info->interrupts[line_number]);
+	kfree(dev_info->interrupts[line_number]);
+}
+EXPORT_SYMBOL(iio_unregister_interrupt_line);
+
+/* Generic interrupt line interrupt handler */
+irqreturn_t iio_interrupt_handler(int irq, void *_int_info)
+{
+	struct iio_interrupt *int_info = _int_info;
+	struct iio_dev *dev_info = int_info->dev_info;
+	struct iio_event_handler_list *p;
+	s64 time_ns;
+
+	if (list_empty(&int_info->ev_list.list))
+		return IRQ_NONE;
+
+	time_ns = iio_get_time_ns();
+	/* detect single element list*/
+	if (int_info->ev_list.list.next->next == &int_info->ev_list.list) {
+		disable_irq_nosync(irq);
+		p = list_first_entry(&int_info->ev_list.list,
+				     struct iio_event_handler_list,
+				     list);
+		p->handler(dev_info, 1, time_ns, 1);
+	} else
+		list_for_each_entry(p, &int_info->ev_list.list, list) {
+		       disable_irq_nosync(irq);
+		       p->handler(dev_info, 1, time_ns, 0);
+		}
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(iio_interrupt_handler);
+
+int iio_add_event_to_list(struct iio_event_handler_list *list,
+				   struct iio_event_handler_list *el)
+{
+	if (el->refcount == 0)
+		list_add(&list->list, &el->list);
+	el->refcount++;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_add_event_to_list);
+
+int iio_remove_event_from_list(struct iio_event_handler_list
+					*el)
+{
+	el->refcount--;
+	if (el->refcount == 0)
+		list_del_init(&el->list);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_remove_event_from_list);
+
+
+int iio_allocate_chrdev(struct iio_handler *handler)
+{
+	int id;
+
+	spin_lock(iio_state_lock);
+	for (id = 0; id <= 256; id++)
+		if (iio_state.fhs[id] == NULL)
+			break;
+	if (id == 256) {
+		spin_unlock(iio_state_lock);
+		return -ENOMEM;
+	}
+	iio_state.fhs[id] = handler;
+	spin_unlock(iio_state_lock);
+	handler->id = id;
+
+	return 0;
+}
+
+void iio_deallocate_chrdev(struct iio_handler *handler)
+{
+	spin_lock(iio_state_lock);
+	iio_state.fhs[handler->id] = NULL;
+	spin_unlock(iio_state_lock);
+}
+
+/* Upon open, switch in the correct file ops
+ * lifted directly from input subsystem */
+static int iio_open_file(struct inode *inode, struct file *file)
+{
+	struct iio_handler *handler;
+	const struct file_operations *old_fops, *new_fops = NULL;
+	int err;
+
+	/* This lock needed as unlike input we are dynamically allocating
+	 * chrdevs */
+	spin_lock(iio_state_lock);
+	handler = iio_state.fhs[iminor(inode)];
+	spin_unlock(iio_state_lock);
+
+	if (!handler) {
+		fops_put(file->f_op);
+		return -ENODEV;
+	}
+	new_fops = fops_get(handler->fops);
+	if (new_fops == NULL) {
+		fops_put(file->f_op);
+		return -ENODEV;
+	}
+
+	/* cribbed from lp.c */
+	if (test_and_set_bit(IIO_BUSY_BIT_POS, &handler->flags)) {
+		fops_put(file->f_op);
+		return -EBUSY;
+	}
+
+	if (!new_fops->open) {
+		fops_put(new_fops);
+		return -ENODEV;
+	}
+	old_fops = file->f_op;
+	file->f_op = new_fops;
+	/* use the private data pointer in file to give access to device
+	 * specific stuff */
+	file->private_data = handler->private;
+	err = new_fops->open(inode, file);
+
+	if (err) {
+		fops_put(file->f_op);
+		file->f_op = fops_get(old_fops);
+	}
+	fops_put(old_fops);
+
+	return err;
+}
+
+
+/* The main file ops structure. All open calls on the major number will
+ * be handled by this with fops for the actual minor number assigned by
+ * switching function above */
+static const struct file_operations iio_fops = {
+	.owner = THIS_MODULE,
+	.open = iio_open_file,
+};
+
+ssize_t iio_interrupt_read(struct file *filep,
+			   char *buf,
+			   size_t count,
+			   loff_t *f_ps)
+{
+	struct iio_event_interface *ev_int = filep->private_data;
+	struct iio_detected_event_list *el;
+	int ret;
+
+	mutex_lock(&ev_int->event_list_lock);
+	if (list_empty(&ev_int->det_events.list)) {
+		if (filep->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			goto error_mutex_unlock;
+		}
+		mutex_unlock(&ev_int->event_list_lock);
+		/* Blocking on device; waiting for something to be there */
+		ret = wait_event_interruptible(ev_int->wait,
+					       !list_empty(&ev_int
+							   ->det_events.list));
+		if (ret)
+			goto error_ret;
+		/* Single access device so noone else can get the data */
+		mutex_lock(&ev_int->event_list_lock);
+	}
+
+	el = list_first_entry(&ev_int->det_events.list,
+			      struct iio_detected_event_list,
+			      list);
+
+	if (copy_to_user(buf, &(el->ev),
+			 sizeof(struct iio_event_data))) {
+		ret = -EFAULT;
+		goto error_mutex_unlock;
+	}
+
+	list_del(&el->list);
+	ev_int->current_events--;
+	mutex_unlock(&ev_int->event_list_lock);
+
+	spin_lock(el->shared_pointer->lock);
+	if (el->shared_pointer)
+		(el->shared_pointer->ev_p) = NULL;
+	spin_unlock(el->shared_pointer->lock);
+
+	kfree(el);
+
+	return sizeof(struct iio_event_data);
+
+error_mutex_unlock:
+	mutex_unlock(&ev_int->event_list_lock);
+error_ret:
+
+	return ret;
+}
+
+int iio_interrupt_release(struct inode *inode, struct file *filep)
+{
+	struct iio_event_interface *ev_int = filep->private_data;
+
+	module_put(ev_int->owner);
+	clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags);
+
+	return 0;
+}
+
+int iio_interrupt_open(struct inode *inode, struct file *filep)
+{
+	struct iio_event_interface *ev_int = filep->private_data;
+
+	try_module_get(ev_int->owner);
+
+	return 0;
+}
+static const struct file_operations iio_interrupt_fileops = {
+	.read =  iio_interrupt_read,
+	.release = iio_interrupt_release,
+	.open = iio_interrupt_open,
+	.owner = THIS_MODULE,
+};
+
+
+ssize_t iio_show_attr_minor(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	int len;
+
+	struct iio_chrdev_minor_attr *_attr
+		= to_iio_chrdev_minor_attr(attr);
+	len = sprintf(buf, "%d\n", _attr->minor);
+
+	return len;
+}
+
+void __init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
+				const char *name,
+				struct module *owner,
+				int id)
+{
+	minor_attr->dev_attr.attr.name = name;
+	minor_attr->dev_attr.attr.owner = owner;
+	minor_attr->dev_attr.attr.mode = S_IRUGO;
+	minor_attr->minor = id;
+	minor_attr->dev_attr.show = &iio_show_attr_minor;
+}
+
+int iio_setup_ev_int(struct iio_event_interface *ev_int,
+		     const char *name,
+		     struct module *owner,
+		     struct device *dev)
+{
+	int ret;
+
+	mutex_init(&ev_int->event_list_lock);
+	/* discussion point - make this variable? */
+	ev_int->max_events = 10;
+	ev_int->current_events = 0;
+	INIT_LIST_HEAD(&ev_int->det_events.list);
+	init_waitqueue_head(&ev_int->wait);
+	ev_int->handler.fops = &iio_interrupt_fileops;
+	ev_int->handler.private = ev_int;
+	ev_int->handler.flags = 0;
+	ret = iio_allocate_chrdev(&ev_int->handler);
+	if (ret)
+		goto error_ret;
+	__init_iio_chrdev_minor_attr(&ev_int->attr,
+				     (const char *)(name),
+				     owner,
+				     ev_int->handler.id);
+	ret = sysfs_create_file(&dev->kobj, &ev_int->attr.dev_attr.attr);
+	if (ret)
+		goto error_deallocate_chrdev;
+
+	return 0;
+error_deallocate_chrdev:
+	iio_deallocate_chrdev(&ev_int->handler);
+error_ret:
+	return ret;
+}
+
+void iio_free_ev_int(struct iio_event_interface *ev_int, struct device *dev)
+{
+	sysfs_remove_file(&dev->kobj, &ev_int->attr.dev_attr.attr);
+	iio_deallocate_chrdev(&ev_int->handler);
+}
+
+static int __init iio_init(void)
+{
+	int ret;
+
+	memset(iio_state.fhs,
+	       sizeof(struct iio_handler *)*256,
+	       0);
+
+	/* Create sysfs class */
+	ret  = class_register(&iio_class);
+	if (ret < 0) {
+		printk(KERN_ERR
+		       "industrialio.c: could not create sysfs class\n");
+		goto error_nothing;
+	}
+
+	ret = register_chrdev(IIO_MAJOR, "industrialio", &iio_fops);
+	if (ret) {
+		printk(KERN_ERR
+		       "industrialio: unable to register a char major %d",
+		       IIO_MAJOR);
+		goto error_unregister_class;
+	}
+
+	return 0;
+error_unregister_class:
+	class_unregister(&iio_class);
+error_nothing:
+	return ret;
+}
+
+static void __exit iio_exit(void)
+{
+	unregister_chrdev(IIO_MAJOR, "bob");
+	class_unregister(&iio_class);
+}
+
+int iio_device_register_sysfs(struct iio_dev *dev_info)
+{
+	int ret;
+
+	dev_info->sysfs_dev = device_create(&iio_class,
+					    dev_info->dev,
+					    MKDEV(0, 0),
+					    IIO_ID_FORMAT,
+					    dev_info->id);
+
+	if (IS_ERR(dev_info->sysfs_dev)) {
+		/* what would correct error here be?*/
+		ret = -EINVAL;
+		goto error_ret;
+	}
+	/* register attributes */
+	ret = sysfs_create_group(&dev_info->dev->kobj, dev_info->attrs);
+	if (ret) {
+		dev_err(dev_info->dev, "Failed to register sysfs hooks\n");
+		goto error_free_sysfs_device;
+	}
+
+	return 0;
+
+error_free_sysfs_device:
+	device_unregister(dev_info->dev);
+error_ret:
+	return ret;
+}
+
+void iio_device_unregister_sysfs(struct iio_dev *dev_info)
+{
+	sysfs_remove_group(&dev_info->dev->kobj, dev_info->attrs);
+	device_unregister(dev_info->sysfs_dev);
+}
+
+int iio_device_register_id(struct iio_dev *dev_info)
+{
+	int ret;
+
+idr_again:
+	if (unlikely(idr_pre_get(&iio_idr, GFP_KERNEL) == 0))
+		return -ENOMEM;
+
+	spin_lock(&iio_idr_lock);
+	ret = idr_get_new(&iio_idr, NULL, &dev_info->id);
+	spin_unlock(&iio_idr_lock);
+	if (unlikely(ret == -EAGAIN))
+		goto idr_again;
+	else if (unlikely(ret))
+		return ret;
+	dev_info->id = dev_info->id & MAX_ID_MASK;
+
+	return 0;
+}
+void iio_device_unregister_id(struct iio_dev *dev_info)
+{
+	/* Can I use the save id? */
+	int id;
+
+	if (likely(sscanf(dev_info->sysfs_dev->bus_id,
+			  IIO_ID_FORMAT, &id) == 1)) {
+		spin_lock(&iio_idr_lock);
+		idr_remove(&iio_idr, id);
+		spin_unlock(&iio_idr_lock);
+	} else
+		dev_dbg(dev_info->dev->parent,
+			"indio_device_unregister() failed: bad class ID!\n");
+}
+
+int iio_device_register_eventset(struct iio_dev *dev_info)
+{
+	int ret, i, j;
+
+	struct device_attribute *devattr;
+	struct iio_event_attr *indio_devattr;
+
+	if (dev_info->num_interrupt_lines == 0)
+		return 0;
+	dev_info->event_interfaces = (struct iio_event_interface *)
+		(kzalloc(sizeof(struct iio_event_interface)
+			 *dev_info->num_interrupt_lines,
+			 GFP_KERNEL));
+	if (dev_info->event_interfaces == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	/* assign id's to the event_interface elements */
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+		dev_info->event_interfaces[i].id = i;
+		dev_info->event_interfaces[i].owner = dev_info->driver_module;
+	}
+	dev_info->interrupts
+		= kzalloc(sizeof(struct iio_interrupt *)
+			  *dev_info->num_interrupt_lines,
+			  GFP_KERNEL);
+	if (dev_info->interrupts == NULL) {
+		dev_err(dev_info->dev,
+			"Failed to register sysfs hooks for events attributes");
+		ret = -ENOMEM;
+		goto error_free_event_interfaces;
+	}
+
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+
+		snprintf(dev_info->event_interfaces[i]._name, 20,
+			"event_line%d_minor", i);
+		ret = iio_setup_ev_int(&dev_info->event_interfaces[i],
+				       (const char *)(dev_info
+						      ->event_interfaces[i]
+						      ._name),
+				       dev_info->driver_module,
+				       dev_info->dev);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Could not get chrdev interface\n");
+			goto error_free_setup_ev_ints;
+		}
+	}
+	dev_info->event_attrs->name = "event_sources";
+	ret = sysfs_create_group(&dev_info->dev->kobj, dev_info->event_attrs);
+	if (ret) {
+		dev_err(dev_info->dev,
+			"Failed to register sysfs hooks for events attributes");
+		goto error_free_setup_ev_ints;
+	}
+	/* May double initialize lists in case of shared handlers,
+	   but other than a slight overhead that isn't a problem */
+	j = 0;
+	while (1) {
+		if (dev_info->event_attrs->attrs[j] == NULL)
+			break;
+		devattr = container_of(dev_info->event_attrs->attrs[j],
+				       struct device_attribute, attr);
+		indio_devattr = to_iio_event_attr(devattr);
+		INIT_LIST_HEAD(&indio_devattr->listel->list);
+		j++;
+	}
+	return 0;
+
+error_free_setup_ev_ints:
+	for (j = 0; j < i; j++)
+		iio_free_ev_int(&dev_info->event_interfaces[j],
+				dev_info->dev);
+	kfree(dev_info->interrupts);
+error_free_event_interfaces:
+	kfree(dev_info->event_interfaces);
+error_ret:
+	return ret;
+}
+
+void iio_device_unregister_eventset(struct iio_dev *dev_info)
+{
+	int i;
+	if (dev_info->num_interrupt_lines == 0)
+		return;
+	for (i = 0; i < dev_info->num_interrupt_lines; i++)
+		iio_free_ev_int(&dev_info->event_interfaces[i],
+					 dev_info->dev);
+	if (dev_info->event_attrs)
+		sysfs_remove_group(&dev_info->dev->kobj, dev_info->event_attrs);
+	kfree(dev_info->event_interfaces);
+}
+
+int iio_get_ptimer(const char **name)
+{
+	struct ptimer_info_listel *ptimer_i;
+
+	*name = NULL;
+	mutex_lock(&industrialio_ptimer_board_lock);
+
+	list_for_each_entry(ptimer_i, &industrialio_ptimer_board_info_list,
+			    list)
+		if (ptimer_i->inuse == false) {
+			ptimer_i->inuse = true;
+			*name = ptimer_i->info.name;
+			break;
+		}
+	mutex_unlock(&industrialio_ptimer_board_lock);
+	if (*name == NULL)
+		return -EINVAL;
+
+	return 0;
+}
+int iio_free_ptimer(const char *name)
+{
+	struct ptimer_info_listel *ptimer_i;
+
+	mutex_lock(&industrialio_ptimer_board_lock);
+	list_for_each_entry(ptimer_i, &industrialio_ptimer_board_info_list,
+			    list)
+		if (ptimer_i->info.name == name) {
+			ptimer_i->inuse = false;
+			break;
+		}
+	mutex_unlock(&industrialio_ptimer_board_lock);
+
+	return 0;
+}
+
+int iio_device_register_ptimer(struct iio_dev *dev_info)
+{
+	int ret = 0;
+
+	if (dev_info->modes & INDIO_RING_POLLED) {
+		ret = iio_get_ptimer(&dev_info->ptimer_name);
+		if (ret)
+			goto error_ret;
+		ret = iio_ptimer_request_periodic_timer((char *)
+							(dev_info->ptimer_name),
+							dev_info);
+		if (ret)
+			goto error_release_ptimer;
+	}
+
+	return ret;
+
+error_release_ptimer:
+	iio_free_ptimer(dev_info->ptimer_name);
+error_ret:
+	return ret;
+
+}
+
+void iio_device_unregister_ptimer(struct iio_dev *dev_info)
+{
+	if (dev_info->ptimer) {
+		iio_ptimer_unrequest_periodic_timer(dev_info);
+		iio_free_ptimer(dev_info->ptimer_name);
+	}
+}
+
+int iio_device_register(struct iio_dev *dev_info)
+{
+	int ret;
+	mutex_init(&dev_info->mlock);
+	dev_set_drvdata(dev_info->dev, (void *)(dev_info));
+
+/*Get a unique id */
+	ret = iio_device_register_id(dev_info);
+	if (ret)
+		goto error_nothing;
+
+/* Create sysfs device */
+	ret = iio_device_register_sysfs(dev_info);
+	if (ret)
+		goto error_free_idr;
+
+/* Interrupt triggered events setup */
+	ret = iio_device_register_eventset(dev_info);
+	if (ret)
+		goto error_free_sysfs;
+
+/* Ring buffer init if relevant */
+	if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY)) {
+
+		ret = iio_device_register_sw_ring(dev_info, 0);
+		if (ret)
+			goto error_free_eventset;
+	}
+/* Register ptimer if relevant */
+	if (dev_info->modes & INDIO_RING_POLLED) {
+		ret = iio_device_register_ptimer(dev_info);
+		if (ret)
+			goto error_unregister_sw_ring;
+	}
+
+	return 0;
+
+error_unregister_sw_ring:
+	iio_device_unregister_sw_ring(dev_info);
+error_free_eventset:
+	iio_device_unregister_eventset(dev_info);
+error_free_sysfs:
+	iio_device_unregister_sysfs(dev_info);
+error_free_idr:
+	iio_device_unregister_id(dev_info);
+error_nothing:
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *dev_info)
+{
+	if (dev_info->modes & INDIO_RING_POLLED)
+		iio_device_unregister_ptimer(dev_info);
+	if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY))
+		iio_device_unregister_sw_ring(dev_info);
+	iio_device_unregister_eventset(dev_info);
+	iio_device_unregister_sysfs(dev_info);
+	iio_device_unregister_id(dev_info);
+
+}
+EXPORT_SYMBOL_GPL(iio_device_unregister);
+
+subsys_initcall(iio_init);
+module_exit(iio_exit);
drivers/industrialio/industrialio-rtc.c +134 -0
@@ -0,0 +1,134 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+#include <linux/industrialio_ptimer.h>
+/* This is a temporary stop gap until a more generic timer subsystem is in place
+ * within the kernel.
+ * See discussion (initial thoughts so far) on the rtc mailing list.
+ * Comments still welcomed though I may not do much about them!.
+ */
+int iio_ptimer_irq_set_state(struct iio_dev *indio_dev, bool state)
+{
+	return rtc_irq_set_state(indio_dev->ptimer->rtc,
+				 &indio_dev->ptimer->task,
+				 state);
+}
+EXPORT_SYMBOL(iio_ptimer_irq_set_state);
+
+int iio_ptimer_set_freq(struct iio_periodic *ptimer,
+			    unsigned frequency)
+{
+	int ret;
+
+	ret = rtc_irq_set_freq(ptimer->rtc, &ptimer->task, frequency);
+	if (ret == 0)
+		ptimer->frequency = frequency;
+
+	return ret;
+}
+EXPORT_SYMBOL(iio_ptimer_set_freq);
+
+static ssize_t iio_ptimer_show_samp_freq(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct iio_periodic *ptimer = dev_info->ptimer;
+	return sprintf(buf, "%u\n", ptimer->frequency);
+}
+
+static ssize_t iio_ptimer_store_samp_freq(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct iio_periodic *ptimer = dev_info->ptimer;
+	int ret;
+	unsigned long val;
+
+	ret = strict_strtoul(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = iio_ptimer_set_freq(ptimer, val);
+	if (ret)
+		goto error_ret;
+	return len;
+
+error_ret:
+	return ret;
+}
+
+
+IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+		       iio_ptimer_show_samp_freq,
+		       iio_ptimer_store_samp_freq);
+
+int iio_ptimer_request_periodic_timer(char *name,
+				      struct iio_dev *indio_dev)
+{
+	int ret;
+
+	indio_dev->ptimer = kmalloc(sizeof(struct iio_periodic), GFP_KERNEL);
+	indio_dev->ptimer->rtc = rtc_class_open(name);
+	if (indio_dev->ptimer == NULL) {
+		ret = -EINVAL;
+		goto error_free_ptimer;
+	}
+	indio_dev->ptimer->task.func = indio_dev->ring_poll_func;
+	indio_dev->ptimer->task.private_data = indio_dev;
+	ret = rtc_irq_register(indio_dev->ptimer->rtc,
+			       &indio_dev->ptimer->task);
+	if (ret)
+		goto error_close_class;
+
+	ret = sysfs_add_file_to_group(&indio_dev->dev->kobj,
+				      &iio_dev_attr_sampling_frequency
+				      .dev_attr.attr,
+				      "ring_buffer");
+	if (ret)
+		goto error_unregister_irq;
+
+	return 0;
+
+error_unregister_irq:
+	rtc_irq_unregister(indio_dev->ptimer->rtc, &indio_dev->ptimer->task);
+
+error_close_class:
+	rtc_class_close(indio_dev->ptimer->rtc);
+error_free_ptimer:
+	kfree(indio_dev->ptimer);
+	indio_dev->ptimer = NULL;
+	return ret;
+}
+EXPORT_SYMBOL(iio_ptimer_request_periodic_timer);
+
+void iio_ptimer_unrequest_periodic_timer(struct iio_dev *indio_dev)
+{
+
+	sysfs_remove_file_from_group(&indio_dev->dev->kobj,
+				      &iio_dev_attr_sampling_frequency
+				     .dev_attr.attr,
+				     "ring_buffer");
+
+	if (indio_dev->ptimer->rtc) {
+		rtc_irq_set_state(indio_dev->ptimer->rtc,
+				  &indio_dev->ptimer->task, 0);
+		rtc_irq_unregister(indio_dev->ptimer->rtc,
+				   &indio_dev->ptimer->task);
+				flush_scheduled_work();
+		rtc_class_close(indio_dev->ptimer->rtc);
+		flush_scheduled_work();
+	}
+	kfree(indio_dev->ptimer);
+}
+EXPORT_SYMBOL(iio_ptimer_unrequest_periodic_timer);
drivers/industrialio/industrialio-ring.c +760 -0
@@ -0,0 +1,770 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008 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.
+ *
+ * Handling of ring allocation / resizing.
+ *
+ *
+ * Things to look at here.
+ * - Better memory allocation techniques?
+ * - Alternative access techniques?
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+
+/* Prevent resizing of the ring if it might break anything */
+void iio_mark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring)
+{
+	spin_lock(ring->use_lock);
+	ring->use_count++;
+	spin_unlock(ring->use_lock);
+}
+EXPORT_SYMBOL_GPL(iio_mark_sw_ring_buffer_in_use);
+
+void iio_unmark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring)
+{
+	spin_lock(ring->use_lock);
+	ring->use_count--;
+	spin_unlock(ring->use_lock);
+}
+EXPORT_SYMBOL_GPL(iio_unmark_sw_ring_buffer_in_use);
+
+/* Mark that a resize is needed */
+static void iio_mark_sw_ring_buffer_need_update(struct iio_sw_ring_buffer *ring)
+{
+	spin_lock(ring->use_lock);
+	ring->update_needed = 1;
+	spin_unlock(ring->use_lock);
+}
+
+/* Event handling for the ring - allows escallation of events */
+int iio_put_ring_event(struct iio_ring_buffer *ring_buf,
+		       int event_code,
+		       s64 timestamp)
+{
+	return __iio_put_event(&ring_buf->ev_int,
+			       event_code,
+			       timestamp,
+			       &ring_buf->shared_ev_pointer);
+}
+EXPORT_SYMBOL(iio_put_ring_event);
+
+int iio_put_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
+				    int event_code,
+				    s64 timestamp)
+{
+	if (ring_buf->shared_ev_pointer.ev_p)
+		__iio_change_event(ring_buf->shared_ev_pointer.ev_p,
+				   event_code,
+				   timestamp);
+	else
+		return iio_put_ring_event(ring_buf,
+					  event_code,
+					  timestamp);
+	return 0;
+}
+EXPORT_SYMBOL(iio_put_or_escallate_ring_event);
+
+
+/* Ring buffer related functionality */
+/* Store to ring is typically called in the bh of a data ready interrupt handler
+ * in the device driver */
+/* Lock always held if their is a chance this may be called */
+int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
+			 unsigned char *data,
+			 s64 timestamp)
+{
+	bool init_read = true;
+	int ret;
+	int code;
+
+	/* initial store */
+	if (unlikely(ring->write_p == 0)) {
+		ring->write_p = ring->data;
+		/* doesn't actually matter if this is out of the set */
+		ring->half_p = ring->data - ring->buf.length*ring->buf.size/2;
+		init_read = false;
+	}
+	memcpy(ring->write_p, data, ring->buf.size);
+	barrier();
+	ring->last_written_p = ring->write_p;
+	barrier();
+	ring->write_p += ring->buf.size;
+	/* End of ring, back to the beginning */
+	if (ring->write_p == ring->data + ring->buf.length*ring->buf.size) {
+		ring->write_p = ring->data;
+		ring->buf.loopcount++;
+	}
+	if (ring->read_p == 0)
+		ring->read_p = ring->data;
+	/* Buffer full - move the read pointer and create / escalate
+	 * ring event */
+	else if (ring->write_p == ring->read_p) {
+		ring->read_p += ring->buf.size;
+		if (ring->read_p
+		    == ring->data + ring->buf.length*ring->buf.size)
+			ring->read_p = ring->data;
+
+		spin_lock(ring->buf.shared_ev_pointer.lock);
+		if (ring->buf.shared_ev_pointer.ev_p) {
+			/* Event escalation - probably quicker to let this
+			   keep running than check if it is necessary */
+			code = IIO_EVENT_CODE_RING_100_FULL;
+			__iio_change_event(ring
+					   ->buf.shared_ev_pointer.ev_p,
+					   code,
+					   timestamp);
+		} else {
+			code = IIO_EVENT_CODE_RING_100_FULL;
+			ret = __iio_put_event(&ring->buf.ev_int,
+					      code,
+					      timestamp,
+					      &ring
+					      ->buf.shared_ev_pointer);
+			if (ret) {
+				spin_unlock(ring->buf.shared_ev_pointer.lock);
+				goto error_ret;
+			}
+		}
+		spin_unlock(ring->buf.shared_ev_pointer.lock);
+	}
+	/* investigate if our event barrier has been passed */
+	/* There are definite 'issues' with this and chances of
+	 * simultaneous read */
+	/* Also need to use loop count to ensure this only happens once */
+	ring->half_p += ring->buf.size;
+	if (ring->half_p == ring->data + ring->buf.length*ring->buf.size)
+		ring->half_p = ring->data;
+	if (ring->half_p == ring->read_p) {
+		spin_lock(ring->buf.shared_ev_pointer.lock);
+		code = IIO_EVENT_CODE_RING_50_FULL;
+		ret = __iio_put_event(&ring->buf.ev_int,
+				      code,
+				      timestamp,
+				      &ring->buf.shared_ev_pointer);
+		spin_unlock(ring->buf.shared_ev_pointer.lock);
+
+		if (ret)
+			goto error_ret;
+	}
+	return 0;
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_store_to_sw_ring);
+
+
+/*doesn't currently read the timestamp */
+/* For software ring buffers this function is needed to get the latest
+ * reading without preventing it from ending up in the ring buffer.
+*/
+int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
+			       unsigned char *data)
+{
+	int loopcount_copy;
+	unsigned char *last_written_p_copy;
+	iio_mark_sw_ring_buffer_in_use(ring);
+again:
+	loopcount_copy = ring->buf.loopcount;
+	barrier();
+	last_written_p_copy = ring->last_written_p;
+	barrier(); /*unnessecary? */
+
+	memcpy(data, last_written_p_copy, ring->buf.size);
+
+	if (unlikely(loopcount_copy != ring->buf.loopcount)) {
+		if (unlikely(ring->last_written_p >= last_written_p_copy))
+			goto again;
+	}
+	iio_unmark_sw_ring_buffer_in_use(ring);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_read_last_from_sw_ring);
+
+/* Ring buffer access fileops */
+int iio_ring_open(struct inode *inode, struct file *filp)
+{
+	struct iio_sw_ring_buffer *ring = filp->private_data;
+
+	iio_mark_sw_ring_buffer_in_use(ring);
+	try_module_get(ring->buf.access_minor_attr.dev_attr.attr.owner);
+
+	return 0;
+}
+
+int iio_ring_release(struct inode *inode, struct file *filp)
+{
+	struct iio_sw_ring_buffer *ring = filp->private_data;
+
+	module_put(ring->buf.access_minor_attr.dev_attr.attr.owner);
+	clear_bit(IIO_BUSY_BIT_POS, &ring->buf.access_handler.flags);
+	iio_unmark_sw_ring_buffer_in_use(ring);
+
+	return 0;
+}
+
+/* no point in ripping more than nearest number of whole records below count */
+/* Depending on movement of pointers in the meantime this may return a lot
+ * less than count*/
+/* Also, we aren't going to wait for enough data to be available */
+
+/* Can only occur currently when the ring buffer is marked
+   - from userspace call */
+ssize_t iio_ring_rip(struct file *filp,
+		     char *buf,
+		     size_t count,
+		     loff_t *f_ps)
+{
+	unsigned char *initial_read_p, *initial_write_p,
+		*current_read_p, *end_read_p;
+
+	struct iio_sw_ring_buffer *ring = filp->private_data;
+	unsigned char *data_cpy;
+	int ret;
+	int dead_offset;
+	int bytes_to_rip = 0;
+	int max_copied;
+	/* Round down to nearest datum boundary */
+	bytes_to_rip = (count - count % ring->buf.size);
+	/* Limit size to whole of ring buffer */
+	if (bytes_to_rip > ring->buf.size*ring->buf.length)
+		bytes_to_rip = ring->buf.size*ring->buf.length;
+	data_cpy = kmalloc(bytes_to_rip, GFP_KERNEL);
+	if (data_cpy == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* build local copy */
+	initial_read_p = ring->read_p;
+	if (unlikely(initial_read_p == 0)) {
+		/* No data here as yet */
+		ret = 0;
+		goto error_free_data_cpy;
+	}
+	initial_write_p = ring->write_p;
+
+	/* Need a consistent pair */
+	while (initial_read_p != ring->read_p
+	      || initial_write_p != ring->write_p) {
+		initial_read_p = ring->read_p;
+		initial_write_p = ring->write_p;
+	}
+	if (initial_write_p == initial_read_p) {
+		/* No new data available.*/
+		ret = 0;
+		goto error_free_data_cpy;
+	}
+
+	if (initial_write_p > initial_read_p + bytes_to_rip) {
+		/* write_p is greater than necessary, all is easy */
+		max_copied = bytes_to_rip;
+		memcpy(data_cpy, initial_read_p, max_copied);
+		end_read_p = initial_read_p + max_copied;
+	} else if (initial_write_p > initial_read_p) {
+		/*not enough data to cpy */
+		max_copied = initial_write_p - initial_read_p;
+		memcpy(data_cpy, initial_read_p, max_copied);
+		end_read_p = initial_write_p;
+	} else { /* going through 'end' of ring buffer */
+		max_copied = ring->data
+			+ ring->buf.length*ring->buf.size - initial_read_p;
+		memcpy(data_cpy, initial_read_p, max_copied);
+		if (initial_write_p > ring->data + bytes_to_rip - max_copied) {
+			/* enough data to finish */
+			memcpy(data_cpy + max_copied, ring->data,
+			       bytes_to_rip - max_copied);
+			max_copied = bytes_to_rip;
+			end_read_p = ring->data + (bytes_to_rip - max_copied);
+		} else {  /* not enough data */
+			memcpy(data_cpy + max_copied, ring->data,
+			       initial_write_p - ring->data);
+			max_copied += initial_write_p - ring->data;
+			end_read_p = initial_write_p;
+		}
+	}
+	/* Now to verify which section was cleanly copied - i.e. how far
+	 * read pointer has been pushed */
+	current_read_p = ring->read_p;
+
+	if (initial_read_p <= current_read_p)
+		dead_offset = current_read_p - initial_read_p;
+	else
+		dead_offset = ring->buf.length*ring->buf.size
+			- (initial_read_p - current_read_p);
+
+	/* possible issue if the initial write has been lapped or indeed
+	 * the point we were reading to has been passed */
+	/* No valid data read.
+	 * In this case the read pointer is already correct having been
+	 * pushed further than we would look. */
+	if (max_copied - dead_offset < 0) {
+		ret = 0;
+		goto error_free_data_cpy;
+	}
+
+	/* setup the next read position */
+	ring->read_p = end_read_p;
+
+	if (copy_to_user(buf, data_cpy + dead_offset,
+			 max_copied - dead_offset))  {
+		ret =  -EFAULT;
+		goto error_free_data_cpy;
+	}
+	kfree(data_cpy);
+
+	return max_copied - dead_offset;
+
+error_free_data_cpy:
+	kfree(data_cpy);
+error_ret:
+	return 0;
+}
+
+static const struct file_operations iio_ring_fileops = {
+	.read = iio_ring_rip,
+	.release = iio_ring_release,
+	.open = iio_ring_open,
+	.owner = THIS_MODULE,
+};
+
+inline int __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
+						  int id,
+						  struct module *owner,
+						  struct device *dev)
+{
+	int ret;
+
+/* Create and register the event character device */
+	buf->event_minor_name = kmalloc(20, GFP_KERNEL);
+	if (buf->event_minor_name == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	sprintf(buf->event_minor_name, "ring_buffer%d_ev_minor", id);
+	ret = iio_setup_ev_int(&(buf->ev_int),
+			       (const char *)(buf->event_minor_name),
+			       owner,
+			       dev);
+	if (ret)
+		goto error_free_event_minor_name;
+
+	return 0;
+
+error_free_event_minor_name:
+	kfree(buf->event_minor_name);
+error_ret:
+	return ret;
+}
+
+inline void __iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
+						struct device *dev)
+{
+	iio_free_ev_int(&(buf->ev_int), dev);
+	kfree(buf->event_minor_name);
+}
+
+inline int
+__iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
+					int id,
+					struct module *owner,
+					struct device *dev,
+					const struct file_operations *fops)
+{
+	int ret;
+/* Create and register the access character device */
+	buf->access_minor_name = kmalloc(20, GFP_KERNEL);
+	if (buf->access_minor_name == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	sprintf(buf->access_minor_name, "ring_buffer%d_access_minor", id);
+
+	ret = iio_allocate_chrdev(&buf->access_handler);
+	if (ret)
+		goto error_free_access_minor_name;
+	buf->access_handler.fops = fops;
+	buf->access_handler.flags = 0;
+
+	__init_iio_chrdev_minor_attr(&buf->access_minor_attr,
+				   (const char *)(buf->access_minor_name),
+				   owner,
+				   buf->access_handler.id);
+
+	ret = sysfs_create_file(&dev->kobj,
+				&(buf->access_minor_attr.dev_attr.attr));
+	if (ret)
+		goto error_deallocate_chrdev;
+	return 0;
+
+error_deallocate_chrdev:
+	iio_deallocate_chrdev(&buf->access_handler);
+error_free_access_minor_name:
+	kfree(buf->access_minor_name);
+error_ret:
+	return ret;
+}
+
+inline void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
+						 struct device *dev)
+{
+	sysfs_remove_file(&dev->kobj,
+			  &buf->access_minor_attr.dev_attr.attr);
+	iio_deallocate_chrdev(&buf->access_handler);
+	kfree(buf->access_minor_name);
+}
+
+int iio_request_hw_ring_buffer(int bytes_per_datum,
+			       int length,
+			       struct iio_hw_ring_buffer **ring,
+			       int id,
+			       struct module *owner,
+			       struct device *dev,
+			       const struct file_operations *fops,
+			       void *private)
+{
+	int ret;
+
+	*ring = kmalloc(sizeof(struct iio_hw_ring_buffer),
+			GFP_KERNEL);
+
+	if (*ring == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	(*ring)->private = private;
+	INIT_IIO_RING_BUFFER(&((*ring)->buf), bytes_per_datum, length);
+	ret = __iio_request_ring_buffer_event_chrdev(&(*ring)->buf,
+						     id,
+						     owner,
+						     dev);
+
+	if (ret)
+		goto error_free_ring_data;
+	ret = __iio_request_ring_buffer_access_chrdev(&(*ring)->buf,
+						      id,
+						      owner,
+						      dev,
+						      fops);
+	if (ret)
+		goto error_free_ring_buffer_event_chrdev;
+	(*ring)->buf.ev_int.private = (*ring);
+	(*ring)->buf.access_handler.private = (*ring);
+
+	return 0;
+
+error_free_ring_buffer_event_chrdev:
+	__iio_free_ring_buffer_event_chrdev(&(*ring)->buf, dev);
+error_free_ring_data:
+	/* there isn't any!*/
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_request_hw_ring_buffer);
+
+void iio_free_hw_ring_buffer(struct iio_hw_ring_buffer *ring,
+			     struct device *dev)
+{
+	__iio_free_ring_buffer_access_chrdev(&(ring->buf), dev);
+	__iio_free_ring_buffer_event_chrdev(&(ring->buf), dev);
+	kfree(ring);
+}
+EXPORT_SYMBOL_GPL(iio_free_hw_ring_buffer);
+
+
+/* Resize the ring if requested - run whenever ring buffer mode entered */
+int __iio_request_update_sw_ring_buffer(int bytes_per_datum,
+					int length,
+					struct iio_sw_ring_buffer *ring,
+					int id,
+					struct module *owner,
+					struct device *dev)
+{
+/* Reference count the ring - if anyone is using it this will fail!*/
+	int ret = 0;
+/* Need to sanity check if this is necessary? */
+	spin_lock(ring->use_lock);
+
+	if (ring->use_count || !ring->update_needed) {
+		ret = -EAGAIN;
+		goto error_ret;
+	}
+	kfree(ring->data);
+	/* keeps clear of chr devs etc - so fine to use here - I THINK!*/
+	INIT_IIO_SW_RING_BUFFER(ring, bytes_per_datum, length);
+	if (ring->data == NULL)
+		ret = -ENOMEM;
+
+error_ret:
+	spin_unlock(ring->use_lock);
+	return ret;
+}
+
+int iio_request_update_sw_ring_buffer(struct iio_dev *dev_info, int id)
+{
+	return __iio_request_update_sw_ring_buffer(dev_info
+						   ->ring_bytes_per_datum,
+						   dev_info->ring_length,
+						   dev_info->ring,
+						   id,
+						   dev_info->driver_module,
+						   dev_info->dev);
+}
+EXPORT_SYMBOL_GPL(iio_request_update_sw_ring_buffer);
+
+/* Should only occur on init so no locking needed */
+int iio_request_sw_ring_buffer(int bytes_per_datum,
+			       int length,
+			       struct iio_sw_ring_buffer **ring,
+			       int id,
+			       struct module *owner,
+			       struct device *dev)
+{
+	int ret;
+
+	/* Actually do the ring buffer initialization */
+	*ring = kzalloc(sizeof(struct iio_sw_ring_buffer),
+			GFP_KERNEL);
+	if (*ring == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	/* Moved to an allocation on demand model.*/
+	iio_mark_sw_ring_buffer_need_update(*ring);
+	ret = __iio_request_ring_buffer_event_chrdev(&(*ring)->buf,
+						     id,
+						     owner,
+						     dev);
+	if (ret)
+		goto error_free_ring_data;
+
+	ret = __iio_request_ring_buffer_access_chrdev(&(*ring)->buf,
+						      id,
+						      owner,
+						      dev,
+						      &iio_ring_fileops);
+	if (ret)
+		goto error_free_ring_buffer_event_chrdev;
+
+	/* Setup the private pointer so the fileoperations will work */
+	(*ring)->buf.ev_int.private = (*ring);
+	(*ring)->buf.access_handler.private = (*ring);
+
+	return 0;
+
+error_free_ring_buffer_event_chrdev:
+	__iio_free_ring_buffer_event_chrdev(&(*ring)->buf, dev);
+error_free_ring_data:
+	FREE_IIO_SW_RING_BUFFER(*ring);
+	kfree(*ring);
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_request_sw_ring_buffer);
+
+void iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
+			  struct device *dev)
+{
+	__iio_free_ring_buffer_access_chrdev(&(ring->buf), dev);
+	__iio_free_ring_buffer_event_chrdev(&(ring->buf), dev);
+	FREE_IIO_SW_RING_BUFFER(ring);
+	kfree(ring);
+}
+EXPORT_SYMBOL_GPL(iio_free_sw_ring_buffer);
+
+static ssize_t iio_read_ring_length(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	int len;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+	len = sprintf(buf, "%d\n", dev_info->ring_length);
+
+	return len;
+}
+
+static ssize_t iio_write_ring_length(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf,
+					      size_t len)
+{
+	int ret;
+	long val;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	/* Ring length stored here and in ring? */
+	if (val != dev_info->ring_length) {
+		dev_info->ring_length = val;
+		iio_mark_sw_ring_buffer_need_update(dev_info->ring);
+	}
+
+	return len;
+error_ret:
+	return ret;
+}
+
+static ssize_t iio_read_ring_bps(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	int len;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+	len = sprintf(buf, "%d\n", dev_info->ring_bytes_per_datum);
+
+	return len;
+}
+
+
+DEVICE_ATTR(length, S_IRUGO | S_IWUSR,
+	    iio_read_ring_length,
+	    iio_write_ring_length);
+/* The software ring buffers aren't currently capable of changing the
+ * storage accuracy so this is read only.
+ */
+DEVICE_ATTR(bps, S_IRUGO,
+	    iio_read_ring_bps,
+	    NULL);
+
+ssize_t iio_store_ring_enable(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf,
+			      size_t len)
+{
+	int ret;
+	bool requested_state, current_state;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+	mutex_lock(&dev_info->mlock);
+	requested_state = (buf[0] == '0') ? 0 : 1;
+	current_state = (dev_info->currentmode
+			 & (INDIO_RING_DATA_RDY | INDIO_RING_POLLED))
+			 ? 1: 0;
+	if (current_state == requested_state)
+		goto done;
+	if (requested_state) {
+		if (dev_info->ring_preenable) {
+			ret = dev_info->ring_preenable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+		ret = iio_request_update_sw_ring_buffer(dev_info, 0);
+		if (ret)
+			goto error_ret;
+		iio_mark_sw_ring_buffer_in_use(dev_info->ring);
+		if (dev_info->modes & INDIO_RING_DATA_RDY)
+			dev_info->currentmode = INDIO_RING_DATA_RDY;
+		else if (dev_info->modes & INDIO_RING_POLLED)
+			dev_info->currentmode = INDIO_RING_POLLED;
+		else { /* should never be reached */
+			ret = -EINVAL;
+			goto error_ret;
+		}
+
+		if (dev_info->ring_postenable) {
+			ret = dev_info->ring_postenable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+	} else {
+		if (dev_info->ring_predisable) {
+			ret = dev_info->ring_predisable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+		iio_unmark_sw_ring_buffer_in_use(dev_info->ring);
+		dev_info->currentmode = INDIO_DIRECT_MODE;
+		if (dev_info->ring_postdisable) {
+			ret = dev_info->ring_postdisable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+	}
+done:
+	mutex_unlock(&dev_info->mlock);
+	return len;
+error_ret:
+	mutex_unlock(&dev_info->mlock);
+	return ret;
+}
+
+static ssize_t iio_show_ring_enable(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+
+	int len;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	if (indio_dev->currentmode & (INDIO_RING_DATA_RDY | INDIO_RING_POLLED))
+		len = sprintf(buf, "1\n");
+	else
+		len = sprintf(buf, "0\n");
+
+	return len;
+}
+
+DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR,
+	    iio_show_ring_enable,
+	    iio_store_ring_enable);
+
+static struct attribute *iio_ring_attributes[] = {
+	&dev_attr_length.attr,
+	&dev_attr_bps.attr,
+	&dev_attr_sw_ring_enable.attr,
+	NULL,
+};
+
+static const struct attribute_group iio_ring_attribute_group = {
+	.name = "ring_buffer",
+	.attrs = iio_ring_attributes,
+};
+
+int iio_device_register_sw_ring(struct iio_dev *dev_info, int id)
+{
+	int ret;
+
+	ret = iio_request_sw_ring_buffer(dev_info->ring_bytes_per_datum,
+					 dev_info->ring_length,
+					 &dev_info->ring,
+					 id,
+					 dev_info->driver_module,
+					 dev_info->dev);
+	if (ret < 0)
+		goto error_ret;
+
+	ret = sysfs_create_group(&dev_info->dev->kobj,
+				 &iio_ring_attribute_group);
+	if (ret)
+		goto error_free_ring;
+
+	return 0;
+
+error_free_ring:
+	iio_free_sw_ring_buffer(dev_info->ring, dev_info->dev);
+error_ret:
+	return ret;
+}

drivers/industrialio/industrialio-ring.c line 770

Quoted 10 lines
drivers/industrialio/industrialio-ring.c +10 -0
+
+void iio_device_unregister_sw_ring(struct iio_dev *dev_info)
+{
+	sysfs_remove_group(&dev_info->dev->kobj,
+			   &iio_ring_attribute_group);
+	/* deallocate ring buffer related stuff */
+	if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY))
+		iio_free_sw_ring_buffer(dev_info->ring, dev_info->dev);
+
+}

Nice idea, I just don't get what is actually going on in here. This needs more planning.

Signature · 4 lines
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.
#5 lore raw

Re: [spi-devel-general] [Patch 1/4] Industrialio Core

Hi Ben,

Firstly thanks for taking a look at this.

Quoted 18 lines · Ben Dooks

include/linux/industrialio.h line 38

Quoted 5 lines
include/linux/industrialio.h +5 -0
+/* The actual event being pushed ot userspace */
+struct iio_event_data {
+	int id;
+	s64 timestamp;
+};

So is this an header for an event? Where is the data in this, this is confusing... Also, should we have a framework to produce an key/pair data, so that an single event can export multiple values from one event...

ie:

struct event_header {
	unsigned int 	id;
	unsigned int	nr_data;	/* number of event_data after */
	s64		timestamp;
Quoted 4 lines
struct event {
	struct event_header	header;
	struct event_data	data[0];
};

At the current time at least the purpose of events in this subsystem is rather different from those in say the input system. So far they have all corresponded to things that don't actually have any data associated with them.

The list so far is:
Ring bufer 50% full, Ring buffer 75% full (due to the support for escalating events, only the highest of these will ever be in the queue).

Motion detected, device dependent on whether this is for specific axis.

Thresholds breached.

Tap detection (common on accelerometers)

Anyhow I did originally start our with a similar layout to you have suggested (based in input subsystem) but removed it for now as it is currently unecessary.

Quoted 14 lines

the naming of these structures is rather long.

include/linux/industrialio.h line 55

Quoted 8 lines
include/linux/industrialio.h +8 -0
+/* Requires high resolution timers */
+/*  TODO - provide alternative if not available? */
+static inline s64 iio_get_time_ns(void)
+{
+	struct timespec ts;
+	ktime_get_ts(&ts);
+	return timespec_to_ns(&ts);
+}

do we really need something that accurate? we should at-least have the option to remove the timestamp or choose something of lower accuracy?

Not necessarily, hence the TODO. I've been wondering about using an approach similar to the 'scan' modes seen in the max1363 driver in which the time stamp will simply be another 'input' recorded to the ring buffer as necessary. Within the event structure, this is certainly something that needs to be configurable.

Quoted 7 lines
Quoted 7 lines
include/linux/industrialio.h +7 -0
+
+#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) {	\
+		INIT_IIO_RING_BUFFER(&(ring)->buf,			\
+				     _bytes_per_datum,			\
+				     _length);				\
+		(ring)->read_p = 0;					\
+		(ring)->write_p = 0;					\
Quoted 8 lines

include/linux/industrialio.h line 100

Quoted 6 lines
include/linux/industrialio.h +6 -0
+		(ring)->last_written_p = 0;				\
+		(ring)->data = kmalloc(_length*(ring)->buf.size,	\
+				       GFP_KERNEL);			\
+		(ring)->use_count = 0;					\
+		(ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
+	}

these should be inlined functions.

Oops, I'll clean them up. Thanks for pointing that out.

Quoted 11 lines

include/linux/industrialio.h line 132

Quoted 8 lines
include/linux/industrialio.h +8 -0
+
+int iio_request_sw_ring_buffer(int bytes_per_datum,
+			       int length,
+			       struct iio_sw_ring_buffer **ring,
+			       int id,
+			       struct module *owner,
+			       struct device *dev);
+

how about tying this to a driver, where you already know the owner and the dev?

Good point.

Quoted 10 lines
Quoted 10 lines
include/linux/industrialio.h +10 -0
+
+struct iio_work_cont {
+	struct work_struct ws;
+	struct work_struct ws_nocheck;
+	int address;
+	int mask;
+	void *st;
+};
+#define INIT_IIO_WORK_CONT(cont, _checkfunc, _nocheckfunc, _add, _mask, _st)\
+	do {								\
Quoted 8 lines

include/linux/industrialio.h line 328

Quoted 6 lines
include/linux/industrialio.h +6 -0
+	INIT_WORK(&(cont)->ws, _checkfunc);				\
+	INIT_WORK(&(cont)->ws_nocheck, _nocheckfunc);			\
+	(cont)->address = _add;						\
+	(cont)->mask = _mask;						\
+	(cont)->st = _st;						\
+	} while (0)

more nasty macros.

Yes, definitely need to get rid of most of them.
[ring buffer code]

Quoted 2 lines

Nice idea, I just don't get what is actually going on in here. This needs more planning.

I definitely should have put together some more explanatory documents explaining the aims and approach taken for the ring buffer handling. I'll get some initial stuff writen and posted here shortly.

Thanks for taking a look and your suggestions,

--
Jonathan Cameron

#6 lore raw

Re: [Patch 1/4] Industrialio Core

In reply to Jonathan Cameron

Jonathan Cameron schreef:
:

drivers/industrialio/industrialio-core.c line 486

Quoted 4 lines
drivers/industrialio/industrialio-core.c +4 -0
+
+static void __exit iio_exit(void)
+{
+	unregister_chrdev(IIO_MAJOR, "bob");

bob?! Maybe you meant "industrialio" ?

See you,
Eric

#7 lore raw

Re: [spi-devel-general] [Patch 1/4] Industrialio Core

Quoted 11 lines · Eric Piel

Jonathan Cameron schreef:
:

drivers/industrialio/industrialio-core.c line 486

Quoted 4 lines
drivers/industrialio/industrialio-core.c +4 -0
+
+static void __exit iio_exit(void)
+{
+	unregister_chrdev(IIO_MAJOR, "bob");

bob?! Maybe you meant "industrialio" ?

See you,
Eric

Good spot ;)

Jonathan

#8 lore raw

PATCH 2/4 Max1363 (and similar) ADCs

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

Add support for a number of Maxim ADCs.
(max1361, max1362, max1363, max1364, max1136, max1136, max1137, max1138, max1139, max1236, max1237, max11238, max1239)
Supplies direct access interfaces and a software ring buffer using a periodic timer to provide the triggering interrupt.

---
Patch depends on iio_core patch

 drivers/industrialio/Kconfig       |    2
 drivers/industrialio/Makefile      |    2
 drivers/industrialio/adc/Kconfig   |   12
 drivers/industrialio/adc/Makefile  |    4
 drivers/industrialio/adc/max1363.c | 1008 +++++++++++++++++++++++++++++++++++++
 5 files changed, 1028 insertions(+)
drivers/industrialio/Kconfig +2 -0
@@ -16,4 +16,6 @@ config INDUSTRIALIO_PTIMER_BOARDINFO
        boolean
        default y
 
+source drivers/industrialio/adc/Kconfig
+
 endif
drivers/industrialio/Makefile +2 -0
@@ -5,3 +5,5 @@ industrialio-objs := industrialio-core.o
 
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
 obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
+
+obj-y += adc/
drivers/industrialio/adc/Kconfig +12 -0
@@ -0,0 +1,12 @@
+#
+# ADC drivers
+#
+
+config MAX1363
+	tristate "MAXIM max1363 ADC driver"
+	help
+	  Say yes here to build support for many MAXIM i2c analog to digital
+	  convertors (ADC). (max1361, max1362, max1363, max1364, max1136,
+	  max1136, max1137, max1138, max1139, max1236, max1237, max11238,
+	  max1239) Provides direct access via sysfs and sw ring buffer using
+	  a periodic timer if available.
drivers/industrialio/adc/Makefile +4 -0
@@ -0,0 +1,4 @@
+
+# Makefile for industrial I/O ADC drivers
+#
+obj-$(CONFIG_MAX1363) += max1363.o
drivers/industrialio/adc/max1363.c +1008 -0

1,008 lines not on this page · raw message

_______________________________________________
lm-sensors mailing list
[email protected]
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

#9 lore raw

PATCH 3/4 ST LIS3L02DQ accelerometer

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

Add support for ST LIS3L02DQ accelerometer as found on the xbow imote2 sensor board. Provides direct access via sysfs interfaces and a software ring buffer using the chips datardy interrupt as a trigger. Motion detection and data ready events are available on a chrdev as are ring buffer 50% and 100% full events.

---
Patch depends on iio_core patch

drivers/industrialio/Kconfig                   |    1
drivers/industrialio/Makefile                  |    1
drivers/industrialio/accelerometer/Kconfig     |   19
drivers/industrialio/accelerometer/Makefile    |    5
drivers/industrialio/accelerometer/lis3l02dq.c | 1371 +++++++++++++++++++++++++
drivers/industrialio/accelerometer/lis3l02dq.h |  168 +++
6 files changed, 1565 insertions(+)
drivers/industrialio/Makefile +1 -0
@@ -7,3 +7,4 @@ obj-$(CONFIG_INDUSTRIALIO)	+= industrial
 obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
 
 obj-y += adc/
+obj-y += accelerometer/
drivers/industrialio/Kconfig +1 -0
@@ -16,6 +16,7 @@ config INDUSTRIALIO_PTIMER_BOARDINFO
        boolean
        default y
 
+source drivers/industrialio/accelerometer/Kconfig
 source drivers/industrialio/adc/Kconfig
 
 endif
drivers/industrialio/accelerometer/Kconfig +19 -0
@@ -0,0 +1,19 @@
+#
+# Accelerometer drivers
+#
+
+config LIS3L02DQ
+	tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
+	help
+	  Say yes here to build generic support for the ST microelectronics
+	  accelerometer. You will also need to one or more of the bus specific
+	  elements below. The driver supplies direct access via sysfs files
+	  and a software ring buffer using a supplied datardy interrupt.
+
+config LIS3L02DQ_SPI
+	depends on LIS3L02DQ && SPI
+	tristate "SPI support"
+	help
+	  Say yes here to build support for the ST LIS3L02DQ accelerometer via
+	  an SPI bus.
+
drivers/industrialio/accelerometer/Makefile +5 -0
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+obj-$(CONFIG_LIS3L02DQ_SPI)	+= lis3l02dq.o
drivers/industrialio/accelerometer/lis3l02dq.h +168 -0

168 lines not on this page · raw message

drivers/industrialio/accelerometer/lis3l02dq.c +1371 -0

1,371 lines not on this page · raw message

#10 lore raw

Re: [Patch 3/4] ST LIS3L02DQ accelerometer

drivers/industrialio/accelerometer/lis3l02dq.c line 172

Quoted 11 lines
drivers/industrialio/accelerometer/lis3l02dq.c +11 -0
+	xfer.tx_buf = kmalloc(4, GFP_KERNEL);
+	if (xfer.tx_buf == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	xfer.rx_buf = kmalloc(4, GFP_KERNEL);
+	if (xfer.rx_buf == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}

Do these really need to be two kmallocs

Quoted 4 lines
drivers/industrialio/accelerometer/lis3l02dq.c +4 -0
+	if (ret) {
+		dev_err(&st->us->dev, "problem with get x offset");
+		goto error_free_rx;
+	}

drivers/industrialio/accelerometer/lis3l02dq.c line 190

Quoted 10 lines
drivers/industrialio/accelerometer/lis3l02dq.c +10 -0
+	*val = ((unsigned char *)(xfer.rx_buf))[0];
+	kfree(xfer.rx_buf);
+	kfree(xfer.tx_buf);
+	return ret;
+error_free_rx:
+	kfree(xfer.rx_buf);
+error_free_tx:
+	kfree(xfer.tx_buf);
+error_ret:
+	return ret;

That lot makes no sense. You could just drop through..

drivers/industrialio/accelerometer/lis3l02dq.c line 198

Quoted 3 lines
drivers/industrialio/accelerometer/lis3l02dq.c +3 -0
+{
+	uint8_t val;
+	int8_t ret;

Kernel types (u8, s8 etc)

drivers/industrialio/accelerometer/lis3l02dq.c line 214

Quoted 5 lines
drivers/industrialio/accelerometer/lis3l02dq.c +4 -0
+	local_rx_buf = kmalloc(4, GFP_KERNEL);
+	local_tx_buf = kmalloc(4, GFP_KERNEL);
+
+	local_tx_buf[1] = LIS3L02DQ_READ_REG(reg_address);

OOPS on out of memory case

Quoted 4 lines
drivers/industrialio/accelerometer/lis3l02dq.c +4 -0
+	struct spi_transfer xfer = {
+		.tx_buf = NULL,
+		.rx_buf = NULL,
+		.bits_per_word = 16,

drivers/industrialio/accelerometer/lis3l02dq.c line 254

Quoted 10 lines
drivers/industrialio/accelerometer/lis3l02dq.c +10 -0
+		.len = 2,
+	};
+	int ret;
+
+	local_tx_buf = kmalloc(4, GFP_KERNEL);
+	if (local_tx_buf == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	xfer.tx_buf = local_tx_buf;

You seem to have a lot of almost identical routines here. It looks like with a few helpers this driver could be vastly shorter.

#11 lore raw

Re: [Patch 3/4] ST LIS3L02DQ accelerometer

Dear Alan,

Quoted 8 lines
Quoted 6 lines

+
+ xfer.rx_buf = kmalloc(4, GFP_KERNEL);
+ if (xfer.rx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_free_tx;
+ }

Do these really need to be two kmallocs

Sorry about that, legacy of a much older version. I definitely didn't clean this driver up properly (iio core is still a fair way off going anywhere so I'll admit I didn't didn't check the drivers using it as thoroughly as I should
have)

Quoted 17 lines
Quoted 9 lines

+ if (ret) {
+ dev_err(&st->us->dev, "problem with get x offset");
+ goto error_free_rx;
+ }
+ *val = ((unsigned char *)(xfer.rx_buf))[0];
+ kfree(xfer.rx_buf);
+ kfree(xfer.tx_buf);
+ return ret;

drivers/industrialio/accelerometer/lis3l02dq.c line 190

Quoted 6 lines
drivers/industrialio/accelerometer/lis3l02dq.c +6 -0
+error_free_rx:
+ kfree(xfer.rx_buf);
+error_free_tx:
+ kfree(xfer.tx_buf);
+error_ret:
+ return ret;

That lot makes no sense. You could just drop through..

Oops.

Quoted 5 lines
Quoted 3 lines

+{
+ uint8_t val;
+ int8_t ret;

Kernel types (u8, s8 etc)

To used to userspace programming! I'll fix them.

Quoted 7 lines
Quoted 5 lines

+ local_rx_buf = kmalloc(4, GFP_KERNEL);
+ local_tx_buf = kmalloc(4, GFP_KERNEL);
+
+ local_tx_buf[1] = LIS3L02DQ_READ_REG(reg_address);

OOPS on out of memory case

Good point.

Quoted 5 lines

You seem to have a lot of almost identical routines here. It looks like with a few helpers this driver could be vastly shorter.

Again a good point. I'll clean it up and repost.

Thanks for the hints,

--
Jonathan Cameron

#12 lore raw

PATCH 4/4 VTI SCA3000 Series accelerometer driver

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

Add support for the sca3000 series of accelerometers from VTI using the industrialio subsystem. Includes event interfaces (via chrdevs) for general events for events relating to the hardware ring buffer. Chrdev access to the hardware ring buffer and direct access to and control of the device via sysfs interfaces within the industrialio class.

---

drivers/industrialio/accelerometer/Kconfig   |    7
drivers/industrialio/accelerometer/Makefile  |    1
drivers/industrialio/accelerometer/sca3000.c | 1547 +++++++++++++++++++++++++++
include/linux/industrialio/sca3000.h         |   22
4 files changed, 1577 insertions(+)
drivers/industrialio/accelerometer/Kconfig +7 -0
@@ -17,3 +17,10 @@ config LIS3L02DQ_SPI
 	  Say yes here to build support for the ST LIS3L02DQ accelerometer via
 	  an SPI bus.
 
+config SCA3000
+	depends on SPI
+	tristate "VTI SCA3000 series accelerometers"
+	help
+	 Say yes here to build support for the VTI SCA3000 series of SPI 
+	 accelerometers. These devices use a hardware ring buffer. Direct
+	 access is available unless motion detector mode is enabled.
drivers/industrialio/accelerometer/Makefile +1 -0
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_LIS3L02DQ_SPI)	+= lis3l02dq.o
+obj-$(CONFIG_SCA3000)		+= sca3000.o
drivers/industrialio/accelerometer/sca3000.c +1547 -0

1,547 lines not on this page · raw message

include/linux/industrialio/sca3000.h +22 -0
@@ -0,0 +1,22 @@
+/* fixme, start these at zero and rebase chip_info array in driver */
+#define SCA3000_VARIANT_D01 0
+#define SCA3000_VARIANT_D03 1
+#define SCA3000_VARIANT_e02 2
+#define SCA3000_VARIANT_e04 3
+#define SCA3000_VARIANT_e05 4
+#define SCA3000_VARIANT_l01 5
+
+struct sca3000_platform_data {
+	int variant;
+};
+
+/* Conversion function for use with the ring buffer when in 11bit mode */
+inline int sca3000_11bit_convert(uint8_t msb, uint8_t lsb)
+{
+	int16_t val;
+
+	val = ((lsb >> 3) & 0x1C) | (msb << 5);
+	val |= (val & (1 << 12)) ? 0xE000 : 0;
+
+	return val;
+};
#13 lore raw
In reply to Jonathan Cameron
Quoted 9 lines · Jonathan Cameron

The subsystem is now in a functional state with a small set of drivers:

Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
- Uses a periodic timer to provide ring buffer mode.
- All reads form these devices are scan modes so direct single element access is not provided.
- Monitor mode on max1363 is not yet supported (need to do a bit debugging of the board I have so as to be able to test this).

Quoted 6 lines

ST LIS3L02DQ - SPI accelerometer.
- Uses a datardy interrupt to driver a software ring buffer.
- Most functionality of this device is supported.

VTI SCA3000 (tested with an e05)
- Hardware ring buffer.

I'd like to see something done to have the common parts of interfaces of the same class (e.g. accelerometers) be standard. Like hwmon does with temp#_input, etc.

Otherwise you made it easier to write drivers, but did nothing to help userspace to USE the drivers :-)

Signature · 4 lines
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh
#14 lore raw

Henrique de Moraes Holschuh schreef:

Quoted 17 lines · Jonathan Cameron
Quoted 9 lines

The subsystem is now in a functional state with a small set of drivers:

Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
- Uses a periodic timer to provide ring buffer mode.
- All reads form these devices are scan modes so direct single element access is not provided.
- Monitor mode on max1363 is not yet supported (need to do a bit debugging of the board I have so as to be able to test this).

Quoted 6 lines

ST LIS3L02DQ - SPI accelerometer.
- Uses a datardy interrupt to driver a software ring buffer.
- Most functionality of this device is supported.

VTI SCA3000 (tested with an e05)
- Hardware ring buffer.

Quoted 6 lines

I'd like to see something done to have the common parts of interfaces of the same class (e.g. accelerometers) be standard. Like hwmon does with temp#_input, etc.

Otherwise you made it easier to write drivers, but did nothing to help userspace to USE the drivers :-)

Hi,
I completely agree with Henrique. There already exist 3 accelerometer drivers in the kernel (and I'm writing a fourth on). What we are desperately in need of is a _user_ interface. So that a generic program can pop up and say "Oh, there is an accelerometer on this computer, I'll use it to detect free falls."

IMHO, I think the ADC should have a much more specific and specialised (user and kernel) API corresponding to the accelerometers. Granted, you probably had in mind only the accelerometers for industrial usage, but it would be much better if the current accelerometer drivers had a reason to be ported to this new subsystem.

In particular, what I think would be worthy would be:
* Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current version of industrialio
* If the accelerometer is soldered on the computer, define once for all to which _physical_ movement corresponds which axis (eg: a laptop on its normal position going up has axis Z increasing).
* Free fall event. Either it's hardware detected, or the accelerometer infrastructure will detect it in software.
* For each axis, what is the maximum and minimum bound and the unit - Probably worthy for the whole ADC infrastructure
* Joystick emulation (calibrated so that when the computer is not moving, all the values are at 0). All the current drivers have it.

That's it for my wish-list for accelerometers :-)

Also, a couple of specific comments on the patches:
* At a quick glance, it seems the VTI SCA3000 driver sends events at 50% and 75% ring full, while the ST LIS3L02DQ driver sends events at 50% and 100%. Why this difference?
* The accelerometer infrastructure has axis offset and axis gain attributes. However, it seems really specific to the ST accelerometer (and still, I doubt it's useful for most users as those values are factory-set to good values). Shouldn't they be moved to the ST LIS3L02DQ driver instead of being part of the accelerometer infrastructure?
* It would be also very good to provide a small userspace program showing how to read an industrialio device (à la evtest).

See you,
Eric

#15 lore raw
Quoted 37 lines · Eric Piel

Henrique de Moraes Holschuh schreef:

Quoted 17 lines · Jonathan Cameron
Quoted 9 lines

The subsystem is now in a functional state with a small set of drivers:

Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
- Uses a periodic timer to provide ring buffer mode.
- All reads form these devices are scan modes so direct single element access is not provided.
- Monitor mode on max1363 is not yet supported (need to do a bit debugging of the board I have so as to be able to test this).

Quoted 6 lines

ST LIS3L02DQ - SPI accelerometer.
- Uses a datardy interrupt to driver a software ring buffer.
- Most functionality of this device is supported.

VTI SCA3000 (tested with an e05)
- Hardware ring buffer.

Quoted 6 lines

I'd like to see something done to have the common parts of interfaces of the same class (e.g. accelerometers) be standard. Like hwmon does with temp#_input, etc.

Otherwise you made it easier to write drivers, but did nothing to help userspace to USE the drivers :-)

Hi,
I completely agree with Henrique. There already exist 3 accelerometer drivers in the kernel (and I'm writing a fourth on). What we are desperately in need of is a _user_ interface. So that a generic program can pop up and say "Oh, there is an accelerometer on this computer, I'll use it to detect free falls."

IMHO, I think the ADC should have a much more specific and specialised (user and kernel) API corresponding to the accelerometers. Granted, you probably had in mind only the accelerometers for industrial usage, but it would be much better if the current accelerometer drivers had a reason to be ported to this new subsystem.

Quoted 12 lines

In particular, what I think would be worthy would be:
* Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current version of industrialio
* If the accelerometer is soldered on the computer, define once for all to which _physical_ movement corresponds which axis (eg: a laptop on its normal position going up has axis Z increasing).
* Free fall event. Either it's hardware detected, or the accelerometer infrastructure will detect it in software.
* For each axis, what is the maximum and minimum bound and the unit - Probably worthy for the whole ADC infrastructure
* Joystick emulation (calibrated so that when the computer is not moving, all the values are at 0). All the current drivers have it.

I think if we're trying to make something that will cover a number of device classes, we need to be more like HID and have a system that reports and possibly records the following:

1) What data is possible to be returned from each event, with the units, magnitude and any scale applied by the device sending.

2) Each event should be able to report a number of items, so that if the sensor reports X/Y/Z in one go, there is just a single event containing those values.

3) A well defined set of information that can be read (like #1) that can provide details of the device and how it relates to the environment it is in.

Signature · 3 lines
Ben ([email protected], http://www.fluff.org/)

  'a smiley only costs 4 bytes'
#16 lore raw

Dear Ben,

Quoted 13 lines
Quoted 4 lines

In particular, what I think would be worthy would be:
* Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current version of industrialio
* If the accelerometer is soldered on the computer, define once for all

Quoted 8 lines

to which _physical_ movement corresponds which axis (eg: a laptop on its normal position going up has axis Z increasing).
* Free fall event. Either it's hardware detected, or the accelerometer infrastructure will detect it in software.
* For each axis, what is the maximum and minimum bound and the unit - Probably worthy for the whole ADC infrastructure
* Joystick emulation (calibrated so that when the computer is not moving, all the values are at 0). All the current drivers have it.

Quoted 3 lines

I think if we're trying to make something that will cover a number of device classes, we need to be more like HID and have a system that reports and possibly records the following:

Ok, this is the key disagreement at the moment on the approach to be taken here. I'd again argue that the purpose of events in this subsys are very different from what you describing. Events tend to indicate that something has happened (eg. the Free fall even mentioned above), not that the current value of something is x. It may be that we will have devices that do indeed act closer to you conventional user interface device and provide (x moved to 103 etc). If we do then this may all need rethinking.

Quoted 6 lines

1) What data is possible to be returned from each event, with the units, magnitude and any scale applied by the device sending.

2) Each event should be able to report a number of items, so that if the sensor reports X/Y/Z in one go, there is just a single event containing those values.

Agreed.

Quoted 3 lines

3) A well defined set of information that can be read (like #1) that can provide details of the device and how it relates to the environment it is in.

Indeed, this is crucial and probably forms the next element of the code to be written. At the moment I'm inclined to go with an approach similar to the input subsystem with it's proc interface that provides a nice machine readable description of device functionality.

Anyone have any other suggestions on how this can be sensibly achieved?

Thanks,
--
Jonathan Cameron

#17 lore raw
In reply to Eric Piel
Quoted 5 lines
Quoted 5 lines

I'd like to see something done to have the common parts of interfaces of the same class (e.g. accelerometers) be standard. Like hwmon does with temp#_input, etc.

Quoted 8 lines
Quoted 2 lines

Otherwise you made it easier to write drivers, but did nothing to help userspace to USE the drivers :-)

Hi,
I completely agree with Henrique. There already exist 3 accelerometer drivers in the kernel (and I'm writing a fourth on). What we are desperately in need of is a _user_ interface. So that a generic program can pop up and say "Oh, there is an accelerometer on this computer, I'll use it to detect free falls."

Agreed. (though on the whole I don't like testing that particular functionality ;)

Quoted 7 lines

IMHO, I think the ADC should have a much more specific and specialised (user and kernel) API corresponding to the accelerometers. Granted, you probably had in mind only the accelerometers for industrial usage, but it would be much better if the current accelerometer drivers had a reason to be ported to this new subsystem.>

Quoted 6 lines

In particular, what I think would be worthy would be:
* Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current version of industrialio
* If the accelerometer is soldered on the computer, define once for all to which _physical_ movement corresponds which axis (eg: a laptop on its normal position going up has axis Z increasing).

Agreed, though this is more documentation (and strict enforcement on drivers)

Quoted 2 lines

* Free fall event. Either it's hardware detected, or the accelerometer infrastructure will detect it in software.

Hadn't thought of doing that in software - should be relatively straight forward, though would involve a fairly large overhead if the intention is to detect it fast enough to park hardware etc. (similar to that for the ring buffer I guess). I'll look into getting that done for the next version (maybe driver specific for now).

Quoted 2 lines

* For each axis, what is the maximum and minimum bound and the unit - Probably worthy for the whole ADC infrastructure

Agreed - only laziness has prevented me adding the sysfs stuff to output that sort of thing.

Quoted 2 lines

* Joystick emulation (calibrated so that when the computer is not moving, all the values are at 0). All the current drivers have it.

Ok, this is a matter of writing an input subsystem element to do this. It was discussed in the original discussion on lkml and is definitely on the todo list (I like playing with that sort of thing as well :)

Quoted 7 lines

That's it for my wish-list for accelerometers :-)

Also, a couple of specific comments on the patches:
* At a quick glance, it seems the VTI SCA3000 driver sends events at 50% and 75% ring full, while the ST LIS3L02DQ driver sends events at 50% and 100%. Why this difference?

I wrote the lis3l02dq driver first, then discovered the VTI only gives hardware interrupts on those values. Probably not something that is going to be easy to standardize in general. Can easily add more levels if only to ensure a consistentish set are available.

Quoted 4 lines

* The accelerometer infrastructure has axis offset and axis gain attributes. However, it seems really specific to the ST accelerometer (and still, I doubt it's useful for most users as those values are factory-set to good values).

I'd seriously debate that! For starters that lis3l02dq is reading slightly over 10g static on my desk. For the sort of stuff I do with these, the factory calibration is typically shockingly inaccurate and subject to issues like temperature of environment etc.

Quoted 2 lines

Shouldn't they be moved to the ST LIS3L02DQ
driver instead of being part of the accelerometer infrastructure?

Based on the other accels I've used (quite a few different ones) these are pretty standard parameters - and I wish they were there on the VTI chip as it's calibration is far from perfect. They are to be found in about half of all the accelerometers I've come across.

Quoted 2 lines

* It would be also very good to provide a small userspace program showing how to read an industrialio device (à la evtest).

One is on it's way (oops, should have cleaned that up before posting these patches)

At the moment the big missing element of the subsystem is an easy way of querying what is there. (proc interface similar to that for the input subsystem) The lack of this makes writing remotely generic tests pretty hideous. Currently I have separate ones for each of the drivers which is just plain silly. I guess for now posting a commented one of those would be a good start and I'll do so after lunch.

Thanks for taking a look and particularly the accelerometer wish list. I'll see what I can do!

--
Jonathan Cameron

#18 lore raw

Jonathan Cameron schreef:
:

Quoted 2 lines
Quoted 1 line

* If the accelerometer is soldered on the computer, define once for all to which _physical_ movement corresponds which axis (eg: a laptop on its normal position going up has axis Z increasing).

Agreed, though this is more documentation (and strict enforcement on drivers)

Indeed, it's more about defining conventions. A one page document saying to userspace developers how they can expect any accelerometer driver will behave is desperately needed. The more conventions, the more all the drivers will behave similarly, and the easier it will be for userspace programs to be written :-)

Quoted 6 lines
Quoted 1 line

* Free fall event. Either it's hardware detected, or the accelerometer infrastructure will detect it in software.

Hadn't thought of doing that in software - should be relatively straight forward, though would involve a fairly large overhead if the intention is to detect it fast enough to park hardware etc. (similar to that for the ring buffer I guess). I'll look into getting that done for the next version (maybe driver specific for now).

Yes, on a second though, this is a low priority point. If userspace is able to know if the hardware has or not freefall detection, it should be possible to just implement the software detection in the userspace daemon. People might come up with lots of "clever" algorithms for that, so it might be better to not do too much in the kernel ;-)

Quoted 3 lines

At the moment the big missing element of the subsystem is an easy way of querying what is there. (proc interface similar to that for the input subsystem)

You mean /sys/class/input/, right? Indeed, something inspired by the input subsystem should work well.

See you,
Eric

#19 lore raw

hat done for the next

Quoted 6 lines
Quoted 1 line

version (maybe driver specific for now).

Yes, on a second though, this is a low priority point. If userspace is able to know if the hardware has or not freefall detection, it should be possible to just implement the software detection in the userspace daemon. People might come up with lots of "clever" algorithms for that, so it might be better to not do too much in the kernel ;-)

Good point; will leave that one for now.

Quoted 7 lines
Quoted 5 lines

At the moment the big missing element of the subsystem is an easy way of querying what is there. (proc interface similar to that for the input subsystem)

You mean /sys/class/input/, right? Indeed, something inspired by the input subsystem should work well.

No, I meant /proc/bus/input/devices which gives machine readable description of all devices registered with the input subsystem and their capabilities.

Makes it easier to make userspace code reasonably generic.

--
Jonathan

#20 lore raw
Quoted 9 lines · Jonathan Cameron
Quoted 6 lines
Quoted 4 lines

At the moment the big missing element of the subsystem is an easy way of querying what is there. (proc interface similar to that for the input subsystem)

You mean /sys/class/input/, right? Indeed, something inspired by the input subsystem should work well.

No, I meant /proc/bus/input/devices which gives machine readable description of all devices registered with the input subsystem and their capabilities.

/proc/bus/input is a legacy interface (pre-sysfs). Newer subsystems should try to stay in /sys/.

Signature · 1 line
Dmitry
#21 lore raw
Quoted 4 lines · Dmitry Torokhov
Quoted 3 lines · Jonathan Cameron
Quoted 3 lines
Quoted 3 lines

At the moment the big missing element of the subsystem is an easy way of querying what is there. (proc interface similar to that for the input subsystem)

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

You mean /sys/class/input/, right? Indeed, something inspired by the input subsystem should work well.

No, I meant /proc/bus/input/devices which gives machine readable description of all devices registered with the input subsystem and their capabilities.

/proc/bus/input is a legacy interface (pre-sysfs). Newer subsystems should try to stay in /sys/.

Ah, I'd assumed it was there to provide a centralized way of discovering what was there. I guess it's just a matter of scanning your way through the contents of /sys/class/input (and equivalent iio) then to find out what is present and what is supported by each device.

Messier in a sense but guess it will work.

Ah well,

Jonathan

#22 lore raw
In reply to Jonathan Cameron
Quoted 7 lines · Jonathan Cameron

* Software ring buffer support to allow semi regular capture of data form the device.  Typically this will be driven from either datardy events, or from a periodic timer interrupt (to this end a very simple wrapper for periodic RTC's is included. This will move to more generic timer interfaces as and when they become available.  For now available rtc's must be registered with the subsystem via the industrialio_register_ptimer function form within a board init.

Could you elaborate a bit on why you'd want hardware supported periodic timers, rather than either an hrtimer or a timer? Maybe provide some real-world scenarios where those software abstractions are inappropriate.

I can imagine numerous reasons, but those might not be yours...

- Dave

#23 lore raw
In reply to Jonathan Cameron
Quoted 4 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

The name is really bad, this sounds like something for doing large scale industrial process control.

Quoted 6 lines

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

We've already got an perfectly good hwmon framework, do we really need to do this again?

Quoted 47 lines

Key features of the subsystem include:

* Provision of sysfs access for direct reading from devices (similar to hwmon but without the buffering / update rate restrictions)

* Provision of chrdevs through which events may be passed to userspace in a similar fashion to the input subsystem. These events may be anything from hardware thresholds set on the sensor itself to sw / hw ring buffer event notifications (50% full etc).

* Provision of access via chrdevs to hardware ring buffers on devices that provide them.

* Software ring buffer support to allow semi regular capture of data form the device. Typically this will be driven from either datardy events, or from a periodic timer interrupt (to this end a very simple wrapper for periodic RTC's is included. This will move to more generic timer interfaces as and when they become available. For now available rtc's must be registered with the subsystem via the industrialio_register_ptimer function form within a board init.

* A set of sample drivers illustrating the main 'classes' of device. By classes I really mean devices that are interfaced with in a similar way.

The subsystem is now in a functional state with a small set of drivers:

Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
- Uses a periodic timer to provide ring buffer mode.
- All reads form these devices are scan modes so direct single element access is not provided.
- Monitor mode on max1363 is not yet supported (need to do a bit debugging of the board I have so as to be able to test this).

ST LIS3L02DQ - SPI accelerometer.
- Uses a datardy interrupt to driver a software ring buffer.
- Most functionality of this device is supported.

VTI SCA3000 (tested with an e05)
- Hardware ring buffer.

More drivers in preparation.

Next focus will be on cleaning up / implementing a more generic timer framework and allowing the system to partly run if not all dependencies are met (particularly availability of timers).

An initial set of patches will be attached to this thread shortly.

Signature · 4 lines
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.
#24 lore raw
Quoted 8 lines · Ben Dooks
Quoted 4 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

The name is really bad, this sounds like something for doing large scale industrial process control.

Well, it says "Industrial I/O". To me, this means it handles I/O devices typically found in industrial applications.

Quoted 10 lines
Quoted 7 lines

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

We've already got an perfectly good hwmon framework, do we really need to do this again?

hwmon is designed for slow I/O. It won't handle an ADC that does a few megasamples/sec.

Thanks,
Hans

#25 lore raw
Quoted 2 lines

hwmon is designed for slow I/O. It won't handle an ADC that does a few megasamples/sec.

At that rate I would assume you want a memory mapped ring buffer or similar not a sysfs style interface ?

#26 lore raw
Quoted 5 lines · Alan Cox
Quoted 2 lines

hwmon is designed for slow I/O. It won't handle an ADC that does a few megasamples/sec.

At that rate I would assume you want a memory mapped ring buffer or similar not a sysfs style interface ?

Agreed, this isn't going to do megsamps per second either (at least not without a fair bit faster processor / memory than available in embedded architectures). If you want to go at those rates you'll need something with a hardware ring buffer or an intermediate chip to provide said buffering whilst not trying to run a general purpose os at the same time.

I am considering adding a 'burst' mode which would use up any available i2c / spi hardware buffers to get somewhere nearer to those sort of speeds.

One of the most important elements of this subsystem is indeed a ring buffer architecture, currently not mmapped. I'm still trying to work out how to make that work without blocking the ring filling on interrupts. Currently I can only seem to get around this by doing copies of ring to mmapped locations and having some dirty bit type marking of regions that don't contain valid data. I guess suitable userspace libraries could hide this mess though.

For the few ksps range the current chrdev interface is adequate if not ideal.

The ring achitecture is one of the areas that probably needs most work.

Thanks,
--
Jonathan Cameron

#27 lore raw
In reply to Hans J. Koch
Quoted 4 lines · Hans J. Koch
Quoted 3 lines · Ben Dooks
Quoted 2 lines · Jonathan Cameron

Dear All,

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

The name is really bad, this sounds like something for doing large scale industrial process control.

Well, it says "Industrial I/O". To me, this means it handles I/O devices typically found in industrial applications.

Yes, industrial is generally process control of manufacturing processes which in my view is making this sound like it is limiting the field of operations.

All the applications we would currently need are things like handheld PDA type devices which are hardly 'industrial' or small consumer measurement systems.

Quoted 16 lines
Quoted 10 lines
Quoted 7 lines

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

We've already got an perfectly good hwmon framework, do we really need to do this again?

hwmon is designed for slow I/O. It won't handle an ADC that does a few megasamples/sec.

Thanks,
Hans

Signature · 3 lines
Ben ([email protected], http://www.fluff.org/)

  'a smiley only costs 4 bytes'
#28 lore raw
Quoted 11 lines · Ben Dooks
Quoted 10 lines · Hans J. Koch
Quoted 8 lines · Ben Dooks
Quoted 4 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

The name is really bad, this sounds like something for doing large scale industrial process control.

Quoted 6 lines
Quoted 2 lines

Well, it says "Industrial I/O". To me, this means it handles I/O devices typically found in industrial applications.

Yes, industrial is generally process control of manufacturing processes which in my view is making this sound like it is limiting the field of operations.

OK, I agree.

Quoted 4 lines

All the applications we would currently need are things like handheld PDA type devices which are hardly 'industrial' or small consumer measurement systems.

Well, though the _use_ of such devices might not be "industrial", _technically_ they are very similar to embedded systems found in automation or other industrial equipment.

Many of these devices (all that have mmappable memory) can be handled with a UIO driver, but for the rest (mostly stuff connected to serial busses), it's important to have a subsystem in the kernel. I really don't care too much about its name. BTW, before UIO was first published, its internal name was "Industrial I/O" ;-)

Thanks,
Hans

#29 lore raw
Quoted 10 lines
Quoted 8 lines
Quoted 4 lines

Well, it says "Industrial I/O". To me, this means it handles I/O devices typically found in industrial applications.

Yes, industrial is generally process control of manufacturing processes which in my view is making this sound like it is limiting the field of operations.

OK, I agree.

Agreed, though don't yet have a better idea.
In fact my use cases are all really embedded systems anyway.

Quoted 9 lines
Quoted 4 lines

All the applications we would currently need are things like handheld PDA type devices which are hardly 'industrial' or small consumer measurement systems.

Well, though the _use_ of such devices might not be "industrial", _technically_ they are very similar to embedded systems found in automation or other industrial equipment.

Quoted 6 lines

Many of these devices (all that have mmappable memory) can be handled with a UIO driver, but for the rest (mostly stuff connected to serial busses), it's important to have a subsystem in the kernel. I really don't care too much about its name. BTW, before UIO was first published, its internal name was "Industrial I/O" ;-)

lol. We could try and make it the standard working name for all new subsystems ;)
--
Jonathan Cameron

#30 lore raw
In reply to Ben Dooks
Quoted 8 lines · Ben Dooks
Quoted 4 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

The name is really bad, this sounds like something for doing large scale industrial process control.

To a certain extent I agree, it's a pretty hideous name and I'm happy to change it if someone can come up with a better one whilst maintaining the flexibility to handle devices that do a range of different sensing and output tasks.

Quoted 9 lines
Quoted 6 lines

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

We've already got an perfectly good hwmon framework, do we really need to do this again?

On this, see the original discussion (where using that was one of the options) discussed. Basically it comes down to a different set of requirements with the ability to handle events from the device, ring buffering and higher (non cached) update rates. Whilst we could have bludgeoned the functionality into that framework, it was decided that it was better to start afresh.

Thanks,

--
Jonathan Cameron

#31 lore raw
In reply to Jonathan Cameron
Quoted 11 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

Thinking about this, basically we have an event buffer and management system that could live in drivers/event. It manages the event sources and buffering, including tagging the data and buffering it ready for consumption.

One thing I'd like to see is that the reading application should be able to choose to have events aggregated or not, so that if a few events turn up very fast it can choose to take them as one single item.

Quoted 4 lines

Key features of the subsystem include:

* Provision of sysfs access for direct reading from devices (similar to hwmon but without the buffering / update rate restrictions)

Having an update rate and buffering is possibly useful, given that if you have say a number of ADC inputs but only one converter, then it is possible that there will be some form of resource starvation.

Quoted 15 lines

* Provision of chrdevs through which events may be passed to userspace in a similar fashion to the input subsystem. These events may be anything from hardware thresholds set on the sensor itself to sw / hw ring buffer event notifications (50% full etc).

* Provision of access via chrdevs to hardware ring buffers on devices that provide them.

* Software ring buffer support to allow semi regular capture of data form the device. Typically this will be driven from either datardy events, or from a periodic timer interrupt (to this end a very simple wrapper for periodic RTC's is included. This will move to more generic timer interfaces as and when they become available. For now available rtc's must be registered with the subsystem via the industrialio_register_ptimer function form within a board init.

Signature · 4 lines
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.
#32 lore raw

[Patch 5/4] IndustrialIO subsystem very early cut of documentation + userspace demo

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

A very early cut of some documentation for the industrialio subsystem. Also includes a small demo app for listening to a ring buffer event chrdev and reading from the access chrdev as appropriate.

Documentation/industrialio/overview.txt +117 -0
@@ -0,0 +1,117 @@
+Overview of the industrialio subsystem.
+
+Main Components
+
+Core
+
+industrialio-core.c contains the main registration code for devices using the
+subsytem. The key function is
+
+int iio_device_register(struct iio_dev *dev_info);
+
+This takes a structure containing a number of user specified variables which
+control how the device driver interacts with the subsystem components.
+
+This is a cut down version containing only those elements intended for direct
+access by drivers.
+
+struct iio_dev {
+/* device specific data */
+	void *dev_data;
+
+/* Modes the drivers supports */
+	int modes;
+
+/* Current mode */
+	int currentmode;
+
+/* The device for with which we are dealing */
+	struct device *dev;
+
+/* General attributes */
+	const struct attribute_group *attrs;
+
+/* Used to specify ownership of interrupt etc that may be created by iio */
+	struct module *driver_module;
+/* How many hardware interrupt lines are there */
+	int num_interrupt_lines;
+
+/* Event control attributes */
+	struct attribute_group *event_attrs;
+
+
+/* Software Ring Buffer (discussed in iio_ring.txt
+   - for now assuming only makes sense to have a single ring */
+	int ring_dimension;
+	int ring_bytes_per_datum;
+	int ring_length;
+
+/* enabling / disabling related functions.
+ * post / pre refer to relative to the change of current_mode. */
+	int (*ring_preenable)(struct iio_dev *);
+	int (*ring_postenable)(struct iio_dev *);
+	int (*ring_predisable)(struct iio_dev *);
+	int (*ring_postdisable)(struct iio_dev *);
+
+	void (*ring_poll_func)(void *private_data);
+
+	/* Device state lock.
+	 * Used to prevent simultaneous changes to device state.
+	 * In here rather than modules as some ring buffer changes must occur
+	 * with this locked.*/
+	struct mutex			mlock;
+
+};
+
+@dev_data - driver specific data.
+
+@modes - currently limited to combinations of INDIO_DIRECT_MODE,
+INDIO_RING_POLLED, INDIO_RING_DATA_RDY and INDIO_RING_HARDWARE_BUFFER
+
+All devices should probably support INDIO_DIRECT_MODE which means
+that sensor values may be read directly from files in sysfs.
+Typically this may be via single element files (x, y, z for accelerometers)
+or scan files (max1363 for example).
+
+INDIO_RING_POLLED currently uses periodic real time clocks to generate
+interrupts which are then used to poll the device. See iio_ring for more details
+
+INDIO_RING_DATA_RDY is for devices that supply a data ready interrupt when new
+data becomes available (eg lis3l02dq)
+
+INDIO_RING_HARDWARE_BUFFER is for devices with hardware ring buffers
+(eg. sca3000)
+
+@attrs general attribute group for both direct access attributes for reading
+from sensors and for sensor specific parameters of use to userspace (conversion
+factors etc).
+
+@driver_module - corresponds to OWNER within the driver.  This is to ensure
+any interrupts etc requested are registered to the relevant module rather than
+iio.
+
+@num_interrupt_lines - does what it says on the tin.  Most devices only have one
+but I have seen ones with separate lines for data ready signals from motion
+detection etc.
+
+@event_attrs - sysfs attributes which control whether particular events (read
+interrupts from the point of view of the sensor) are enabled or not.  If they
+are up to 10 events will be queued on the related chrdev for reading by
+userspace code.
+
+Ring parameters and functions are covered in iio_ring.txt.
+
+@mlock - device configuration lock. Note it is the responsibility of drivers
+to get this lock if they wish to change parameters which may effect ring buffer
+capture (changing scan modes for example.)
+
+
+What happens when a iio_dev is registered.
+
+1) Unique id obtained.
+2) Sysfs direct read and configuration elements registered
+3) Device event (sensor interrupts) registered.
+4) If software ring to be used, setup the ring but don't actually allocate.
+   This occurs on first enabled / when reenabled after parameter change.
+4) If polled ring get a periodic timer.
+
Documentation/industrialio/iio_ring.txt +91 -0

91 lines not on this page · raw message

Documentation/industrialio/TestRingMax1363.c +106 -0

106 lines not on this page · raw message

#33 lore raw
In reply to Jonathan Cameron
Quoted 11 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

Could this be done using UIO instead?

#34 lore raw
Quoted 11 lines · Jan Engelhardt
Quoted 8 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

Firstly thanks to all the people who have contributed to the discussion of this in the past.

Quoted 7 lines
Quoted 4 lines

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

Could this be done using UIO instead?

No, this lot is directed towards devices that use serial buses such as i2c and SPI whereas
UIO is, from my admittedly cursory knowledge, intended for devices which can have
their control registers memory mapped into user space.

Looks like I need to be a bit more specific in my descriptions. It's terrible but as all the
sensors etc I work with are connected like this it never occurred to me to make it
explicit in the description.
--
Jonathan

#35 lore raw
Quoted 17 lines · Jonathan Cameron
Quoted 16 lines · Jan Engelhardt
Quoted 12 lines · Jonathan Cameron

Dear All,

The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135

Firstly thanks to all the people who have contributed to the discussion of this in the past.

In brief the intention is provide a kernel subsystem directed towards the handling on sensors (and later related output devices) such as ADC's, accelerometers and many others.

Quoted 4 lines
Quoted 2 lines

Could this be done using UIO instead?

No, this lot is directed towards devices that use serial buses such as i2c and SPI

[...]

There is spidev driver to do SPI in userspace. But no, it does not mean that we can always use UIO. For example, we can't use UIO when we want to process IRQs (think touchscreen pen-down irq), or better, we anyway will need to write UIO-kernel helper for this.

So, sometimes it's just more convenient to do things in kernel.

Signature · 3 lines
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2