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:
parent
930bae8667
commit
c57f1ba732
|
@ -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.
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
Loading…
Reference in New Issue