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

Jonathan Cameron Jonathan.Cameron at gmail.com
Thu Jul 24 19:57:24 CEST 2008


From: Jonathan Cameron <jic23 at cam.ac.uk>

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.
--- a/Documentation/industrialio/overview.txt	1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/industrialio/overview.txt	2008-07-24 18:37:56.000000000 +0100
@@ -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;
+
+};
+
+ at dev_data - driver specific data.
+
+ at 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)
+
+ at 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).
+
+ at 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.
+
+ at 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.
+
+ at 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.
+
+ at 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.
+

--- a/Documentation/industrialio/iio_ring.txt	1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/industrialio/iio_ring.txt	2008-07-24 18:45:04.000000000 +0100
@@ -0,0 +1,91 @@
+Industrialio subsystem ring buffers
+
+The industrial io subsystem supports both hardware (eg. sca3000) and software
+(eg. max1363 and lis3l02dq) ring buffers.
+
+For both types a chrdev is used to provide events to userspace. These merely
+contain an id and a timestamp.
+
+This is used to provide notifications of the ring having reached a certain level
+(50%, 75%, 100%).
+
+Direct access to the contents of the ring buffer is available via a second
+dev. The data output is pretty much raw device readings, so a userspace function
+is needed to convert these into appropriate SI units.  This function should be
+provided in a device specific header if appropriate.
+
+The hardware ring buffer simply implements the above functionality by pull data
+directly from the device on demand (sca3000).
+
+
+The software ring buffer is somewhat more complex.
+
+The design considerations for this are:
+
+1) Writing should be as latency free as possible (preferably lock free)
+2) As few readings as possible should be missed (ideally none - but as
+   we aren't dealing with a realtime OS some will occasionally be lost).
+3) Reading does not lock the buffer but instead takes a copy then validate
+   what data is 'clean' approach.
+4) The filling of the buffer should be either driver by the device (datardy)
+   or if that is not possible via a periodic time source.
+5) All latencies should be as small as possible(wishful thinking ;)
+
+The code in industrialio-ring.c meets most of these requirements and hopefuly
+does not have any critical failure cases.
+
+A number of pointers into a static array are maintained.
+
+write_p - the next location to write to.
+read_p - the oldest location from which we may read (start point for copying)
+last_written_p - the newest location from which we may read (used to provide
+	       direct access whilst the ring buffer is in use, without adding
+	       to the communications with the sensor.
+
+half_p - Kept half the length of the buffer behind the write pointer and used
+       in conjunction with read_p to trigger an event when the buffer is half
+       full.
+
+The events are designed to escalate until we reach the point of buffer 100%
+full. Thus a single event has it's code changed when it becomes outdated.
+
+
+The other interesting bit is reading data from the ring. Currently this is via
+normal file reads rather than mmaping the ring buffer.
+
+1) A copy of the ring buffer between read_p and write_p is made.  This is done
+without locking the buffer in anyway so the data is not guaranteed to have not
+been changed by subsequent writes.
+
+2) The value of read_p after the copy is used to provide a worst case location
+for where we have clean data from.  There is an obvious nasty case of the read
+pointer having wrapped all the way round the buffer. For now we assume the
+capture rate is slow enough that this will not have happened.
+
+Only the valid data is then sent to userspace.
+
+
+What the iio_dev ring parameters are
+
+ at ring_bytes_per_datum Number of bytes per 'reading', this includes timestamp
+
+ at ring_length Number of readings in the ring
+
+The four functions are concerned with behaviour around the point where the
+device mode actually switches.
+ at ring_preenable - usually things like disabling unwanted (sensor side)
+		interrupts.
+
+ at ring_postenable - usually actually enabling the data ready generation if
+		 appropriate.
+
+ at ring_predisable - usually disabling data ready generation
+ at ring_postdisable - restoring anything disable before the the ring came into
+		  use.
+
+ at ring_poll_func - For perioidic timer based rings, this function is called on
+		each timer interrupt. Reads from the device and pushes the
+		data into the ring. Also tends to grab a timestamp at the
+		point likely to be as close as possible to when the data
+		was acquired. Sensor specific offsets can compensate for
+		some fixed lags (particularly at low bus speeds).
--- a/Documentation/industrialio/TestRingMax1363.c	1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/industrialio/TestRingMax1363.c	2008-07-24 18:48:56.000000000 +0100
@@ -0,0 +1,106 @@
+/* Industrialio test ring buffer with a max1238
+ *
+ * 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.
+ * Little tool to poke character devices associated with ring buffer and see
+ * what events are coming out.
+ *
+ * Todo: Make this more adapatable - e.g. allow specification of which
+ *       ring to poke.
+ *
+ * Clearly this needs a lot of work if it going to be a coherent / general
+ * piece of example code.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <linux/types.h>
+
+
+struct iio_event_data {
+	int id;
+	__s64 timestamp;
+};
+int main(int argc, char **argv)
+{
+	FILE  *sysfsfp, *fp_ev;
+	int fp;
+	char data[20000];
+	size_t read_size;
+	int i, j, k;
+	int minor, minor_ev;
+	char name[100];
+	char name2[100];
+	char command[100];
+	char temp[100];
+	int pos;
+	struct iio_event_data dat;
+	int device_no;
+
+	if (argc == 1)
+		return -1;
+	device_no = atoi(argv[1]);
+	pos = sprintf(temp, "/sys/class/industrialio/industrialio%d/device/",
+		      device_no);
+	sprintf(temp + pos, "ring_buffer0_access_minor");
+	sysfsfp = fopen(temp, "r");
+	if (sysfsfp == NULL) {
+		printf("failed to open minor stuff \n");
+		return -1;
+	}
+
+	fscanf(sysfsfp, "%d\n", &minor);
+	sprintf(name, "/dev/indring%d", minor);
+
+	fclose(sysfsfp);
+	sprintf(temp + pos, "ring_buffer0_ev_minor");
+	sysfsfp = fopen(temp, "r");
+	if (sysfsfp == NULL) {
+		printf("failed to open minor stuff \n");
+		return -1;
+	}
+
+	fscanf(sysfsfp, "%d\n", &minor_ev);
+	fclose(sysfsfp);
+	sprintf(name2, "/dev/indringev%d", minor_ev);
+
+	fp = open(name, O_RDONLY | O_NONBLOCK);
+	if (fp == -1) {
+		sprintf(command, "mknod %s c 244 %d; mknod %s c 244 %d ",
+			name, minor, name2, minor_ev);
+		system(command);
+		fp = open(name, O_RDONLY | O_NONBLOCK);
+		if (fp == -1) {
+			printf("Unable to open %s\n", name);
+			return -1;
+		}
+	}
+	fp_ev = fopen(name2, "rb");
+	if (fp_ev == NULL)
+		printf("bug opening %s\n", name2);
+	/* Wait for events 10 times */
+	for (j = 0; j < 10; j++) {
+		read_size = fread(&dat, 1, sizeof(struct iio_event_data),
+				  fp_ev);
+		printf("event code received: %d\n", dat.id);
+		read_size = read(fp, (char *)(data), 100*(12+8));
+		if (read_size == -EAGAIN)
+			printf("nothing available \n");
+
+		/* print a small amount of data */
+		for (i = 0; i < 10; i++) {
+			for (k = 0; k < 12; k++)
+				printf("%d ",
+				       ((int)((data[i*32 + (k)*2 + 0]
+					       & 0x0F) << 8)
+					+ ((int)((data[i*32 + (k)*2 + 1])))));
+			printf(" %lld\n", *(__s64 *)(&data[i*32 + 12*2]));
+		}
+	}
+	return 0;
+}




More information about the lm-sensors mailing list