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

Jonathan Cameron
2008-07-24T17:57:32Z

From: Jonathan Cameron <jic23@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;
+
+};
+
+@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.
+

--- 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
+
+@ring_bytes_per_datum Number of bytes per 'reading', this includes timestamp
+
+@ring_length Number of readings in the ring
+
+The four functions are concerned with behaviour around the point where the
+device mode actually switches.
+@ring_preenable - usually things like disabling unwanted (sensor side)
+ interrupts.
+
+@ring_postenable - usually actually enabling the data ready generation if
+ appropriate.
+
+@ring_predisable - usually disabling data ready generation
+@ring_postdisable - restoring anything disable before the the ring came into
+ use.
+
+@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;
+}