Skip to contentSkip to conversation

RFC: IIO v3 patchset

PATCH In review Jonathan Cameron 2 participants 16 messages Dec 1, 2008 – Dec 2, 2008

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

cover RFC: IIO v3 patchset

Dear All,

Sorry this has been so long in coming, it has been a busy couple of months.

Summary of what IIO is about:

The Industrial I/O (name open to suggestions) subsystem is the result of discussions on LKML about suitable facilities in the kernel for the handling of Analog to Digital Converters (ADCs) and the huge classes of sensor that act in similar fashion (digital accelerometers etc). The intention is that this may also provide a home for DACs as a lot of chips have both functions. The sort of devices we are talking typically communicate over I2C or SPI buses though drivers for rs232 devices etc are definitely on the cards. Basically we are interested in devices where direct memory mapped access is not possible.

My particular motivation was to produce an adaptable data capture platform and, seeing as that's what is paying the bills, it has been the initial focus of my efforts. The RFCs below include the question of what other facilities should be included as options in this system.

For discussion of why these don't fit within existing subsystems see
http://lkml.org/lkml/2008/5/20/135 and the rest of the associated thread.

The design is intended to allow a disparate set of drivers providing whatever subset of functionality people have uses for. Hence, the intention is that it is not necessary or even desirable to support as much as the example drivers provided here. The system is designed to allow everything from simple drivers providing sysfs based reading of individual channels to the level of complexity seen in the sca3000 accelerometer driver found in patch 11.
To this end any final submission will include at least one example of a minimal driver.

I'm still fairly new to kernel coding so have doubtlessly made many mistakes and non optimal decisions within this code. So, whilst this is intended primarily to feed a discussion of the overall framework, I would be grateful to receive any more specific comments on the code! Particularly welcome are any suggestions that will result in reductions in code length without loss of functionality as this is still rather larger than would be ideal!

As ever, thanks to the numerous people who commented on the last version posted and those who have been testing the various intermediate versions (Marcelo Pias in particular.)

Contents:

[1/13] The Industrial I/O core:
The core registration and event handling functionality of IIO.

[2/13] The Industrial I/O core - ring buffer support:
Adds ring buffer handling to the IIO core.

[3/13] IIO: Software ring buffer implementation.
An example software ring buffer module.

[4/13] The Industrial I/O core - trigger support
Adds trigger support to the IIO core.

[5/13] IIO: Periodic real time clock based trigger.
An example of IIO trigger supplying driver.

[6/13] IIO: Proof of concept GPIO based IIO trigger
A very simple GPIO based trigger suitable for external synchronization.

[7/13] IIO: Maxim MAX1363 driver
Driver for the the MAX1363 ADC chip (core functionality)

[8/13] IIO: Maxim MAX1363 driver (ring buffer support)
Add triggered software ring buffer support to the driver

[9/13] IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (core)
Driver for the ST LIS3L02DQ accelerometer (SPI only)

[10/13] IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (ring)
Add triggered software ring buffer support and a data ready based trigger to the device.

[11/13] IIO: VTI SCA3000 3d accelerometer driver.
Driver for the VTI SCA3000 range of accelerometers.

[12/13] IIO: Documentation
Whilst most documentation related to IIO is contained as kernel doc comments within the source code, a brief overview of what it does and discussions of elements of individual drivers are provided in Documentation/industrialio.

[13/13] IIO: Example user space ring buffer access.
Simple example program to illustrate how a user space application can interact with the ring buffer functionality of IIO (this includes event handling). The example used uses the datardy trigger of the lis3l02dq to drive the filling of a software ring buffer from the lis3l02dq device.

Overview of changes:

* Introduction of trigger drivers to allow more flexible sampling strategies and separate out the periodic RTC element (as only one example of a trigger driver.) New triggers provided as example / proofs of concept are GPIO based for external sync and data ready on the lis3l02dq (to act as example of a unified iio:trigger / iio:device driver)

* Far more modular design:
- The core now acts as a registration system providing sysfs class handling and registration of chrdevs. The registration is relatively heavy weight pulling in other core elements such as ring buffers as appropriate.
- Ring buffers are now an optional element (compile to near zero) if not enabled. Intention is that it will be possible to select most appropriate implementation at run time though for now this is hard coded in the drivers (as there is only one software implementation)
- Separate library module provides a fairly generic ring buffer implementation.
- Drivers are now spit into core component (direct access to channels and event chrdevs) and ring buffer related code.
- The intention is that, if appropriate, other additional optional elements will be provided via a similar approach. This would include interfacing to hwmon and input subsystems.
- Hopefully this will make code review a much simpler exercise than the behemoth patches sent last time!

Requests for comment:

Big general question - Is it sensible to try and have unified drivers providing all functionality users may want from a given sensor chip? This came up in the previous discussion but I'm still not sure we have a definitive answer. Basically, is it sensible to have the iio-core provide hwmon interfaces (for devices that are used as such complete with caching etc) or input interfaces (for things like accelerometers that are increasingly used as input devices.)
It would be difficult to say the least to run these at the same time as for example the ring buffering elements.

Is it worthwhile allowing multiple triggers for single device? This may greatly increase the individual driver complexity and not actually be that useful.

Devices supplied triggers. These may be used to trigger other devices. Tricky to set this up unless they are already set to trigger themselves. A clean means of specifying restrictions on trigger / device associations is needed.

Consider optimal approach to configuring scan modes.
* Somewhat clunky approach used with max1363 - basically associate formalized text label with each scan mode. Somewhat chip specific approach.

* Scan element selection as in lis3l02dq and sca3000 drivers. Selectable sysfs elements represent each signal that is available. Works well in this case where any combination is possible but complex in the cases with exclusion problems such as unipolar and bipolar for the max1363. Also nasty search functionality and masking of results would be needed to apply this to chips like the max1363 where not every combination is directly possible.

Whilst it would be nice to clean this up, perhaps enforcing two rigid a set of rules here will just lead to fiddly unmaintainable code. Maybe one to reconsider down the line when many more device drivers are available to consider.

There are numerous RFCs concerned with more specific questions within the various patches.

TO DO:

There are still quite a few elements that I know need cleaning up in this code some of which are listed here.

Protection on many of the control interfaces. In some cases this will require input via board configs. For example the maximum sampling rate of a sensor is often limited by a complex interaction of bus speed and available processing power. The easiest option would be to allow board configuration data to specify limits that the developer has tested and verified to give reasonable performance.

Provision of static sysfs elements to make things like conversion factors available in as generic a form as possible.

Drivers are all currently missing things like power management support which will ideally be in place before final submission.

Other drivers in development:

Analog Devices ADIS16350 inertial measurement unit.

Illustration of sysfs Interface:

This is just intended to give an idea of what is available for a typical driver (lis3l02dq) under /sys/class/industrialio/ (There some RFCs after)

ls -R industrialio/
industrialio/:
iio:device0 iio:trigger0

industrialio/iio:device0:

accel_x                       event_line0_sources
accel_x_gain                  name
accel_x_offset                power
accel_y                       ring_buf0_acc_minor
accel_y_gain                  ring_buf0_ev_minor
accel_y_offset                ring_buffer
accel_z                       sampling_frequency
accel_z_gain                  scan_elements
accel_z_offset                subsystem
available_sampling_frequency  thresh
device                        trigger
event_line0_minor             uevent

industrialio/iio:device0/event_line0_sources:

accel_x_high  accel_y_high  accel_z_high
accel_x_low   accel_y_low   accel_z_low

industrialio/iio:device0/power:
wakeup

industrialio/iio:device0/ring_buffer:
bps length ring_enable

industrialio/iio:device0/scan_elements:
scan_en_accel_x scan_en_accel_y scan_en_accel_z scan_en_timestamp

industrialio/iio:device0/trigger:
current_trigger

industrialio/iio:trigger0:
device name power subsystem uevent

industrialio/iio:trigger0/power:
wakeup

RFC (on sysfs subsystem):

Add an interface under the trigger to list which devices have it set as their 'current_trigger'? (useful for debugging if nothing else). Clearly this particular trigger driver only provides a name element.

Directory structure: Basically any suggestions for a more coherent and intuitive structure would be welcomed.

All other comments welcomed!

--
Jonathan Cameron

#1 lore raw

PATCH 1/13 RFC: IIO core functionality

From: Jonathan Cameron <[email protected]>

The Industrial I/O core functionality.

--

This patch contains the core functionality of the industrial I/O (IIO) subsystem. This handling of sysfs registration of devices and allocation of such things as character devices + the handling of any access to the major device number associated with IIO.

Principle changes since the last version are concerned with making this considerably more modular so as to make the subsystem more adaptable and hopefuly easier to review.

If this component is used with a driver then the available functionality is limited to sysfs access to readings and control along with chrdev based event interfaces to transmit hardware events (typically interrupts) to userspace programs.

Whilst this may seem overly complex, it as pretty much as simple as I can manage whilst keeping it fairly adaptable. All suggestions for simplifications particularly welcome.

 drivers/Kconfig                           |    2
 drivers/Makefile                          |    1
 drivers/industrialio/Kconfig              |   12
 drivers/industrialio/Makefile             |    6
 drivers/industrialio/industrialio-core.c  |  907 ++++++++++++++++++++++++++++++
 include/linux/industrialio/chrdev.h       |   91 +++
 include/linux/industrialio/iio.h          |  440 ++++++++++++++
 include/linux/industrialio/ring_generic.h |   24
 include/linux/industrialio/sysfs.h        |  284 +++++++++
 include/linux/industrialio/trigger.h      |   68 ++
 10 files changed, 1835 insertions(+)
drivers/industrialio/industrialio-core.c new file +907 -0
new file mode 100644
index 0000000..88ad39d
@@ -0,0 +1,907 @@
+/* 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/industrialio/iio.h>
+#include <linux/industrialio/trigger.h>
+
+#define IIO_ID_PREFIX "iio:device"
+#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
+
+/* Temporary - I'll request one when we are near submission*/
+#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 __iio_state - internal iio subsystem state information.
+ * @fhs:	file operations for the various chrdevs. These are
+ *		all intially set to null in init.
+ **/
+struct __iio_state {
+	struct iio_handler *fhs[256];
+};
+
+static struct __iio_state iio_state;
+static DEFINE_SPINLOCK(iio_state_lock);
+
+ssize_t iio_scan_el_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_scan_el *this_el = to_iio_scan_el(attr);
+
+	return sprintf(buf, "%d\n",
+		       !!(indio_dev->scan_mask & this_el->mask));
+}
+EXPORT_SYMBOL(iio_scan_el_show);
+
+ssize_t iio_scan_el_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *buf,
+			  size_t len)
+{
+	int ret = 0;
+	bool state;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_scan_el *this_el = to_iio_scan_el(attr);
+
+	state = !(buf[0] == '0');
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) {
+		ret = -EBUSY;
+		goto error_ret;
+	}
+
+	if (!state && (indio_dev->scan_mask & this_el->mask)) {
+		indio_dev->scan_mask &= ~this_el->mask;
+		indio_dev->scan_count--;
+	} else if (state && !(indio_dev->scan_mask & this_el->mask)) {
+		indio_dev->scan_mask |= this_el->mask;
+		indio_dev->scan_count++;
+	}
+	if (this_el->set_state)
+		ret = this_el->set_state(this_el, indio_dev, state);
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+
+}
+EXPORT_SYMBOL(iio_scan_el_store);
+
+ssize_t iio_scan_el_ts_show(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", indio_dev->scan_timestamp);
+}
+EXPORT_SYMBOL(iio_scan_el_ts_show);
+
+ssize_t iio_scan_el_ts_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf,
+			     size_t len)
+{
+	int ret = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	bool state;
+	state = !(buf[0] == '0');
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) {
+		ret = -EBUSY;
+		goto error_ret;
+	}
+	indio_dev->scan_timestamp = state;
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+EXPORT_SYMBOL(iio_scan_el_ts_store);
+
+/* TODO Check if these need locking */
+void __iio_change_event(struct iio_detected_event_list *ev,
+			int ev_code,
+			s64 timestamp)
+{
+	ev->ev.id = ev_code;
+	ev->ev.timestamp = timestamp;
+}
+EXPORT_SYMBOL(__iio_change_event);
+
+/* Used both in the interrupt line put events and the ring buffer ones */
+
+/* Note that in it's current form someone has to be listening before events
+ * are queued. Hence a client MUST open the chrdev before the ring buffer is
+ * switched on.
+ */
+ int __iio_push_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 = 0;
+
+	/* 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);
+	}
+
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(__iio_push_event);
+
+int iio_push_event(struct iio_dev *dev_info,
+		   int ev_line,
+		   int ev_code,
+		   s64 timestamp)
+{
+	return __iio_push_event(&dev_info->event_interfaces[ev_line],
+			       ev_code, timestamp, NULL);
+}
+EXPORT_SYMBOL(iio_push_event);
+
+/* 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, *q;
+	s64 time_ns;
+	unsigned long flags;
+	spin_lock_irqsave(&int_info->ev_list_lock, flags);
+	if (list_empty(&int_info->ev_list)) {
+		spin_unlock_irqrestore(&int_info->ev_list_lock, flags);
+		return IRQ_NONE;
+	}
+
+
+	time_ns = iio_get_time_ns();
+	/* detect single element list*/
+	if (list_is_singular(&int_info->ev_list)) {
+		disable_irq_nosync(irq);
+		p = list_first_entry(&int_info->ev_list,
+				     struct iio_event_handler_list,
+				     list);
+		/* single event handler - maybe shared */
+		p->handler(dev_info, 1, time_ns, !(p->refcount > 1));
+	} else /* safe against list deletion? */
+		list_for_each_entry_safe(p, q, &int_info->ev_list, list) {
+			disable_irq_nosync(irq);
+			p->handler(dev_info, 1, time_ns, 0);
+		}
+	spin_unlock_irqrestore(&int_info->ev_list_lock, flags);
+	return IRQ_HANDLED;
+}
+
+/* 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;
+	}
+	printk(KERN_INFO  "initializing interrupt line %d\n", line_number);
+	spin_lock_init(&dev_info->interrupts[line_number]->ev_list_lock);
+	INIT_LIST_HEAD(&dev_info->interrupts[line_number]->ev_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 might 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);
+
+/* This turns up an awful lot */
+ssize_t iio_read_const_attr(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string);
+}
+EXPORT_SYMBOL(iio_read_const_attr);
+
+/* 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);
+
+/* Reference counted add and remove */
+int iio_add_event_to_list(struct iio_event_handler_list *el,
+			  struct list_head *head)
+{
+	struct iio_interrupt *inter
+		= container_of(head, struct iio_interrupt, ev_list);
+	unsigned long flags;
+	/* take mutex to protect this element */
+	mutex_lock(&el->exist_lock);
+	if (el->refcount == 0) {
+		spin_lock_irqsave(&inter->ev_list_lock, flags);
+		/*take spinlock*/
+		list_add(&el->list, head);
+		spin_unlock_irqrestore(&inter->ev_list_lock, flags);
+	}
+	el->refcount++;
+	mutex_unlock(&el->exist_lock);
+	return 0;
+}
+EXPORT_SYMBOL(iio_add_event_to_list);
+
+int iio_remove_event_from_list(struct iio_event_handler_list *el,
+			       struct list_head *head)
+{
+	struct iio_interrupt *inter
+		= container_of(head, struct iio_interrupt, ev_list);
+	unsigned long flags;
+	mutex_lock(&el->exist_lock);
+	el->refcount--;
+	if (el->refcount == 0) {
+		spin_lock_irqsave(&inter->ev_list_lock, flags);
+		/* take spinlock */
+		list_del_init(&el->list);
+		spin_unlock_irqrestore(&inter->ev_list_lock, flags);
+	}
+	mutex_unlock(&el->exist_lock);
+	return 0;
+}
+EXPORT_SYMBOL(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 (more or less)
+ */
+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  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;
+}
+
+static const struct file_operations iio_fops = {
+	.owner = THIS_MODULE,
+	.open = iio_open_file,
+};
+
+/* File ops for event character device files */
+ssize_t iio_event_chrdev_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_event_chrdev_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_event_chrdev_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_event_chrdev_fileops = {
+	.read =  iio_event_chrdev_read,
+	.release = iio_event_chrdev_release,
+	.open = iio_event_chrdev_open,
+	.owner = THIS_MODULE,
+};
+
+ssize_t iio_show_attr_minor(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_chrdev_minor_attr *_attr
+		= to_iio_chrdev_minor_attr(attr);
+	return sprintf(buf, "%d\n", _attr->minor);
+}
+
+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_event_chrdev_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, "industrialio");
+	class_unregister(&iio_class);
+}
+
+static int iio_device_register_sysfs(struct iio_dev *dev_info)
+{
+	int ret = 0;
+
+	dev_info->sysfs_dev = device_create(&iio_class,
+					    dev_info->dev,
+					    0,
+					    dev_info,
+					    IIO_ID_FORMAT,
+					    dev_info->id);
+	if (IS_ERR(dev_info->sysfs_dev)) {
+		/* what would correct error here be?*/
+		ret = -EINVAL;
+		dev_err(dev_info->dev, "Failed in device create \n");
+		goto error_ret;
+	}
+
+	/* register attributes */
+	ret = sysfs_create_group(&dev_info->sysfs_dev->kobj, dev_info->attrs);
+	if (ret) {
+		dev_err(dev_info->dev, "Failed to register sysfs hooks\n");
+		goto error_free_sysfs_device;
+	}
+
+	if (dev_info->scan_el_attrs) {
+		ret = sysfs_create_group(&dev_info->sysfs_dev->kobj,
+					 dev_info->scan_el_attrs);
+		if (ret)
+			dev_err(dev_info->dev,
+				"Failed to add sysfs scan els\n");
+	}
+	return ret;
+
+error_free_sysfs_device:
+	device_unregister(dev_info->sysfs_dev);
+error_ret:
+	return ret;
+}
+
+static void iio_device_unregister_sysfs(struct iio_dev *dev_info)
+{
+	if (dev_info->scan_el_attrs)
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   dev_info->scan_el_attrs);
+
+	sysfs_remove_group(&dev_info->sysfs_dev->kobj, dev_info->attrs);
+	device_unregister(dev_info->sysfs_dev);
+}
+
+static 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;
+}
+
+static void iio_device_unregister_id(struct iio_dev *dev_info)
+{
+	spin_lock(&iio_idr_lock);
+	idr_remove(&iio_idr, dev_info->id);
+	spin_unlock(&iio_idr_lock);
+}
+
+static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
+{
+	int ret;
+	/*p for adding, q for removing */
+	struct attribute **attrp, **attrq;
+
+	if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) {
+		attrp = dev_info->event_conf_attrs[i].attrs;
+		while (*attrp) {
+			ret =  sysfs_add_file_to_group(&dev_info->sysfs_dev
+						       ->kobj,
+						       *attrp,
+						       dev_info
+						       ->event_attrs[i].name);
+			if (ret)
+				goto error_ret;
+			attrp++;
+		}
+	}
+	return 0;
+
+error_ret:
+	attrq = dev_info->event_conf_attrs[i].attrs;
+	while (attrq != attrp) {
+			sysfs_remove_file_from_group(&dev_info->sysfs_dev->kobj,
+					     *attrq,
+					     dev_info->event_attrs[i].name);
+		attrq++;
+	}
+
+	return ret;
+}
+
+static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info,
+						  int i)
+{
+	struct attribute **attrq;
+
+	if (dev_info->event_conf_attrs
+		&& dev_info->event_conf_attrs[i].attrs) {
+		attrq = dev_info->event_conf_attrs[i].attrs;
+		while (*attrq) {
+			sysfs_remove_file_from_group(&dev_info->sysfs_dev->kobj,
+						     *attrq,
+						     dev_info
+						     ->event_attrs[i].name);
+			attrq++;
+		}
+	}
+
+	return 0;
+}
+
+static int iio_device_register_eventset(struct iio_dev *dev_info)
+{
+	int ret = 0, i, j;
+
+	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->sysfs_dev);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Could not get chrdev interface\n");
+			goto error_free_setup_ev_ints;
+		}
+	}
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+		snprintf(dev_info->event_interfaces[i]._attrname, 20,
+			"event_line%d_sources", i);
+		dev_info->event_attrs[i].name
+			= (const char *)
+			(dev_info->event_interfaces[i]._attrname);
+		ret = sysfs_create_group(&dev_info->sysfs_dev->kobj,
+					 &dev_info->event_attrs[i]);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Failed to register sysfs for event attrs");
+			goto error_remove_sysfs_interfaces;
+		}
+	}
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+		ret = __iio_add_event_config_attrs(dev_info, i);
+		if (ret)
+			goto error_unregister_config_attrs;
+	}
+
+
+
+	return 0;
+
+error_unregister_config_attrs:
+	for (j = 0; j < i; j++)
+		__iio_remove_event_config_attrs(dev_info, i);
+	i = dev_info->num_interrupt_lines - 1;
+error_remove_sysfs_interfaces:
+	for (j = 0; j < i; j++)
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   &dev_info->event_attrs[j]);
+
+	i = dev_info->num_interrupt_lines - 1;
+error_free_setup_ev_ints:
+	for (j = 0; j < i; j++)
+		iio_free_ev_int(&dev_info->event_interfaces[j],
+				dev_info->sysfs_dev);
+	kfree(dev_info->interrupts);
+error_free_event_interfaces:
+	kfree(dev_info->event_interfaces);
+error_ret:
+
+	return ret;
+}
+
+static 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++)
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   &dev_info->event_attrs[i]);
+
+	for (i = 0; i < dev_info->num_interrupt_lines; i++)
+		iio_free_ev_int(&dev_info->event_interfaces[i],
+					 dev_info->dev);
+	kfree(dev_info->event_interfaces);
+}
+
+
+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) {
+		dev_err(dev_info->dev, "Failed to get id\n");
+		goto error_nothing;
+	}
+
+/* Create sysfs device */
+	ret = iio_device_register_sysfs(dev_info);
+	if (ret) {
+		dev_err(dev_info->dev,
+			"Failed to register sysfs interfaces\n");
+		goto error_free_idr;
+	}
+
+/* Interrupt triggered events setup */
+	ret = iio_device_register_eventset(dev_info);
+	if (ret) {
+		dev_err(dev_info->dev,
+			"Failed to register event set \n");
+		goto error_free_sysfs;
+	}
+
+
+/* Ring buffer init if relevant */
+	if (dev_info->modes & (INDIO_RING_TRIGGERED
+			       | INDIO_RING_HARDWARE_BUFFER)) {
+		ret = iio_device_register_ring(dev_info, 0);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Failed to register ring");
+			goto error_free_eventset;
+		}
+	}
+
+/* Register ability to use triggers */
+	iio_device_register_trigger_consumer(dev_info);
+
+	return 0;
+
+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(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *dev_info)
+{
+	iio_device_unregister_trigger_consumer(dev_info);
+	if (dev_info->modes & (INDIO_RING_TRIGGERED
+			       | INDIO_RING_HARDWARE_BUFFER))
+		iio_device_unregister_ring(dev_info);
+	iio_device_unregister_eventset(dev_info);
+	iio_device_unregister_sysfs(dev_info);
+	iio_device_unregister_id(dev_info);
+}
+EXPORT_SYMBOL(iio_device_unregister);
+
+
+
+subsys_initcall(iio_init);
+module_exit(iio_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <[email protected]>");
+MODULE_DESCRIPTION("Industrial I/O core");
+MODULE_LICENSE("GPL");
include/linux/industrialio/iio.h new file +440 -0

440 lines not on this page · raw message

include/linux/industrialio/sysfs.h new file +284 -0

284 lines not on this page · raw message

include/linux/industrialio/chrdev.h new file +91 -0

91 lines not on this page · raw message

drivers/Kconfig +2 -0
index 2f557f5..ae35394 100644
@@ -106,5 +106,7 @@ source "drivers/uio/Kconfig"
 
 source "drivers/xen/Kconfig"
 
+source "drivers/industrialio/Kconfig"
+
 source "drivers/staging/Kconfig"
 endmenu
drivers/Makefile +1 -0
index fceb71a..41a7d1c 100644
@@ -67,6 +67,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 new file +12 -0
new file mode 100644
index 0000000..7be386c
@@ -0,0 +1,12 @@
+#
+# Industrial I/O subsytem configuration
+#
+
+menuconfig INDUSTRIALIO
+	tristate "Industrial I/O support"
+	---help---
+	  The industrial I/O subsystem provides a unified framework for
+	  drivers for many different types of embedded sensors using a
+	  number of different physical interfaces (i2c, spi etc). See
+	  Documentation/industrialio for more information.
+
drivers/industrialio/Makefile new file +6 -0
new file mode 100644
index 0000000..579ac30
@@ -0,0 +1,6 @@
+#
+# Makefile for the industrial I/O core.
+#
+
+obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
+industrialio-y := industrialio-core.o
include/linux/industrialio/trigger.h new file +68 -0

68 lines not on this page · raw message

include/linux/industrialio/ring_generic.h new file +24 -0
new file mode 100644
index 0000000..c79e2dd
@@ -0,0 +1,24 @@
+/* The industrial I/O core - generic ring buffer interfaces.
+ *
+ * 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 _IIO_RING_GENERIC_H_
+#define _IIO_RING_GENERIC_H_
+
+#include <linux/industrialio/chrdev.h>
+
+struct iio_ring_buffer;
+
+#ifdef CONFIG_IIO_RING_BUFFER
+#else
+
+struct iio_ring_buffer {};
+struct iio_ring_access_funcs {};
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif /* _IIO_RING_GENERIC_H_ */
#2 lore raw

PATCH 2/13 RFC: IIO ring buffer support (core)

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

The Industrial I/O core - ring buffer support

--
This patch adds ring buffer support to the industrial I/O subsystem core. This functionality may be disabled when not desired.

The support is designed to be generic enough to allow multiple software implementations. At the moment only one is available so the functionality for run time or board conf based selection has not yet been implemented.

The framework also allows for hardware ring buffers existing on the actual chip. An example of this usage is the VTI sca3000 driver found later in this patch set.

 drivers/industrialio/Kconfig              |    7
 drivers/industrialio/Makefile             |    1
 drivers/industrialio/industrialio-ring.c  |  491 ++++++++++++++++++++++++++++++
 include/linux/industrialio/ring_generic.h |  123 +++++++
 4 files changed, 622 insertions(+)
drivers/industrialio/Kconfig +7 -0
index 433f777..d132ab6 100644
@@ -10,3 +10,10 @@ menuconfig INDUSTRIALIO
 	  number of different physical interfaces (i2c, spi etc). See
 	  Documentation/industrialio for more information.
 
+config IIO_RING_BUFFER
+       bool "Enable ring buffer support within iio"
+	  depends on INDUSTRIALIO
+	  help
+	    Provide support for various ring buffer based data
+	    acquisition methods.
+
drivers/industrialio/Makefile +1 -0
index 1acd8fa..1f47af3 100644
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
 industrialio-y := industrialio-core.o
+industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
drivers/industrialio/industrialio-ring.c new file +491 -0

491 lines not on this page · raw message

include/linux/industrialio/ring_generic.h +123 -0

129 lines not on this page · raw message

#3 lore raw

PATCH 3/13 RFC: IIO Software ring buffer implementation

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: Software ring buffer implementation.

--

Intended to act as an example of how a software ring buffer might function rather than as a definitive solution. The particular focus of this was an application that required relatively hight sampling rates (couple of Ksamples) with low computational load and few dropped samples.

To my mind this is the most flakey element of this patch set. All suggestions on alternative approaches / simplifications would be most welcome. My intention is to also provide a far simple locked ring buffer (hence all accesses in bh of interrupt handlers) to act as a very simple example for people coding up there own but this is not yet in place.

 drivers/industrialio/Kconfig         |    9
 drivers/industrialio/Makefile        |    4
 drivers/industrialio/ring_sw.c       |  370 +++++++++++++++++++++++++++++++++++
 include/linux/industrialio/ring_sw.h |  183 +++++++++++++++++
 4 files changed, 565 insertions(+), 1 deletion(-)
drivers/industrialio/Kconfig +9 -0
index d132ab6..73c5616 100644
@@ -17,3 +17,12 @@ config IIO_RING_BUFFER
 	    Provide support for various ring buffer based data
 	    acquisition methods.
 
+config IIO_SW_RING
+       tristate "Industrial I/O lock free software ring"
+       depends on IIO_RING_BUFFER && INDUSTRIALIO
+
+       ---help---
+	example software ring buffer implementation.  The design aim
+	of this particular realization was to minize write locking
+	with the intention that some devices would be able to write
+	in interrupt context.
\ No newline at end of file
drivers/industrialio/Makefile +3 -1
index 1f47af3..0fda138 100644
@@ -3,5 +3,7 @@
 #
 
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
-industrialio-y := industrialio-core.o
+industrialio-y := industrialio-core.o 
 industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
+
+obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
drivers/industrialio/ring_sw.c new file +370 -0

370 lines not on this page · raw message

include/linux/industrialio/ring_sw.h new file +183 -0

183 lines not on this page · raw message

#4 lore raw

PATCH 4/13 RFC: IIO trigger support

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

The Industrial I/O core - trigger support

--
This patch adds supports for IIO triggers. These are effectively interrupt sources which are used to 'trigger' things to happen within IIO device drivers. Currently this limited to sampling data and pushing to a software ring buffers.

Current trigger drivers (found in next couple of patches) are
1) Periodic RTC - this replacement for the timer elements of the IIO v2
2) GPIO - sample on raising an external pin (useful for synchronizing samples with external hardware over which you do not have full control) This one is very much a proof of concept.
3) Data ready signals from certain hardware (currently LIS3L02DQ accelerometer)

The various triggers highlight some of the complexities to be overcome by any attempt to create a generic framework for handling these sort of events.

 drivers/industrialio/Kconfig                |    9 +
 drivers/industrialio/Makefile               |    1 +
 drivers/industrialio/industrialio-trigger.c |  363 +++++++++++++++++++++++++++
 include/linux/industrialio/trigger.h        |  110 ++++++++
 4 files changed, 483 insertions(+), 0 deletions(-)
drivers/industrialio/Kconfig +9 -0
index 73c5616..9bf6a36 100644
@@ -9,6 +9,15 @@ menuconfig INDUSTRIALIO
 	  drivers for many different types of embedded sensors using a
 	  number of different physical interfaces (i2c, spi etc). See
 	  Documentation/industrialio for more information.
+config IIO_TRIGGERS
+       boolean "Enable triggered sampling support"
+       depends on INDUSTRIALIO
+       default y
+         help
+	   Provides IIO core support for triggers.  Currently these
+	   are used to initialize capture of samples to push into 
+	   ring buffers.  The triggers are effectively a 'capture
+	   data now' interrupt.
 
 config IIO_RING_BUFFER
        bool "Enable ring buffer support within iio"
drivers/industrialio/Makefile +1 -0
index 0fda138..15ef75f 100644
@@ -5,5 +5,6 @@
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
 industrialio-y := industrialio-core.o 
 industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
+industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
drivers/industrialio/industrialio-trigger.c new file +363 -0

363 lines not on this page · raw message

include/linux/industrialio/trigger.h +110 -0

116 lines not on this page · raw message

#5 lore raw

PATCH 5/13 RFC: IIO Periodic real time clock based trigger.

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: Periodic real time clock based trigger.

--

Quite a few real time clock implementations provide configurable periodic interrupts. This module allows these to be used as triggers for filling IIO ring buffers at regular intervals.

As has been discussed a number of times on LKML and elsewhere, it would be nice to have a generic timer system to handle the many timers that are available and make use of their periodic capabilities. Until this is available the RTC subsystem provides the easiest way of doing this.

Whilst this is in of itself a useful implementation, the principal reason for including it here is to illustrate how IIO trigger drivers work.

 drivers/industrialio/Kconfig                       |    2 +
 drivers/industrialio/Makefile                      |    2 +
 drivers/industrialio/triggers/Kconfig              |    6 +
 drivers/industrialio/triggers/Makefile             |    5 +
 .../industrialio/triggers/iio-trig-periodic-rtc.c  |  197 ++++++++++++++++++++
 include/linux/industrialio/prtc_trigger.h          |    7 +
 6 files changed, 219 insertions(+), 0 deletions(-)
drivers/industrialio/Kconfig +2 -0
index 9bf6a36..c20ca2f 100644
@@ -19,6 +19,8 @@ config IIO_TRIGGERS
 	   ring buffers.  The triggers are effectively a 'capture
 	   data now' interrupt.
 
+source drivers/industrialio/triggers/Kconfig
+
 config IIO_RING_BUFFER
        bool "Enable ring buffer support within iio"
 	  depends on INDUSTRIALIO
drivers/industrialio/Makefile +2 -0
index 15ef75f..518849a 100644
@@ -8,3 +8,5 @@ industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
 industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
+
+obj-y += triggers/
\ No newline at end of file
drivers/industrialio/triggers/Kconfig new file +6 -0
new file mode 100644
index 0000000..20e6498
@@ -0,0 +1,6 @@
+config IIO_PERIODIC_RTC_TRIGGER
+       tristate "Periodic RTC triggers"
+          depends on INDUSTRIALIO && IIO_TRIGGERS && RTC_CLASS
+	  help
+	    Provides support for using periodic capable real time
+	    clocks as IIO triggers.
drivers/industrialio/triggers/Makefile new file +5 -0
new file mode 100644
index 0000000..4ae55b9
@@ -0,0 +1,5 @@
+#
+# Makefile for triggers not associated with iio-devices
+#
+obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
+
drivers/industrialio/triggers/iio-trig-periodic-rtc.c new file +197 -0

197 lines not on this page · raw message

include/linux/industrialio/prtc_trigger.h new file +7 -0
new file mode 100644
index 0000000..9dabc19
@@ -0,0 +1,7 @@
+struct iio_prtc_trigger {
+	const char *name;
+};
+struct iio_prtc_trigger_group {
+	int num_triggers;
+	struct iio_prtc_trigger *triggers;
+};
#6 lore raw

PATCH 6/13 RFC: Proof of concept GPIO based IIO trigger

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: Proof of concept GPIO based IIO trigger

--

A very limited trigger driver using a GPIO to provide synchronization with external devices. Things like setting polarity and interrupt types will need to be added to make this more useful.

 drivers/industrialio/triggers/Kconfig         |   12 ++
 drivers/industrialio/triggers/Makefile        |    5 +-
 drivers/industrialio/triggers/iio-trig-gpio.c |  193 +++++++++++++++++++++++++
 include/linux/industrialio/gpio_trigger.h     |    6 +
 4 files changed, 215 insertions(+), 1 deletions(-)
drivers/industrialio/triggers/Kconfig +7 -0
index 20e6498..e100098 100644
@@ -1,6 +1,18 @@
+config IIO_GPIO_TRIGGER
+       tristate "GPIO triggers"
+          depends on INDUSTRIALIO && IIO_TRIGGERS && GENERIC_GPIO
+	  help
+	    Driver provides support for using GPIOs as the IIO
+	    triggers.
+
 config IIO_PERIODIC_RTC_TRIGGER
        tristate "Periodic RTC triggers"
           depends on INDUSTRIALIO && IIO_TRIGGERS && RTC_CLASS
 	  help
 	    Provides support for using periodic capable real time
 	    clocks as IIO triggers.
drivers/industrialio/triggers/Makefile +4 -1
index 4ae55b9..767d2cd 100644
@@ -1,5 +1,8 @@
 #
 # Makefile for triggers not associated with iio-devices
 #
-obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
 
+obj-$(CONFIG_IIO_GPIO_TRIGGER) += iio-trig-gpio.o
+
+obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
+#obj-$(CONFIG_IIO_PTIMER_BOARDINFO) += iio-ptimer-boardinfo.o
drivers/industrialio/triggers/iio-trig-gpio.c new file +193 -0

193 lines not on this page · raw message

include/linux/industrialio/gpio_trigger.h new file +6 -0
new file mode 100644
index 0000000..25be3da
@@ -0,0 +1,6 @@
+
+
+struct iio_gpio_trigger_pdata {
+	int num_gpios;
+	int *gpios;
+};
#7 lore raw

PATCH 7/13 RFC: Maxim MAX1363 driver

In reply to Jonathan Cameron

From: Jonathan Cameron <jic23-KWPb1pKIrIJaa/[email protected]>

IIO: Maxim MAX1363 driver
Also supports max1361, max1362, max1364, max1136, max1136, max1137, max1138, max1139, max1236, max1237, max11238, max1239

--
This patch provides the core functionality (sysfs control and direct device scanning, chrdev event interface).

It illustrates one common form of scan mode configuration found in numerous similar devices. A restricted set of combinations of elements are possible via a set of predefined scan modes. How to handle this in a consistent way alongside devices such as the lis3l02dq accelerometer (any combinations of channels possible) is very much a question for discussion.

 drivers/industrialio/Kconfig            |    4 +-
 drivers/industrialio/Makefile           |    3 +-
 drivers/industrialio/adc/Kconfig        |   13 +
 drivers/industrialio/adc/Makefile       |    7 +
 drivers/industrialio/adc/max1363.h      |  250 +++++++++++++
 drivers/industrialio/adc/max1363_core.c |  599 +++++++++++++++++++++++++++++++
 6 files changed, 874 insertions(+), 2 deletions(-)
drivers/industrialio/Kconfig +3 -1
index c20ca2f..f682fbf 100644
@@ -36,4 +36,6 @@ config IIO_SW_RING
 	example software ring buffer implementation.  The design aim
 	of this particular realization was to minize write locking
 	with the intention that some devices would be able to write
-	in interrupt context.
\ No newline at end of file
+	in interrupt context.
+
+source drivers/industrialio/adc/Kconfig
\ No newline at end of file
drivers/industrialio/Makefile +2 -1
index 518849a..dd6d5ca 100644
@@ -9,4 +9,5 @@ industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 
-obj-y += triggers/
\ No newline at end of file
+obj-y += triggers/
+obj-y += adc/
\ No newline at end of file
drivers/industrialio/adc/Kconfig new file +13 -0
new file mode 100644
index 0000000..203a5f9
@@ -0,0 +1,13 @@
+#
+# ADC drivers
+#
+comment "Analog to digital convertors"
+	depends on INDUSTRIALIO
+config MAX1363
+	tristate "MAXIM max1363 ADC driver"
+	depends on INDUSTRIALIO
+	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.
drivers/industrialio/adc/Makefile new file +7 -0
new file mode 100644
index 0000000..2a37fee
@@ -0,0 +1,7 @@
+
+# Makefile for industrial I/O ADC drivers
+#
+
+max1363-y := max1363_core.o
+
+obj-$(CONFIG_MAX1363) += max1363.o
drivers/industrialio/adc/max1363.h new file +250 -0

250 lines not on this page · raw message

drivers/industrialio/adc/max1363_core.c new file +599 -0

599 lines not on this page · raw message

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

#8 lore raw

cover RFC: Maxim MAX1363 driver (ring buffer support)

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: Maxim MAX1363 driver (ring buffer support)

--
This patch adds triggered software ring buffer support to the core driver. It should act as an example of how many of the elements of the IIO subsystem function.

There is currently an issue with unaligned timestamps on arm to be resolved when non multiple of 4 scan modes used.

 drivers/industrialio/adc/Kconfig        |    6 +
 drivers/industrialio/adc/Makefile       |    2 +
 drivers/industrialio/adc/max1363.h      |    9 ++
 drivers/industrialio/adc/max1363_ring.c |  219 +++++++++++++++++++++++++++++++
 4 files changed, 236 insertions(+), 0 deletions(-)
drivers/industrialio/adc/Kconfig +6 -0
index 203a5f9..be77ab5 100644
@@ -11,3 +11,9 @@ config MAX1363
 	  convertors (ADC). (max1361, max1362, max1363, max1364, max1136,
 	  max1136, max1137, max1138, max1139, max1236, max1237, max11238,
 	  max1239) Provides direct access via sysfs.
+
+config MAX1363_RING
+       boolean "Ring buffer support"
+       depends on MAX1363 && IIO_SW_RING && IIO_TRIGGERS
+       help
+         Enable software ring buffer support in the max1363 driver.
drivers/industrialio/adc/Makefile +2 -0
index 2a37fee..a6bff5f 100644
@@ -3,5 +3,7 @@
 #
 
 max1363-y := max1363_core.o
+#got to be a better way of doing this 
+max1363-$(CONFIG_MAX1363_RING) += max1363_ring.o
 
 obj-$(CONFIG_MAX1363) += max1363.o
drivers/industrialio/adc/max1363.h +9 -0
index 71e9577..b0e8928 100644
@@ -228,6 +228,15 @@ struct max1363_data {
 };
 
 #ifdef CONFIG_MAX1363_RING
+
+ssize_t max1363_scan_from_ring(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf);
+int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev);
+void max1363_ring_cleanup(struct iio_dev *indio_dev);
+
+
+
 #else /* CONFIG_MAX1363_RING */
 
 static inline ssize_t max1363_scan_from_ring(struct device *dev,
drivers/industrialio/adc/max1363_ring.c new file +219 -0

219 lines not on this page · raw message

#9 lore raw

PATCH 9/13 RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (core)

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (core)

--

This is a fairly typical SPI accelerometer.

I2C functionality will be added at a later date.

Features:
Variable sampling rates
Data ready signal to indicate new data
-this is awkward as it will go high on any channel
having new data and hence needs complex handler.
Axis by axis gain and offset adjustment.
Axis threshold detector (single theshold for all 3)

 drivers/industrialio/Kconfig                       |    1 +
 drivers/industrialio/Makefile                      |    3 +-
 drivers/industrialio/accelerometer/Kconfig         |   21 +
 drivers/industrialio/accelerometer/Makefile        |    6 +
 drivers/industrialio/accelerometer/lis3l02dq.h     |  218 +++++
 .../industrialio/accelerometer/lis3l02dq_core.c    |  886 ++++++++++++++++++++
 include/linux/industrialio/accel.h                 |  157 ++++
 7 files changed, 1291 insertions(+), 1 deletions(-)
drivers/industrialio/Kconfig +1 -0
index f682fbf..58bfbd7 100644
@@ -38,4 +38,5 @@ config IIO_SW_RING
 	with the intention that some devices would be able to write
 	in interrupt context.
 
+source drivers/industrialio/accelerometer/Kconfig
 source drivers/industrialio/adc/Kconfig
\ No newline at end of file
drivers/industrialio/Makefile +2 -1
index dd6d5ca..453dcc3 100644
@@ -10,4 +10,5 @@ industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 
 obj-y += triggers/
-obj-y += adc/
\ No newline at end of file
+obj-y += adc/
+obj-y += accelerometer/
\ No newline at end of file
drivers/industrialio/accelerometer/Kconfig new file +21 -0
new file mode 100644
index 0000000..7e011a6
@@ -0,0 +1,21 @@
+#
+# Accelerometer drivers
+#
+comment "Accelerometers"
+	depends on INDUSTRIALIO
+config LIS3L02DQ
+	tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
+	depends on INDUSTRIALIO
+	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 new file +6 -0
new file mode 100644
index 0000000..22f3d87
@@ -0,0 +1,6 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+lis3l02dq-y := lis3l02dq_core.o
+obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
drivers/industrialio/accelerometer/lis3l02dq.h new file +218 -0

218 lines not on this page · raw message

drivers/industrialio/accelerometer/lis3l02dq_core.c new file +886 -0

886 lines not on this page · raw message

include/linux/industrialio/accel.h new file +157 -0

157 lines not on this page · raw message

#10 lore raw

PATCH 10/13 RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (ring)

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (ring)

--

This patch adds triggered software ring buffer support to the lis3l02dq driver. It also adds a new trigger from the data ready signal of the lis3l02dq. The presence of a data ready signal is common for these sorts of devices and as one will often want to trigger other sensors at the same frequency within multimodal sensing systems, it is useful to have this as an IIO trigger.

 drivers/industrialio/accelerometer/Makefile        |    4 +-
 drivers/industrialio/accelerometer/lis3l02dq.h     |   20 +-
 .../industrialio/accelerometer/lis3l02dq_ring.c    |  600 ++++++++++++++++++++
 3 files changed, 622 insertions(+), 2 deletions(-)
drivers/industrialio/accelerometer/Makefile +3 -1
index 22f3d87..3079723 100644
@@ -3,4 +3,6 @@
 #
 
 lis3l02dq-y := lis3l02dq_core.o
-obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
+#RFC separate config param per device?
+lis3l02dq-$(CONFIG_IIO_RING_BUFFER) += lis3l02dq_ring.o
+obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
\ No newline at end of file
drivers/industrialio/accelerometer/lis3l02dq.h +19 -1
index f53b68b..5ff9bc3 100644
@@ -183,6 +183,24 @@ int lis3l02dq_spi_write_reg_8(struct device *dev,
 #define LIS3L02DQ_SCAN_ACC_Y 1
 #define LIS3L02DQ_SCAN_ACC_Z 2
 /* temporary disable */
+#ifdef CONFIG_IIO_RING_BUFFER
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t lis3l02dq_read_accel_from_ring(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+
+
+int lis3l02dq_register_ring_funcs(struct iio_dev *indio_dev);
+void lis3l02dq_unregister_ring_funcs(struct iio_dev *indio_dev);
+
+int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length);
+#else
 
 static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev){};
 static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
@@ -210,7 +228,7 @@ int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length)
 	return 0;
 };
 
-
+#endif
 #endif /* SPI_LIS3L02DQ_H_ */
 
 
drivers/industrialio/accelerometer/lis3l02dq_ring.c new file +600 -0

600 lines not on this page · raw message

#11 lore raw

PATCH 11/13 RFC: VTI SCA3000 3d accelerometer driver.

In reply to Jonathan Cameron

From: Jonathan Cameron <jic23-KWPb1pKIrIJaa/[email protected]>

IIO: VTI SCA3000 3d accelerometer driver.

--
This patch provides support for the VTI SCA3000 seriese of accelerometers. These are relatively unusual chips having internal ring buffers. They provide interrupt signals on ring buffer events (50% full etc) and so fit well within the IIO ring buffer architecture.

This patch also introduces the ring_hw.h header which provides the infrastructure code for hardware ring buffers.

At the current time this driver doesn't support triggers or software ring buffers. As it does not supply a data ready signal it doesn't really make sense to use it as a trigger, but more complex use of the ring buffer might include the ability to trigger a copy of current contents for example on some other event.

 drivers/industrialio/accelerometer/Kconfig        |    8 +
 drivers/industrialio/accelerometer/Makefile       |    6 +-
 drivers/industrialio/accelerometer/sca3000.h      |  266 ++++
 drivers/industrialio/accelerometer/sca3000_core.c | 1522 +++++++++++++++++++++
 drivers/industrialio/accelerometer/sca3000_ring.c |  196 +++
 include/linux/industrialio/ring_hw.h              |    9 +
 include/linux/industrialio/sca3000.h              |   36 +
 7 files changed, 2042 insertions(+), 1 deletions(-)
drivers/industrialio/accelerometer/Kconfig +8 -0
index 7e011a6..02bfb53 100644
@@ -19,3 +19,11 @@ config LIS3L02DQ_SPI
 	  Say yes here to build support for the ST LIS3L02DQ accelerometer via
 	  an SPI bus.
 
+config SCA3000
+       depends on INDUSTRIALIO
+       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.
\ No newline at end of file
drivers/industrialio/accelerometer/Makefile +5 -1
index 3079723..eabc23e 100644
@@ -5,4 +5,8 @@
 lis3l02dq-y := lis3l02dq_core.o
 #RFC separate config param per device?
 lis3l02dq-$(CONFIG_IIO_RING_BUFFER) += lis3l02dq_ring.o
-obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
\ No newline at end of file
+obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
+
+sca3000-y := sca3000_core.o
+sca3000-$(CONFIG_IIO_RING_BUFFER) += sca3000_ring.o
+obj-$(CONFIG_SCA3000)		+= sca3000.o
drivers/industrialio/accelerometer/sca3000.h new file +266 -0

266 lines not on this page · raw message

drivers/industrialio/accelerometer/sca3000_core.c new file +1522 -0

1,522 lines not on this page · raw message

drivers/industrialio/accelerometer/sca3000_ring.c new file +196 -0

196 lines not on this page · raw message

include/linux/industrialio/ring_hw.h new file +9 -0
new file mode 100644
index 0000000..93b0cae
@@ -0,0 +1,9 @@
+/**
+ * struct iio_hw_ring_buffer- hardware ring buffer
+ * @buf:	generic ring buffer elements
+ * @private:	device specific data
+ */
+struct iio_hw_ring_buffer {
+	struct iio_ring_buffer buf;
+	void *private;
+};
include/linux/industrialio/sca3000.h new file +36 -0
new file mode 100644
index 0000000..a07c70d
@@ -0,0 +1,36 @@
+/*
+ * sca3000.h -- conversion functions related to VTI sca3000 series accelerometers
+ *
+ * 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.
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23-KWPb1pKIrIJaa/[email protected]>
+ *
+ */
+
+#ifndef _SCA3000_H_USER_
+#define _SCA3000_H_USER_
+
+/* Conversion function for use with the ring buffer when in 11bit mode */
+static 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;
+};
+
+static inline int sca3000_13bit_convert(uint8_t msb, uint8_t lsb)
+{
+	s16 val;
+
+	val = ((lsb >> 3) & 0x1F) | (msb << 5);
+	/* sign fill */
+	val |= (val & (1 << 12)) ? 0xE000 : 0;
+
+	return val;
+}
+#endif /* _SCA3000_H_USER_ */

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

#12 lore raw

PATCH 12/13 RFC: IIO Documentation

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

The Industrial I/O Documentation.

--

This patch provides some documentation for the industrial I/O (IIO) subsystem. Much of the documentation is as kernel-doc comments within the code, but an overview is provided here along with some specific details of ring buffering and the max1363 driver.

 Documentation/industrialio/iio_ring.txt |   90 +++++++++++++++++++++++++++++++
 Documentation/industrialio/max1363.txt  |   37 +++++++++++++
 Documentation/industrialio/overview.txt |   76 ++++++++++++++++++++++++++
 3 files changed, 203 insertions(+), 0 deletions(-)
Documentation/industrialio/iio_ring.txt new file +90 -0
new file mode 100644
index 0000000..410a552
@@ -0,0 +1,90 @@
+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
+chrdev.  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 pulling
+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 samples 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 definitely good 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).
+
+More information is available in <linux/industrialio/ring_generic.h>
Documentation/industrialio/max1363.txt new file +37 -0
new file mode 100644
index 0000000..ad388d9
@@ -0,0 +1,37 @@
+
+Documentation for the max1363 industrialio driver via i2c interface.
+
+This driver has been tested with max1363 and max1238 chips.
+
+Please report any problems / success in using with other chips.
+
+Supported chips
+
+With monitor and smbus alert.
+
+max1361, max1362 - 10 bit versions
+max1363, max1364 - 12 bit versions
+
+Simple
+max1136, max1137 - 4 channel 10 bit versions
+max1138, max1139 - 12 channel 10 bit versions
+
+max1236, max1237 - 4 channel 12 bit versions
+max1238, max1239 - 12 channel 12 bit versions.
+
+
+May add support for the following in the future:
+
+* max103{6-9} - 8 bit versions. Obviously the read code will be somewhat
+simpler for these.
+
+* max1069, max1169 - 14, 16 bit single channel i2c adapters.
+
+Please point out any other similar devices - or even better submit a patch
+adding support.
+
+Probably not similar enough to support via the same driver:
+
+* max127, max128 - multirange 8 channel adc's.
+
+* max1153,max1154,max1253 and max1254 - more complex devices.
Documentation/industrialio/overview.txt new file +76 -0

76 lines not on this page · raw message

#13 lore raw

PATCH 13/13 RFC: Example user space application

In reply to Jonathan Cameron

From: Jonathan Cameron <[email protected]>

IIO: Example user space application. Configures and then reads from a software ring buffer which is filled based on a the lis3l02dq data ready trigger and is a software ring buffer.

--
Quite a lot of this program would be simplified if a couple of generic library routines were available. It also illustrates the necessary separation between user space and kernel space elements of the IIO headers (cannot currently be used in here so a couple of things have to be redefined).

 Documentation/industrialio/TestRingLIS3L02DQ.c |  340 ++++++++++++++++++++++++
 1 files changed, 340 insertions(+), 0 deletions(-)
Documentation/industrialio/TestRingLIS3L02DQ.c new file +340 -0
new file mode 100644
index 0000000..e963d61
@@ -0,0 +1,340 @@
+/* Industrialio test ring buffer for lis3l02dq
+ *
+ * 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.
+ *
+ * There are numerous places where this code could be shortened but that
+ * is work for another day.  The basic principles are what matter at
+ * this stage.
+ */
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+
+/* FIXME: Get this from main headers - Separation into userspace
+ * accessible parts and kernel only parts still needs to be done.
+ */
+struct iio_event_data {
+	int id;
+	__s64 timestamp;
+};
+
+#define IIO_EVENT_CODE_RING_50_FULL 200
+#define IIO_EVENT_CODE_RING_75_FULL 201
+#define IIO_EVENT_CODE_RING_100_FULL 202
+/* Returns the base device directory of a named sensor.
+ * This sort of useful function should go in a generic userspace
+ * library */
+char *find_device_by_name(const char *name)
+{
+	const char *iio_dir = "/sys/class/industrialio";
+	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);
+	pos += sprintf(temp + pos, "/");
+	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, "iio_trigger", 10) != 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 name\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 RingLength = 100;
+int NumVals = 3;
+
+int main(int argc, char **argv)
+{
+	int toread, sample = 0;
+	uint16_t tempx, tempy, tempz;
+	FILE  *sysfsfp, *fp_ev, *ringcont;
+	int fp;
+	char *data;
+	const char *device_name = "lis3l02dq";
+	size_t read_size;
+	int i, j, k, temps;
+	int minor, minor_ev;
+	char name_chrdevevt[100];
+	char name_chrdevring[100];
+	char command[100];
+	int device_no;
+	char temp[200];
+	int pos;
+	struct iio_event_data dat;
+	int scanx = 1, scany = 1, scanz = 1;
+	int scants = 1;
+
+	int datum_size = 0;
+	char *BaseDirectoryName;
+
+	/* Parameters to disable particular channels */
+	for (i = 1; i < argc; i++) {
+		if (strcmp(argv[i], "-x") == 0)
+			scanx = 0;
+		if (strcmp(argv[i], "-y") == 0)
+			scany = 0;
+		if (strcmp(argv[i], "-z") == 0)
+			scanz = 0;
+		if (strcmp(argv[i], "-ts") == 0)
+			scants = 0;
+	}
+	/* Getting the s64 aligned correctly - this will end up in
+	 * the lis3l02dq userspace header
+	 */
+	if (scants && (scanx || scany || scanz))
+		datum_size = 16;
+	else if (scants)
+		datum_size = 8;
+	else {
+		if (scanx)
+			datum_size += sizeof(__s16);
+		if (scany)
+			datum_size += sizeof(__s16);
+		if (scanz)
+			datum_size += sizeof(__s16);
+	}
+	data = malloc(datum_size*RingLength);
+	BaseDirectoryName = find_device_by_name(device_name);
+
+	if (BaseDirectoryName == NULL) {
+		printf("Failed to find the %s \n", device_name);
+		return -1;
+	}
+
+	/* The next section sets up all the device parameters to
+	 * appropriate values before intializing the ring.
+	 */
+	/* Set a sensible ring buffer length */
+	sprintf(temp, "%sring_buffer/length", BaseDirectoryName);
+	sysfsfp = fopen(temp, "w");
+	if (sysfsfp == NULL) {
+		printf("failed to open the rb length file at %s\n", temp);
+		return -1;
+	}
+	fprintf(sysfsfp, "%d", RingLength);
+	fclose(sysfsfp);
+	/* Create relevant device Nodes */
+	sprintf(temp, "%sring_buf0_acc_minor", BaseDirectoryName);
+	sysfsfp = fopen(temp, "r");
+	if (sysfsfp == NULL) {
+		printf("failed to open minor stuff %s \n", temp);
+		return -1;
+	}
+
+	fscanf(sysfsfp, "%d\n", &minor);
+	sprintf(name_chrdevring, "/dev/indring%d", minor);
+
+	fclose(sysfsfp);
+	sprintf(temp, "%sring_buf0_ev_minor", BaseDirectoryName);
+	sysfsfp = fopen(temp, "r");
+	if (sysfsfp == NULL) {
+		printf("failed to open minor stuff \n");
+		return -1;
+	}
+
+	fscanf(sysfsfp, "%d\n", &minor_ev);
+	fclose(sysfsfp);
+
+	/* set scan of x accel */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_accel_x",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_accel_x\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scanx);
+		fclose(sysfsfp);
+	}
+
+
+	/* set scan of y accel */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_accel_y",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_accel_y\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scany);
+		fclose(sysfsfp);
+	}
+
+
+	/* set scan of z accel */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_accel_z",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_accel_z\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scanz);
+		fclose(sysfsfp);
+	}
+
+
+	/* set scan of ts */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_timestamp",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_timestamp\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scants);
+		fclose(sysfsfp);
+	}
+
+	sprintf(name_chrdevevt, "/dev/indringev%d", minor_ev);
+	/* Enable the ring buffer */
+	{
+		sprintf(temp, "%sring_buffer/ring_enable", BaseDirectoryName);
+		ringcont = fopen(temp, "w");
+
+		if (ringcont == NULL) {
+			printf("Failed to open the rb control file \n");
+			return -1;
+		};
+		fprintf(ringcont, "1");
+		fclose(ringcont);
+	}
+	/* Attempt to open non blocking the access dev */
+	fp = open(name_chrdevring, O_RDONLY | O_NONBLOCK);
+	if (fp == -1) { /*If it isn't there make the node */
+		sprintf(command, "mknod %s c 244 %d; mknod %s c 244 %d; ",
+			name_chrdevring, minor, name_chrdevevt, minor_ev);
+		system(command);
+		sleep(5);
+		fp = open(name_chrdevring, O_RDONLY | O_NONBLOCK);
+		if (fp == -1) {
+			printf("Unable to open %s\n", name_chrdevring);
+			return -1;
+		}
+	}
+	/* Attempt to open the event access dev (blocking this time) */
+	fp_ev = fopen(name_chrdevevt, "rb");
+	if (fp_ev == NULL) {
+
+		printf("bug opening %s\n", name_chrdevevt);
+		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");
+		}
+		read_size = read(fp, (char *)(data), toread*(datum_size));
+		if (read_size == -EAGAIN)
+			printf("nothing available \n");
+		/* Print out the first 10% of the values recieved from the rb*/
+		for (i = 0; i < (read_size  / datum_size)*0.1; i++) {
+			k = 0;
+			printf("%d, ", sample++);
+			if (scanx)
+				printf("%d, ",
+				       *((__s16 *)(data + i*(datum_size))
+					 + k++));
+			if (scany)
+				printf("%d, ",
+				       *((__s16 *)(data + i*(datum_size))
+					 + k++));
+			if (scanz)
+				printf("%d, ",
+				       *((__s16 *)(data + i*(datum_size))
+					 + k++));
+			if (scants)
+				printf(" %lld ",
+				       *((__s64 *)(data + i*(datum_size))
+					 + ((k+3)/4)));
+			printf("\n");
+
+		}
+
+	}
+	close(fp);
+	fclose(fp_ev);
+	/* Stop the ring buffer */
+	{
+
+		sprintf(temp, "%sring_buffer/ring_enable", BaseDirectoryName);
+		ringcont = fopen(temp, "w");
+		if (ringcont == NULL) {
+			printf("Failed to open the rb control file \n");
+			return -1;
+		};
+		fprintf(ringcont, "0");
+
+		fclose(ringcont);
+	}
+
+	free(BaseDirectoryName);
+	free(data);
+
+	return 0;
+}
#14 lore raw
In reply to Jonathan Cameron
Quoted 4 lines

have both functions. The sort of devices we are talking typically communicate over I2C or SPI buses though drivers for rs232 devices etc are definitely on the cards. Basically we are interested in devices where direct memory mapped access is not possible.

We have I2C and SPI drivers so I assume you will use the lower layers of the stacks to do this ?

Quoted 2 lines

For discussion of why these don't fit within existing subsystems see
http://lkml.org/lkml/2008/5/20/135 and the rest of the associated thread.

I don't see much there which says why you can't unify all this in a user space library.

For RS232/423/.. devices you can go this path if you want (but I would keep it all in userspace anyway) as you can use a line discipline to sit on top of the port and provide another interface (eg the way PPP does)

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

#15 lore raw

Dear Alan,

Quoted 7 lines
Quoted 4 lines

have both functions. The sort of devices we are talking typically communicate over I2C or SPI buses though drivers for rs232 devices etc are definitely on the cards. Basically we are interested in devices where direct memory mapped access is not possible.

We have I2C and SPI drivers so I assume you will use the lower layers of the stacks to do this ?

Yes, in much the same way that for example hwmon is built on top of these (amongst other interfaces)

Quoted 6 lines
Quoted 3 lines

For discussion of why these don't fit within existing subsystems see
http://lkml.org/lkml/2008/5/20/135 and the rest of the associated thread.

I don't see much there which says why you can't unify all this in a user space library.

Indeed, that was an option considered at the time and the majority of that discussion focused on how to do things in a similar way to existing similar kernel subsystems.

Perhaps this question needs further clarification.

Much of the stuff provided in this initial patch set could indeed be provided by a fairly complex user space library.

I get the feeling this rest of this response is going to look pretty familiar. Though as I have never attempted to code anything other than simple userspace drivers I suspect others may have more detailed responses. These are in no particular order.

I believe the principle argument in favour of user space drivers is that of enhanced stability as they are unlikely to be able to bring the kernel down. This advantage is indeed valid here.

The other advantage is that the number of copies required to get data to and from hardware are reduced. For the devices considered here a userspace driver would have to go via one of the chrdev interfaces and neither i2c-dev or spidev are able to support mmap ed interfaces.

The problems I can forsee (or have encountered) with user space drivers for this sort of device:
* In many cases the ADCs form part of more complex devices which already have a support within the kernel. (e.g. PMICs such as those supported by the DA903x mfd driver) and as such any additional interface will require at least some kernel parts. Other places I've seen ADCs that I'd like to have available via a consistent interface, include firewire and usb cameras (aux ADC's and DAC's are common for controlling pan tilt heads etc). Some of these could be implemented in userspace (and quite possibly should be,) but others would require driver changes that will effectively equal those of adding direct support for the subsystem described in these patches.

* Spidev and i2cdev are fairly restricted interfaces which I have used extensively for initial investigation of sensors. The key thing here is that almost no devices use purely these interfaces. They include interrupt lines etc requiring relatively complex handlers. Thus any driver would probably require at least some kernel space element per chip anyway. Also worth noting that this tends to be the most complex and hence probably error prone part of these drivers. The spidev documentation talks about the limitations of the driver.

An example of the problems that would be encountered with the current i2c-dev implementation within a full driver would be a control register read on a VTI SCA3000 (i2c) accelerometer. This would require an initial 3 byte
write followed by a 1 byte read. In kernel space this whole thing
can be set up in a dma capable buffer and done in one go with copies out to userspace only needed if the value is meant to be read by the user.

* Need to provide kernel space interfaces. This actually fulls under the RFC question of what the scope of this subsystem should be. For example, things like battery chargers, free fall detectors etc frequently use generic components of the types that IIO will include. The systems that use these sometimes require kernel level access which a userspace driver clearly cannot supply.

* Performance: Thinks like time stamping of data ready signals needs to be as near as possible to the source interrupt. As for the complex question of which is optimal for the ring buffering functionality, this would need thorough optimization of both approaches before it could be addressed. I'm not aware of any similar comparisons but would be grateful if anyone could direct me to any they know of. It is also worth noting here that many applications that will use the ring buffered interface will never actually read the contents of the buffer as they are interested in only restricted regions of data (based on some external trigger).

In conclusion, I'd be interested in the long run in the possibility of moving all the above functionality into userspace but at the moment don't believe it to be the sensible option here.
The alternative of would be to provide only those drivers that would benefit from a kernel space driver within this subsystem. This would lose the advantages to be gained from a consistent interface. This could in turn be made transparent via a userspace library but only at the cost of unnecessary complexity.

Quoted 4 lines

For RS232/423/.. devices you can go this path if you want (but I would keep it all in userspace anyway) as you can use a line discipline to sit on top of the port and provide another interface (eg the way PPP does)

An interesting suggestion. The main reason I'd be interested in bringing these into the kernel is to do with the handling of additional interrupt lines (which a number of similar devices have).

Thanks for the comments,

--
Jonathan Cameron