Staging: IIO: Initial documentation

This needs considerably more work, all comments / suggestions
welcomed.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Jonathan Cameron 2009-08-18 18:06:32 +01:00 committed by Greg Kroah-Hartman
parent 930bae8667
commit c57f1ba732
7 changed files with 600 additions and 0 deletions

View file

@ -0,0 +1,49 @@
IIO Device drivers
This is not intended to provide a comprehensive guide to writing an
IIO device driver. For further information see the drivers within the
subsystem.
The crucial structure for device drivers in iio is iio_dev.
First allocate one using:
struct iio_dev *indio_dev = iio_allocate_device();
The fill in the following.
indio_dev->dev.parent
the struct device associated with the underlying hardware.
indio_dev->num_interrupt_lines
number of event triggering hardware lines the device has.
indio_dev->event_attrs
attributes used to enable / disable hardware events - note the
attributes are embedded in iio_event_attr structures with an
associated iio_event_handler which may or may note be shared.
If num_interrupt_lines = 0, then no need to fill this in.
indio_dev->attrs
general attributes such as polled access to device channels.
indio_dev->dev_data
private device specific data.
indio_dev->driver_module
typically set to THIS_MODULE. Used to specify ownership of some
iio created resources.
indio_dev->modes
whether direct access and / or ring buffer access is supported.
Once these are set up, a call to iio_device_register(indio_dev),
will register the device with the iio core.
Worth noting here is that, if a ring buffer is to be used, it can be
allocated prior to registering the device with the iio-core, but must
be registered afterwards (otherwise the whole parentage of devices
gets confused)
On remove iio_device_unregister(indio_dev) will remove the device from
the core, and iio_free_device will clean up.

View file

@ -0,0 +1,159 @@
/* IIO - useful set of util functionality
*
* 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.
*/
#define IIO_EVENT_CODE_RING_50_FULL 200
#define IIO_EVENT_CODE_RING_75_FULL 201
#define IIO_EVENT_CODE_RING_100_FULL 202
struct iio_event_data {
int id;
__s64 timestamp;
};
inline char *find_ring_subelement(const char *directory, const char *subelement)
{
DIR *dp;
const struct dirent *ent;
int pos;
char temp[100];
char *returnstring;
dp = opendir(directory);
if (dp == NULL) {
printf("could not directory: %s\n", directory);
return NULL;
}
while (ent = readdir(dp), ent != NULL) {
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0) {
if (strncmp(ent->d_name, subelement, strlen(subelement)) == 0) {
int length = sprintf(temp, "%s%s%s", directory, ent->d_name, "/");
returnstring = malloc(length+1);
strncpy(returnstring, temp, length+1);
return returnstring;
}
}
}
return 0;
}
char *find_type_by_name(const char *name, const char *type)
{
const char *iio_dir = "/sys/class/iio/";
const struct dirent *ent;
int cnt, pos, pos2;
FILE *nameFile;
DIR *dp;
char thisname[100];
char temp[100];
char *returnstring = NULL;
struct stat Stat;
pos = sprintf(temp, "%s", iio_dir);
dp = opendir(iio_dir);
if (dp == NULL) {
printf("No industrialio devices available");
return NULL;
}
while (ent = readdir(dp), ent != NULL) {
cnt++;
/*reject . and .. */
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0) {
/*make sure it isn't a trigger!*/
if (strncmp(ent->d_name, type, strlen(type)) == 0) {
/* build full path to new file */
pos2 = pos + sprintf(temp + pos, "%s/", ent->d_name);
sprintf(temp + pos2, "name");
printf("search location %s\n", temp);
nameFile = fopen(temp, "r");
if (!nameFile) {
sprintf(temp + pos2, "modalias", ent->d_name);
nameFile = fopen(temp, "r");
if (!nameFile) {
printf("Failed to find a name for device\n");
return NULL;
}
}
fscanf(nameFile, "%s", thisname);
if (strcmp(name, thisname) == 0) {
returnstring = malloc(strlen(temp) + 1);
sprintf(temp + pos2, "");
strcpy(returnstring, temp);
return returnstring;
}
fclose(nameFile);
}
}
}
}
int write_sysfs_int(char *filename, char *basedir, int val)
{
int ret;
FILE *sysfsfp;
char temp[100];
sprintf(temp, "%s%s", basedir, filename);
sysfsfp = fopen(temp, "w");
if (sysfsfp == NULL)
return -1;
fprintf(sysfsfp, "%d", val);
fclose(sysfsfp);
return 0;
}
/**
* write_sysfs_string_and_verify() - string write, readback and verify
* @filename: name of file to write to
* @basedir: the sysfs directory in which the file is to be found
* @val: the string to write
**/
int write_sysfs_string_and_verify(char *filename, char *basedir, char *val)
{
int ret;
FILE *sysfsfp;
char temp[100];
sprintf(temp, "%s%s", basedir, filename);
sysfsfp = fopen(temp, "w");
if (sysfsfp == NULL)
return -1;
fprintf(sysfsfp, "%s", val);
fclose(sysfsfp);
sysfsfp = fopen(temp, "r");
if (sysfsfp == NULL)
return -1;
fscanf(sysfsfp, "%s", temp);
if (strcmp(temp, val) != 0) {
printf("Possible failure in string write %s to %s%s \n",
val,
basedir,
filename);
return -1;
}
return 0;
}
int read_sysfs_posint(char *filename, char *basedir)
{
int ret;
FILE *sysfsfp;
char temp[100];
sprintf(temp, "%s%s", basedir, filename);
sysfsfp = fopen(temp, "r");
if (sysfsfp == NULL)
return -1;
fscanf(sysfsfp, "%d\n", &ret);
fclose(sysfsfp);
return ret;
}

View file

@ -0,0 +1,171 @@
/* Industrialio test ring buffer with a lis3l02dq acceleromter
*
* 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.
*
* Assumes suitable udev rules are used to create the dev nodes as named here.
*/
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <linux/types.h>
#include <dirent.h>
#include "iio_util.h"
static const char *ring_access = "/dev/iio/lis3l02dq_ring_access";
static const char *ring_event = "/dev/iio/lis3l02dq_ring_event";
static const char *device_name = "lis3l02dq";
static const char *trigger_name = "lis3l02dq-dev0";
static int NumVals = 3;
static int scan_ts = 1;
static int RingLength = 128;
/*
* Could get this from ring bps, but only after starting the ring
* which is a bit late for it to be useful
*/
int size_from_scanmode(int numVals, int timestamp)
{
if (numVals && timestamp)
return 16;
else if (timestamp)
return 8;
else
return numVals*2;
}
int main(int argc, char **argv)
{
int i, j, k, toread;
FILE *fp_ev;
int fp;
char *data;
size_t read_size;
struct iio_event_data dat;
char *BaseDirectoryName,
*TriggerDirectoryName,
*RingBufferDirectoryName;
BaseDirectoryName = find_type_by_name(device_name, "device");
if (BaseDirectoryName == NULL) {
printf("Failed to find the %s \n", device_name);
return -1;
}
TriggerDirectoryName = find_type_by_name(trigger_name, "trigger");
if (TriggerDirectoryName == NULL) {
printf("Failed to find the %s\n", trigger_name);
return -1;
}
RingBufferDirectoryName = find_ring_subelement(BaseDirectoryName,
"ring_buffer");
if (RingBufferDirectoryName == NULL) {
printf("Failed to find ring buffer\n");
return -1;
}
if (write_sysfs_string_and_verify("trigger/current_trigger",
BaseDirectoryName,
(char *)trigger_name) < 0) {
printf("Failed to write current_trigger file \n");
return -1;
}
/* Setup ring buffer parameters */
if (write_sysfs_int("length", RingBufferDirectoryName,
RingLength) < 0) {
printf("Failed to open the ring buffer length file \n");
return -1;
}
/* Enable the ring buffer */
if (write_sysfs_int("ring_enable", RingBufferDirectoryName, 1) < 0) {
printf("Failed to open the ring buffer control file \n");
return -1;
};
data = malloc(size_from_scanmode(NumVals, scan_ts)*RingLength);
if (!data) {
printf("Could not allocate space for usespace data store\n");
return -1;
}
/* Attempt to open non blocking the access dev */
fp = open(ring_access, O_RDONLY | O_NONBLOCK);
if (fp == -1) { /*If it isn't there make the node */
printf("Failed to open %s\n", ring_access);
return -1;
}
/* Attempt to open the event access dev (blocking this time) */
fp_ev = fopen(ring_event, "rb");
if (fp_ev == NULL) {
printf("Failed to open %s\n", ring_event);
return -1;
}
/* Wait for events 10 times */
for (j = 0; j < 10; j++) {
read_size = fread(&dat, 1, sizeof(struct iio_event_data),
fp_ev);
switch (dat.id) {
case IIO_EVENT_CODE_RING_100_FULL:
toread = RingLength;
break;
case IIO_EVENT_CODE_RING_75_FULL:
toread = RingLength*3/4;
break;
case IIO_EVENT_CODE_RING_50_FULL:
toread = RingLength/2;
break;
default:
printf("Unexpecteded event code\n");
continue;
}
read_size = read(fp,
data,
toread*size_from_scanmode(NumVals, scan_ts));
if (read_size == -EAGAIN) {
printf("nothing available \n");
continue;
}
for (i = 0;
i < read_size/size_from_scanmode(NumVals, scan_ts);
i++) {
for (k = 0; k < NumVals; k++) {
__s16 val = *(__s16 *)(&data[i*size_from_scanmode(NumVals, scan_ts)
+ (k)*2]);
printf("%05d ", val);
}
printf(" %lld\n",
*(__s64 *)(&data[(i+1)*size_from_scanmode(NumVals, scan_ts)
- sizeof(__s64)]));
}
}
/* Stop the ring buffer */
if (write_sysfs_int("ring_enable", RingBufferDirectoryName, 0) < 0) {
printf("Failed to open the ring buffer control file \n");
return -1;
};
/* Disconnect from the trigger - writing something that doesn't exist.*/
write_sysfs_string_and_verify("trigger/current_trigger",
BaseDirectoryName, "NULL");
free(BaseDirectoryName);
free(TriggerDirectoryName);
free(RingBufferDirectoryName);
free(data);
return 0;
}

View file

@ -0,0 +1,62 @@
Overview of IIO
The Industrial I/O subsytem is intended to provide support for devices
that in some sense are analog to digital convertors (ADCs). As many
actual devices combine some ADCs with digital to analog convertors
(DACs) the intention is to add that functionality at a future date
(hence the name).
The aim is to fill the gap between the somewhat similar hwmon and
input subsystems. Hwmon is very much directed at low sample rate
sensors used in applications such as fan speed control and temperature
measurement. Input is, as it's name suggests focused on input
devices. In some cases there is considerable overlap between these and
IIO.
A typical device falling into this category would be connected via SPI
or I2C.
Functionality of IIO
* Basic device registration and handling. This is very similar to
hwmon with simple polled access to device channels via sysfs.
* Event chrdevs. These are similar to input in that they provide a
route to user space for hardware triggered events. Such events include
threshold detectors, free-fall detectors and more complex action
detection. They events themselves are currently very simple with
merely an event code and a timestamp. Any data associated with the
event must be accessed via polling. Note a given device may have one
or more event channel. These events are turned on or off (if possible)
via sysfs interfaces.
* Hardware ring buffer support. Some recent sensors have included
fifo / ring buffers on the sensor chip. These greatly reduce the load
on the host CPU by buffering relatively large numbers of data samples
based on an internal sampling clock. Examples include VTI SCA3000
series and Analog Device ADXL345 accelerometers. Each ring buffer
typically has an event chrdev (similar to the more general ones above)
to pass on events such as buffer 50% full and an access chrdev via
which the raw data it self may be read back.
* Trigger and software ring buffer support. In many data analysis
applications it it useful to be able to capture data based on some
external signal (trigger). These triggers might be a data ready
signal, a gpio line connected to some external system or an on
processor periodic interrupt. A single trigger many initialize data
capture or reading from a number of sensors. These triggers are
used in iio to fill software ring buffers acting in a very similar
fashion to the hardware buffers described above.
Other documentation:
userspace.txt - overview of ring buffer reading from userspace
device.txt - elemennts of a typical device driver.
trigger.txt - elements of a typical trigger driver.
ring.txt - additional elements required for ring buffer support

View file

@ -0,0 +1,61 @@
Ring buffer support within IIO
This document is intended as a general overview of the functionality
a ring buffer may supply and how it is specified within IIO. For more
specific information on a given ring buffer implementation, see the
comments in the source code. Note that the intention is to allow
some drivers to specify ring buffers choice at probe or runtime, but
for now the selection is hard coded within a given driver.
A given ring buffer implementation typically embedded a struct
iio_ring_buffer and it is a pointer to this that is provided to the
IIO core. Access to the embedding structure is typically done via
container_of functions.
struct iio_ring_buffer contains 4 function pointers
(preenable, postenable, predisable, postdisable).
These are used to perform implementation specific steps on either side
of the core changing it's current mode to indicate that the ring buffer
is enabled or disabled (along with enabling triggering etc as appropriate).
Also in struct iio_ring_buffer is a struct iio_ring_access_funcs.
The function pointers within here are used to allow the core to handle
as much ring buffer functionality as possible. Note almost all of these
are optional.
mark_in_use, unmark_in_use
Basically indicate that not changes should be made to the ring
buffer state that will effect the form of the data being captures
(e.g. scan elements or length)
store_to
If possible, push data to ring buffer.
read_last
If possible get the most recent entry from the buffer (without removal).
This provides polling like functionality whilst the ring buffering is in
use without a separate read from the device.
rip_lots
The primary ring buffer reading function. Note that it may well not return
as much data as requested. The deadoffset is used to indicate that some
initial data in the data array is not guaranteed to be valid.
mark_param_changed
Used to indicate that something has changed. Used in conjunction with
request_update
If parameters have changed that require reinitialization or configuration of
the ring buffer this will trigger it.
get_bpd, set_bpd
Get/set the number of bytes for a given reading (single element, not sample set)
The value of bps (bytes per set) is created from a combination of this and the
enabled scan elements.
get_length / set_length
Get/set the number of sample sets that may be held by the buffer.
is_enabled
Query if ring buffer is in use
enable
Start the ring buffer.

View file

@ -0,0 +1,38 @@
IIO trigger drivers.
Many triggers are provided by hardware that will also be registered as
an IIO device. Whilst this can create device specific complexities
such triggers are registered with the core in the same way as
stand-alone triggers.
struct iio_trig *trig = iio_allocate_trigger();
allocates a trigger structure. The key elements to then fill in within
a driver are:
trig->control_attrs
Any sysfs attributes needed to control parameters of the trigger
trig->private_data
Device specific private data.
trig->owner
Typically set to THIS_MODULE. Used to ensure correct
ownership of core allocated resources.
trig->name
A unique name for the trigger.
When these have been set call:
iio_trigger_register(trig);
to register the trigger with the core, making it available to trigger
consumers.
Trigger Consumers
Currently triggers are only used for the filling of software ring
buffers and as such any device supporting INDIO_RING_TRIGGERED has the
consumer interface automatically created.

View file

@ -0,0 +1,60 @@
Userspace access to IIO
Example, ST Microelectronics LIS3L02DQ accelerometer.
Typical sysfs entries (pruned for clarity)
/sys/class/iio
device0 - iio_dev related elements
name - driver specific identifier (here lis3l02dq)
accel_x - polled (or from ring) raw readout of acceleration
accel_x_gain - hardware gain (calibration)
accel_x_offset - hardware offset (calibration)
available_sampling_frequency
available_sampling_frequency - what options are there
sampling_frequency - control of internal sampling frequency
scan_elements - controls which channels will be stored in the ring buffer
scan_en_accel_x
scan_en_accel_y
scan_en_timestamp
device - link to underlying hardware device
uevent - udev related element
thresh - unified threshold used for detection on all axis
event_line0_sources - which events are enabled
accel_x_high - enable x axis high threshold event
accel_x_low - enable x axis low threshold event
event_line0 - event interface
dev - major:minor for the chrdev (note major allocation dynamic)
trigger - consumer attachement
current_trigger - name based association with a trigger
ring_buffer0 - ring buffer interface
bps - byptes per sample (read only), dependant on scan element selection
length - (rw) specificy length fo software ring buffer (typically ro in hw case)
ring_enable - turn the ring on. If its the first to be enabled attached to this
trigger will also enable the trigger.
ring_access0
dev - major:minor for ring buffer access chrdev
ring_event_line0
dev - major:minor for ring buffer event chrdev
trigger0 - data ready trigger elements
name - unqiue name of trigger
Udev will create the following entries under /dev by default:
ring_access0 - ring access chrdev
ring_event0 - ring event chrdev
event_line0 - general event chrdev.
For the example code we assume the following rules have been used to ensure
unique and consistent naming of these for the lis3l02dq in question:
KERNEL="ring_event_line*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_ring_event"
KERNEL="event_line*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_event"
KERNEL="ring_access*", ID="spi1.0", DRIVER="lis3l02dq", NAME="iio/lis3l02dq_ring_access"
The files, lis3l02dqbuffersimple.c and iio_util.h in this directory provide an example
of how to use the ring buffer and event interfaces.