Dear All,
The need for an industrialio subsystem was discussed in
http://lkml.org/lkml/2008/5/20/135
Firstly thanks to all the people who have contributed to the discussion
of this in the past.
In brief the intention is provide a kernel subsystem directed towards the
handling on sensors (and later related output devices) such as ADC's,
accelerometers and many others.
Key features of the subsystem include:
* Provision of sysfs access for direct reading from devices (similar to hwmon
but without the buffering / update rate restrictions)
* Provision of chrdevs through which events may be passed to userspace in a
similar fashion to the input subsystem. These events may be anything from
hardware thresholds set on the sensor itself to sw / hw ring buffer event
notifications (50% full etc).
* Provision of access via chrdevs to hardware ring buffers on devices that
provide them.
* Software ring buffer support to allow semi regular capture of data form the
device. Typically this will be driven from either datardy events, or from
a periodic timer interrupt (to this end a very simple wrapper for periodic
RTC's is included. This will move to more generic timer interfaces as and when
they become available. For now available rtc's must be registered with the
subsystem via the industrialio_register_ptimer function form within a board
init.
* A set of sample drivers illustrating the main 'classes' of device. By classes
I really mean devices that are interfaced with in a similar way.
The subsystem is now in a functional state with a small set of drivers:
Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
- Uses a periodic timer to provide ring buffer mode.
- All reads form these devices are scan modes so direct single element access
is not provided.
- Monitor mode on max1363 is not yet supported (need to do a bit debugging of
the board I have so as to be able to test this).
ST LIS3L02DQ - SPI accelerometer.
- Uses a datardy interrupt to driver a software ring buffer.
- Most functionality of this device is supported.
VTI SCA3000 (tested with an e05)
- Hardware ring buffer.
More drivers in preparation.
Next focus will be on cleaning up / implementing a more generic timer framework
and allowing the system to partly run if not all dependencies are met
(particularly availability of timers).
An initial set of patches will be attached to this thread shortly.
--
Jonathan Cameron
From: Jonathan Cameron <[email protected]>
Industrialio subsystem core patch. This subsystem is intended to support the use
of (initially) sensors within linux for the purposes of data capture and its use
within control applications. The intention is to provide consistent interfaces
(where it makes sense) with device control occuring through sysfs interfaces and
provision of events to userspace via chrdevs. Currently software ring buffers
are available if the sensor provides a data ready signal or a periodic rtc is
available (and registered with the subsystem in board init code).
Signed-off-by: Jonathan Cameron <[email protected]>
---
The periodic timer code is a temporary stop gap until a more generic subsystem
becomes available.
The intention of publishing these patches is to generate feedback both at the
high level of suggestions / comments on the general approach taken by the
subsystem as a whole and at the low level of implementation details.
Areas that in my view need attention are the software ring buffer (particularly
careful analysis of corner cases and efficiency of it as a storage method).
Although none of the current drivers is capable of filling it in interrupt
context, I can envision some hardware may need to and this will clearly require
some changes. The overall layout of the interfaces (and indeed the code) needs
some work, particularly with a view to cutting down the dependancies if a
given driver doesn't need all of the systems functionality.
Additional test drivers will obviously assist in working out many of these
issues and I hope to add several more over the comming weeks.
My sincerest thanks goes to anyone who takes the time to read through or test
this patch set.
drivers/Kconfig | 3
drivers/Makefile | 1
drivers/industrialio/Kconfig | 19
drivers/industrialio/Makefile | 7
drivers/industrialio/industrialio-core.c | 787 ++++++++++++++++++
drivers/industrialio/industrialio-ring.c | 770 +++++++++++++++++
drivers/industrialio/industrialio-rtc.c | 134 +++
drivers/industrialio/industrialio_ptimer_board_info.c | 44 +
include/linux/industrialio.h | 374 ++++++++
include/linux/industrialio_ptimer.h | 18
include/linux/industrialio_sysfs.h | 274 ++++++
11 files changed, 2431 insertions(+)
------ a/drivers/Kconfig 2008-07-13 22:51:29.000000000 +0100
+++ b/drivers/Kconfig 2008-07-14 17:26:34.000000000 +0100
@@ -101,4 +101,7 @@ source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"
source "drivers/xen/Kconfig"
+
+source "drivers/industrialio/Kconfig"
endmenu
+
--- a/drivers/Makefile 2008-07-13 22:51:29.000000000 +0100
+++ b/drivers/Makefile 2008-07-14 17:26:34.000000000 +0100
@@ -62,6 +62,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/
--- a/drivers/industrialio/Kconfig 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/Kconfig 2008-07-23 15:44:45.000000000 +0100
@@ -0,0 +1,19 @@
+#
+# Industrial I/O subsytem configuration
+#
+
+menuconfig INDUSTRIALIO
+ tristate "Industrial I/O support"
+ ---help---
+ The industrial IO subsystem provides a unified framework for drivers
+ for many different types of embedded sensors using a number of
+ different phyiscal interfaces (i2c, spi etc). See
+ Documentation/industrialio for more information.
+
+if INDUSTRIALIO
+
+config INDUSTRIALIO_PTIMER_BOARDINFO
+ boolean
+ default y
+
+endif
--- a/drivers/industrialio/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/Makefile 2008-07-23 12:05:27.000000000 +0100
@@ -0,0 +1,7 @@
+#
+# Makefile for the industrial I/O core.
+#
+industrialio-objs := industrialio-core.o industrialio-ring.o industrialio-rtc.o
+
+obj-$(CONFIG_INDUSTRIALIO) += industrialio.o
+obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
--- a/include/linux/industrialio.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/industrialio.h 2008-07-23 15:20:19.000000000 +0100
@@ -0,0 +1,374 @@
+/* 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.
+ */
+
+#ifndef _INDUSTRIAL_IO_H_
+#define _INDUSTRIAL_IO_H_
+
+#include <linux/device.h>
+#include <linux/industrialio_sysfs.h>
+
+/* TODO LIST */
+/* Static device specific elements (conversion factors etc)
+ should be exported via sysfs
+ Break up this header - some drivers only want a fraction of this.
+*/
+
+
+/* Event interface flags */
+#define IIO_BUSY_BIT_POS 1
+
+
+struct iio_handler {
+ const struct file_operations *fops;
+ int id;
+ unsigned long flags;
+ void *private;
+};
+
+/* The actual event being pushed ot userspace */
+struct iio_event_data {
+ int id;
+ s64 timestamp;
+};
+
+
+struct iio_detected_event_list {
+ struct list_head list;
+ struct iio_event_data ev;
+ /* Part of shared event handling - (typicaly ring buffers) */
+ struct iio_shared_ev_pointer *shared_pointer;
+};
+
+/* Requires high resolution timers */
+/* TODO - provide alternative if not available? */
+static inline s64 iio_get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
+
+struct iio_dev;
+
+/* Each device has one of these per interrupt */
+struct iio_event_handler_list {
+ struct list_head list;
+ int (*handler)(struct iio_dev *dev_io, int index, s64 timestamp,
+ int no_test);
+ /* This element may be shared */
+ int refcount;
+};
+
+/* Wraps adding to lists and does reference counting to allowed shared
+ * handlers.
+ */
+int iio_add_event_to_list(struct iio_event_handler_list *list,
+ struct iio_event_handler_list *el);
+
+int iio_remove_event_from_list(struct iio_event_handler_list *el);
+
+struct iio_sw_ring_buffer;
+struct iio_hw_ring_buffer;
+
+#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) { \
+ (ring_buf)->size = _bytes_per_datum; \
+ (ring_buf)->length = _length; \
+ (ring_buf)->loopcount = 0; \
+ (ring_buf)->shared_ev_pointer.ev_p = 0; \
+ (ring_buf)->shared_ev_pointer.lock = \
+ __SPIN_LOCK_UNLOCKED((ring_buf) \
+ ->shared_ev_pointer->loc); \
+ }
+
+#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) { \
+ INIT_IIO_RING_BUFFER(&(ring)->buf, \
+ _bytes_per_datum, \
+ _length); \
+ (ring)->read_p = 0; \
+ (ring)->write_p = 0; \
+ (ring)->last_written_p = 0; \
+ (ring)->data = kmalloc(_length*(ring)->buf.size, \
+ GFP_KERNEL); \
+ (ring)->use_count = 0; \
+ (ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
+ }
+
+#define FREE_IIO_SW_RING_BUFFER(ring) kfree((ring)->data)
+
+
+
+int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
+ unsigned char *data,
+ s64 timestamp);
+
+/* Edge cases :
+ 1) data at last_p is no longer valid - requires complete wrap around.
+ To detect, loop count has changed - if only by 1 then problem only
+ if current_lastp is equal to or greater than copy made at start.
+ If we have wrapped an entire int in this time (loopcount) then
+ something very very weird has occured!
+*/
+int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
+ unsigned char *data);
+
+/* Up to the drivers to mark the ring whenever it must not change size
+ * and unmark when it may without problems */
+void iio_mark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring);
+
+void iio_unmark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring);
+
+int iio_request_sw_ring_buffer(int bytes_per_datum,
+ int length,
+ struct iio_sw_ring_buffer **ring,
+ int id,
+ struct module *owner,
+ struct device *dev);
+
+int iio_request_update_sw_ring_buffer(struct iio_dev *dev_info, int id);
+
+void iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
+ struct device *dev);
+
+int iio_request_hw_ring_buffer(int bytes_per_datum,
+ int length,
+ struct iio_hw_ring_buffer **ring,
+ int id,
+ struct module *owner,
+ struct device *dev,
+ const struct file_operations *fops,
+ void *private);
+
+void iio_free_hw_ring_buffer(struct iio_hw_ring_buffer *ring,
+ struct device *dev);
+
+/* Device operating modes */
+#define INDIO_DIRECT_MODE 0x01
+#define INDIO_RING_POLLED 0x02
+#define INDIO_RING_DATA_RDY 0x04
+#define INDIO_RING_HARDWARE_BUFFER 0x08
+
+struct iio_event_interface {
+ struct iio_handler handler;
+ wait_queue_head_t wait;
+ struct mutex event_list_lock;
+ struct iio_detected_event_list det_events;
+ int max_events;
+ int current_events;
+ /* Integer id, used to differentiate this one form any others */
+ int id;
+ struct iio_chrdev_minor_attr attr;
+ struct module *owner;
+ void *private;
+ /* used to store name for associated sysfs file */
+ char _name[20];
+};
+
+struct iio_shared_ev_pointer {
+ struct iio_detected_event_list *ev_p;
+ spinlock_t lock;
+};
+
+/* A general ring buffer structure
+ * Intended to be completely lock free as we always want fills from
+ * the interrupt handler to not have to wait. This obviously increases
+ * the possible time required to read from the buffer. */
+struct iio_ring_buffer {
+ /* Number of datums */
+ int length;
+ /* length of single datum - including timestamp if there */
+ int size;
+ int loopcount;
+ /* accessing the ring buffer */
+ char *access_minor_name;
+ struct iio_chrdev_minor_attr access_minor_attr;
+ struct iio_handler access_handler;
+ /* events triggered by the ring buffer */
+ char *event_minor_name;
+ struct iio_event_interface ev_int;
+ /* a fully shared output event ? wtf?*/
+ struct iio_shared_ev_pointer shared_ev_pointer;
+};
+
+int iio_put_ring_event(struct iio_ring_buffer *ring_buf,
+ int event_code,
+ s64 timestamp);
+
+int iio_put_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
+ int event_code,
+ s64 timestamp);
+
+struct iio_sw_ring_buffer {
+ struct iio_ring_buffer buf;
+ unsigned char *data;
+ unsigned char *read_p;
+ unsigned char *write_p;
+ unsigned char *last_written_p;
+ /* used to act as a point at which to signal an event */
+ unsigned char *half_p;
+ int use_count;
+ int update_needed;
+ spinlock_t use_lock;
+};
+
+struct iio_hw_ring_buffer {
+ struct iio_ring_buffer buf;
+ void *private;
+};
+
+/* Vast majority of this is set by the industrialio subsystem on a
+ * call to iio_device_register. */
+/* TODO Macros to simplify setting the relevant stuff in the driver. */
+struct iio_dev {
+/* generic handling data used by ind io */
+ int id;
+/* device specific data */
+ void *dev_data;
+
+/* Modes the drivers supports */
+ int modes; /* Driver Set */
+ int currentmode;
+/* Direct sysfs related functionality */
+ struct device *sysfs_dev;
+ struct device *dev; /* Driver Set */
+ /* General attributes */
+ const struct attribute_group *attrs;
+
+/* Interrupt handling related */
+ struct module *driver_module;
+ int num_interrupt_lines; /* Driver Set */
+
+ struct iio_interrupt **interrupts;
+
+
+ /* Event control attributes */
+ struct attribute_group *event_attrs;
+ /* The character device related elements */
+ struct iio_event_interface *event_interfaces;
+
+/* Software Ring Buffer
+ - for now assuming only makes sense to have a single ring */
+ int ring_dimension;
+ int ring_bytes_per_datum;
+ int ring_length;
+ struct iio_sw_ring_buffer *ring;
+ struct attribute_group *ring_attrs_group;
+ struct iio_ring_attr *ring_attrs;
+ /* enabling / disabling related functions.
+ * post / pre refer to relative to the change of current_mode. */
+ int (*ring_preenable)(struct iio_dev *);
+ int (*ring_postenable)(struct iio_dev *);
+ int (*ring_predisable)(struct iio_dev *);
+ int (*ring_postdisable)(struct iio_dev *);
+ void (*ring_poll_func)(void *private_data);
+ struct iio_periodic *ptimer;
+
+ /* Device state lock.
+ * Used to prevent simultaneous changes to device state.
+ * In here rather than modules as some ring buffer changes must occur
+ * with this locked.*/
+ struct mutex mlock;
+
+ /* Name used to allow releasing of the relevant ptimer on exit.
+ * Ideally the ptimers will only be held when the driver is actually
+ * using them, but for now they have one the whole time they are loaded.
+ */
+ const char *ptimer_name;
+};
+
+int iio_device_register(struct iio_dev *dev_info);
+void iio_device_unregister(struct iio_dev *dev_info);
+
+/* Wrapper class used to allow easy specification of different line numbers */
+struct iio_interrupt {
+ struct iio_dev *dev_info;
+ int line_number;
+ int irq;
+ struct iio_event_handler_list ev_list;
+};
+
+irqreturn_t iio_interrupt_handler(int irq, void *_int_info);
+
+int iio_register_interrupt_line(unsigned int irq,
+ struct iio_dev *dev_info,
+ int line_number,
+ unsigned long type,
+ const char *name);
+
+void iio_unregister_interrupt_line(struct iio_dev *dev_info,
+ int line_number);
+
+
+/* Used to try inserting an event into the list for userspace reading via
+ * chrdev */
+int iio_put_event(struct iio_dev *dev_info,
+ int ev_line,
+ int ev_code,
+ s64 timestamp);
+
+struct iio_work_cont {
+ struct work_struct ws;
+ struct work_struct ws_nocheck;
+ int address;
+ int mask;
+ void *st;
+};
+#define INIT_IIO_WORK_CONT(cont, _checkfunc, _nocheckfunc, _add, _mask, _st)\
+ do { \
+ INIT_WORK(&(cont)->ws, _checkfunc); \
+ INIT_WORK(&(cont)->ws_nocheck, _nocheckfunc); \
+ (cont)->address = _add; \
+ (cont)->mask = _mask; \
+ (cont)->st = _st; \
+ } while (0)
+
+/* Ring buffer related */
+int iio_device_register_sw_ring(struct iio_dev *dev_info, int id);
+void iio_device_unregister_sw_ring(struct iio_dev *dev_info);
+
+int __iio_put_event(struct iio_event_interface *ev_int,
+ int ev_code,
+ s64 timestamp,
+ struct iio_shared_ev_pointer*
+ shared_pointer_p);
+void __iio_change_event(struct iio_detected_event_list *ev,
+ int ev_code,
+ s64 timestamp);
+
+int iio_setup_ev_int(struct iio_event_interface *ev_int,
+ const char *name,
+ struct module *owner,
+ struct device *dev);
+
+void iio_free_ev_int(struct iio_event_interface *ev_int, struct device *dev);
+
+int iio_allocate_chrdev(struct iio_handler *handler);
+void iio_deallocate_chrdev(struct iio_handler *handler);
+
+ssize_t iio_show_attr_minor(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+/* For now this is on the type of periodic timer used*/
+struct iio_periodic {
+ struct rtc_device *rtc;
+ int frequency;
+ struct rtc_task task;
+};
+
+int iio_ptimer_request_periodic_timer(char *name, struct iio_dev *indio_dev);
+void iio_ptimer_unrequest_periodic_timer(struct iio_dev *indio_dev);
+int iio_ptimer_set_freq(struct iio_periodic *ptimer, unsigned frequency);
+int iio_ptimer_irq_set_state(struct iio_dev *indio_dev, bool state);
+
+/* Board registration is handled by contents of
+ * industrialio_ptimer_board_info.c
+ */
+extern struct mutex industrialio_ptimer_board_lock;
+extern struct list_head industrialio_ptimer_board_info_list;
+#endif /* _INDUSTRIAL_IO_H_ */
--- a/include/linux/industrialio_ptimer.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/industrialio_ptimer.h 2008-07-23 15:41:29.000000000 +0100
@@ -0,0 +1,18 @@
+#ifndef _INDUSTRIALIO_PTIMER_H_
+#define _INDUSTRIALIO_PTIMER_H_
+
+#define IIO_PTIMER_NAME_SIZE 10
+
+
+struct ptimer_info {
+ char name[IIO_PTIMER_NAME_SIZE];
+};
+struct ptimer_info_listel {
+ struct list_head list;
+ bool inuse;
+ struct ptimer_info info;
+};
+
+extern int
+industrialio_register_ptimer(struct ptimer_info const *info, unsigned n);
+#endif
--- a/include/linux/industrialio_sysfs.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/industrialio_sysfs.h 2008-07-23 16:04:18.000000000 +0100
@@ -0,0 +1,274 @@
+/* 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.
+ *
+ * General attributes
+ */
+
+#ifndef _INDUSTRIAL_IO_SYSFS_H_
+#define _INDUSTRIAL_IO_SYSFS_H_
+
+#include <linux/industrialio.h>
+
+
+struct iio_event_attr {
+ struct device_attribute dev_attr;
+ int mask;
+ struct iio_event_handler_list *listel;
+};
+
+
+#define to_iio_event_attr(_dev_attr) \
+ container_of(_dev_attr, struct iio_event_attr, dev_attr)
+
+
+struct iio_chrdev_minor_attr {
+ struct device_attribute dev_attr;
+ int minor;
+};
+
+void
+__init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
+ const char *name,
+ struct module *owner,
+ int id);
+
+
+#define to_iio_chrdev_minor_attr(_dev_attr) \
+ container_of(_dev_attr, struct iio_chrdev_minor_attr, dev_attr);
+
+struct iio_dev_attr {
+ struct device_attribute dev_attr;
+ int address;
+};
+
+
+#define to_iio_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct iio_dev_attr, dev_attr)
+
+/* Some attributes will be hard coded (device dependant) and not require an
+ address, in these cases pass a negative */
+#define IIO_ATTR(_name, _mode, _show, _store, _addr) \
+ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
+ .address = _addr }
+
+#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \
+ struct iio_dev_attr iio_dev_attr_##_name \
+ = IIO_ATTR(_name, _mode, _show, _store, _addr)
+
+/* This may get broken down into separate files later */
+
+/* Generic attributes of onetype or another */
+
+/* Revision number for the device. As the form of this varies greatly from
+ * device to device, no particular form is specified. In most cases this will
+ * only be for information to the user, not to effect functionality etc.
+ */
+#define IIO_DEV_ATTR_REV(_show) \
+ IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0)
+
+/* For devices with internal clocks - and possibly poling later */
+
+#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store) \
+ IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0)
+
+#define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show) \
+ IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0)
+
+/* ADC types of attibute */
+
+#define IIO_DEV_ATTR_AVAIL_SCAN_MODES(_show) \
+ IIO_DEVICE_ATTR(available_scan_modes, S_IRUGO, _show, NULL, 0)
+
+#define IIO_DEV_ATTR_SCAN_MODE(_mode, _show, _store) \
+ IIO_DEVICE_ATTR(scan_mode, _mode, _show, _store, 0)
+
+#define IIO_DEV_ATTR_INPUT(_number, _show) \
+ IIO_DEVICE_ATTR(in##_number, S_IRUGO, _show, NULL, _number)
+
+#define IIO_DEV_ATTR_SCAN(_show) \
+ IIO_DEVICE_ATTR(scan, S_IRUGO, _show, NULL, 0);
+/* Accelerometer types of attribute */
+
+#define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(x_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(y_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(z_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(x_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(y_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
+
+
+/* The actual device readings are always going to be read only */
+#define IIO_DEV_ATTR_ACCEL_X(_show, _addr) \
+ IIO_DEVICE_ATTR(x, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y(_show, _addr) \
+ IIO_DEVICE_ATTR(y, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z(_show, _addr) \
+ IIO_DEVICE_ATTR(z, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_TEMP(_show) \
+ IIO_DEVICE_ATTR(temp, S_IRUGO, _show, NULL, 0)
+/* Thresholds are somewhat chip dependent - may need quite a few defs here */
+/* For unified thesholds (shared across all directions */
+#define IIO_DEV_ATTR_ACCEL_THRESH(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(thresh, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_X(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(thresh_x, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_Y(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(thresh_y, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_Z(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(thresh_z, _mode, _show, _store, _addr)
+
+/* This is an event attr in some case and a dev in others - FIX*/
+#define IIO_DEV_ATTR_SW_RING_ENABLE(_show, _store) \
+ IIO_DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
+
+/* Hardware ring buffer related attributes */
+#define IIO_DEV_ATTR_HW_RING_ENABLE(_show, _store) \
+ IIO_DEVICE_ATTR(hw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
+
+#define IIO_DEV_ATTR_RING_BPS(_mode, _show, _store) \
+ IIO_DEVICE_ATTR(ring_bps, _mode, _show, _store, 0)
+
+/* Bits per sample */
+#define IIO_DEV_ATTR_RING_BPS_AVAILABLE(_show) \
+ IIO_DEVICE_ATTR(ring_bps_available, S_IRUGO, _show, NULL, 0)
+
+/* Events that the device may generate */
+
+#define IIO_EVENT_SH(_name, _handler) \
+ static struct iio_event_handler_list \
+ iio_event_##_name = { \
+ .handler = _handler, \
+ .refcount = 0, \
+ };
+#define IIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask) \
+ static struct iio_event_attr \
+ iio_event_attr_##_name \
+ = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store),\
+ .mask = _mask,\
+ .listel = &_ev_list };
+
+/*FIXME use the above to define this */
+#define IIO_EVENT_ATTR(_name, _show, _store, _mask, _handler) \
+ static struct iio_event_handler_list \
+ iio_event_##_name = { \
+ .handler = _handler, \
+ }; \
+ static struct \
+ iio_event_attr \
+ iio_event_attr_##_name \
+ = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store), \
+ .mask = _mask, \
+ .listel = &iio_event_##_name }; \
+/*FIXME, add line number to the above?*/
+
+/* In most of these cases, this actually corresponds to something with a
+ value attached */
+
+/* For some devices you can select whether all conditions or any condition
+ must be met for interrupt generation */
+#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_DATA_RDY 100
+
+/* Threshold pass events */
+#define IIO_EVENT_ATTR_ACCEL_X_HIGH(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(x_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_ACCEL_X_HIGH 1
+
+/* Shared handler version */
+#define IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(_evlist, _show, _store, _mask)\
+ IIO_EVENT_ATTR_SH(x_high, _evlist, _show, _store, _mask)
+
+
+#define IIO_EVENT_ATTR_ACCEL_Y_HIGH(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(y_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(_evlist, _show, _store, _mask)\
+ IIO_EVENT_ATTR_SH(y_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_HIGH 2
+
+#define IIO_EVENT_ATTR_ACCEL_Z_HIGH(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(z_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(_evlist, _show, _store, _mask)\
+ IIO_EVENT_ATTR_SH(z_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_HIGH 3
+
+#define IIO_EVENT_ATTR_ACCEL_X_LOW(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(x_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_X_LOW_SH(_evlist, _show, _store, _mask)\
+ IIO_EVENT_ATTR_SH(x_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_X_LOW 4
+
+#define IIO_EVENT_ATTR_ACCEL_Y_LOW(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(y_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(_evlist, _show, _store, _mask)\
+ IIO_EVENT_ATTR_SH(y_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_LOW 5
+
+#define IIO_EVENT_ATTR_ACCEL_Z_LOW(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(z_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(z_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_LOW 6
+
+#define IIO_EVENT_ATTR_FREE_FALL_DETECT(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(free_fall, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(free_fall, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_FREE_FALL 7
+
+/* These may be software or hardware events depending on type of ring buffer */
+
+#define IIO_EVENT_ATTR_RING_50_FULL(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(ring_50_full, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_RING_50_FULL_SH(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(ring_50_full, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_RING_75_FULL_SH(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(ring_75_full, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_SW_RING_ENABLE(_show, _store, _mask, _handler) \
+ IIO_EVENT_ATTR(sw_ring_enable, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_RING_50_FULL 100
+#define IIO_EVENT_CODE_RING_75_FULL 101
+#define IIO_EVENT_CODE_RING_100_FULL 102
+/* HOW TO HANDLE COMPOSITE EVENTS? */
+
+#endif /* _INDUSTRIAL_IO_SYSFS_H_ */
--- a/drivers/industrialio/industrialio_ptimer_board_info.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/industrialio_ptimer_board_info.c 2008-07-23 14:16:28.000000000 +0100
@@ -0,0 +1,44 @@
+/* The industrial I/O periodic timer registration code
+ *
+ * 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/industrialio_ptimer.h>
+
+LIST_HEAD(industrialio_ptimer_board_info_list);
+EXPORT_SYMBOL_GPL(industrialio_ptimer_board_info_list);
+
+DEFINE_MUTEX(industrialio_ptimer_board_lock);
+EXPORT_SYMBOL_GPL(industrialio_ptimer_board_lock);
+
+
+int __init
+industrialio_register_ptimer(struct ptimer_info const *info, unsigned n)
+{
+ int i;
+ struct ptimer_info_listel *pi;
+
+ mutex_lock(&industrialio_ptimer_board_lock);
+ for (i = 0; i < n; i++) {
+ pi = kzalloc(sizeof(*pi), GFP_KERNEL);
+ if (!pi) {
+ mutex_unlock(&industrialio_ptimer_board_lock);
+ return -ENOMEM;
+ }
+ strncpy(pi->info.name, info[i].name, IIO_PTIMER_NAME_SIZE);
+ list_add_tail(&pi->list, &industrialio_ptimer_board_info_list);
+ }
+ mutex_unlock(&industrialio_ptimer_board_lock);
+
+ return 0;
+}
--- a/drivers/industrialio/industrialio-core.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/industrialio-core.c 2008-07-23 15:07:21.000000000 +0100
@@ -0,0 +1,787 @@
+/* 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/rtc.h>
+
+#include <linux/industrialio.h>
+#include <linux/industrialio_ptimer.h>
+
+MODULE_AUTHOR("Jonathan Cameron <[email protected]>");
+MODULE_DESCRIPTION("Industrial I/O core");
+MODULE_LICENSE("GPL");
+
+#define IIO_ID_PREFIX "industrialio"
+#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
+#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 used to maintain internal state about industrialio.
+ * This will be used to handle the character device accesses
+ * and redirect them to the relevant driver.
+ * Will reduce this to the included table if nothing else comes
+ * up that should go in here!
+ */
+struct __iio_state {
+ /* All initially set to NULL in init */
+ struct iio_handler *fhs[256];
+};
+
+static struct __iio_state iio_state;
+static DEFINE_SPINLOCK(iio_state_lock);
+
+/* Used to escalate shared event.
+ * Currently this is only used with ring buffer events.
+ */
+void __iio_change_event(struct iio_detected_event_list *ev,
+ int ev_code,
+ s64 timestamp)
+{
+ ev->ev.id = ev_code;
+ ev->ev.timestamp = timestamp;
+}
+
+/* Used both in the interrupt line put events and the ring buffer ones */
+ int
+__iio_put_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;
+
+ /* 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);
+ }
+
+ return 0;
+error_ret:
+ return ret;
+
+}
+
+int iio_put_event(struct iio_dev *dev_info,
+ int ev_line,
+ int ev_code,
+ s64 timestamp)
+{
+ return __iio_put_event(&dev_info->event_interfaces[ev_line],
+ ev_code, timestamp, NULL);
+}
+EXPORT_SYMBOL(iio_put_event);
+
+/* 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;
+ }
+
+ INIT_LIST_HEAD(&dev_info->interrupts[line_number]->ev_list.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 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);
+
+/* 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);
+
+/* 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;
+ s64 time_ns;
+
+ if (list_empty(&int_info->ev_list.list))
+ return IRQ_NONE;
+
+ time_ns = iio_get_time_ns();
+ /* detect single element list*/
+ if (int_info->ev_list.list.next->next == &int_info->ev_list.list) {
+ disable_irq_nosync(irq);
+ p = list_first_entry(&int_info->ev_list.list,
+ struct iio_event_handler_list,
+ list);
+ p->handler(dev_info, 1, time_ns, 1);
+ } else
+ list_for_each_entry(p, &int_info->ev_list.list, list) {
+ disable_irq_nosync(irq);
+ p->handler(dev_info, 1, time_ns, 0);
+ }
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(iio_interrupt_handler);
+
+int iio_add_event_to_list(struct iio_event_handler_list *list,
+ struct iio_event_handler_list *el)
+{
+ if (el->refcount == 0)
+ list_add(&list->list, &el->list);
+ el->refcount++;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_add_event_to_list);
+
+int iio_remove_event_from_list(struct iio_event_handler_list
+ *el)
+{
+ el->refcount--;
+ if (el->refcount == 0)
+ list_del_init(&el->list);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(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 */
+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 unlike input 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;
+}
+
+
+/* The main file ops structure. All open calls on the major number will
+ * be handled by this with fops for the actual minor number assigned by
+ * switching function above */
+static const struct file_operations iio_fops = {
+ .owner = THIS_MODULE,
+ .open = iio_open_file,
+};
+
+ssize_t iio_interrupt_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_interrupt_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_interrupt_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_interrupt_fileops = {
+ .read = iio_interrupt_read,
+ .release = iio_interrupt_release,
+ .open = iio_interrupt_open,
+ .owner = THIS_MODULE,
+};
+
+
+ssize_t iio_show_attr_minor(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+
+ struct iio_chrdev_minor_attr *_attr
+ = to_iio_chrdev_minor_attr(attr);
+ len = sprintf(buf, "%d\n", _attr->minor);
+
+ return len;
+}
+
+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_interrupt_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, "bob");
+ class_unregister(&iio_class);
+}
+
+int iio_device_register_sysfs(struct iio_dev *dev_info)
+{
+ int ret;
+
+ dev_info->sysfs_dev = device_create(&iio_class,
+ dev_info->dev,
+ MKDEV(0, 0),
+ IIO_ID_FORMAT,
+ dev_info->id);
+
+ if (IS_ERR(dev_info->sysfs_dev)) {
+ /* what would correct error here be?*/
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ /* register attributes */
+ ret = sysfs_create_group(&dev_info->dev->kobj, dev_info->attrs);
+ if (ret) {
+ dev_err(dev_info->dev, "Failed to register sysfs hooks\n");
+ goto error_free_sysfs_device;
+ }
+
+ return 0;
+
+error_free_sysfs_device:
+ device_unregister(dev_info->dev);
+error_ret:
+ return ret;
+}
+
+void iio_device_unregister_sysfs(struct iio_dev *dev_info)
+{
+ sysfs_remove_group(&dev_info->dev->kobj, dev_info->attrs);
+ device_unregister(dev_info->sysfs_dev);
+}
+
+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;
+}
+void iio_device_unregister_id(struct iio_dev *dev_info)
+{
+ /* Can I use the save id? */
+ int id;
+
+ if (likely(sscanf(dev_info->sysfs_dev->bus_id,
+ IIO_ID_FORMAT, &id) == 1)) {
+ spin_lock(&iio_idr_lock);
+ idr_remove(&iio_idr, id);
+ spin_unlock(&iio_idr_lock);
+ } else
+ dev_dbg(dev_info->dev->parent,
+ "indio_device_unregister() failed: bad class ID!\n");
+}
+
+int iio_device_register_eventset(struct iio_dev *dev_info)
+{
+ int ret, i, j;
+
+ struct device_attribute *devattr;
+ struct iio_event_attr *indio_devattr;
+
+ 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->dev);
+ if (ret) {
+ dev_err(dev_info->dev,
+ "Could not get chrdev interface\n");
+ goto error_free_setup_ev_ints;
+ }
+ }
+ dev_info->event_attrs->name = "event_sources";
+ ret = sysfs_create_group(&dev_info->dev->kobj, dev_info->event_attrs);
+ if (ret) {
+ dev_err(dev_info->dev,
+ "Failed to register sysfs hooks for events attributes");
+ goto error_free_setup_ev_ints;
+ }
+ /* May double initialize lists in case of shared handlers,
+ but other than a slight overhead that isn't a problem */
+ j = 0;
+ while (1) {
+ if (dev_info->event_attrs->attrs[j] == NULL)
+ break;
+ devattr = container_of(dev_info->event_attrs->attrs[j],
+ struct device_attribute, attr);
+ indio_devattr = to_iio_event_attr(devattr);
+ INIT_LIST_HEAD(&indio_devattr->listel->list);
+ j++;
+ }
+ return 0;
+
+error_free_setup_ev_ints:
+ for (j = 0; j < i; j++)
+ iio_free_ev_int(&dev_info->event_interfaces[j],
+ dev_info->dev);
+ kfree(dev_info->interrupts);
+error_free_event_interfaces:
+ kfree(dev_info->event_interfaces);
+error_ret:
+ return ret;
+}
+
+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++)
+ iio_free_ev_int(&dev_info->event_interfaces[i],
+ dev_info->dev);
+ if (dev_info->event_attrs)
+ sysfs_remove_group(&dev_info->dev->kobj, dev_info->event_attrs);
+ kfree(dev_info->event_interfaces);
+}
+
+int iio_get_ptimer(const char **name)
+{
+ struct ptimer_info_listel *ptimer_i;
+
+ *name = NULL;
+ mutex_lock(&industrialio_ptimer_board_lock);
+
+ list_for_each_entry(ptimer_i, &industrialio_ptimer_board_info_list,
+ list)
+ if (ptimer_i->inuse == false) {
+ ptimer_i->inuse = true;
+ *name = ptimer_i->info.name;
+ break;
+ }
+ mutex_unlock(&industrialio_ptimer_board_lock);
+ if (*name == NULL)
+ return -EINVAL;
+
+ return 0;
+}
+int iio_free_ptimer(const char *name)
+{
+ struct ptimer_info_listel *ptimer_i;
+
+ mutex_lock(&industrialio_ptimer_board_lock);
+ list_for_each_entry(ptimer_i, &industrialio_ptimer_board_info_list,
+ list)
+ if (ptimer_i->info.name == name) {
+ ptimer_i->inuse = false;
+ break;
+ }
+ mutex_unlock(&industrialio_ptimer_board_lock);
+
+ return 0;
+}
+
+int iio_device_register_ptimer(struct iio_dev *dev_info)
+{
+ int ret = 0;
+
+ if (dev_info->modes & INDIO_RING_POLLED) {
+ ret = iio_get_ptimer(&dev_info->ptimer_name);
+ if (ret)
+ goto error_ret;
+ ret = iio_ptimer_request_periodic_timer((char *)
+ (dev_info->ptimer_name),
+ dev_info);
+ if (ret)
+ goto error_release_ptimer;
+ }
+
+ return ret;
+
+error_release_ptimer:
+ iio_free_ptimer(dev_info->ptimer_name);
+error_ret:
+ return ret;
+
+}
+
+void iio_device_unregister_ptimer(struct iio_dev *dev_info)
+{
+ if (dev_info->ptimer) {
+ iio_ptimer_unrequest_periodic_timer(dev_info);
+ iio_free_ptimer(dev_info->ptimer_name);
+ }
+}
+
+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)
+ goto error_nothing;
+
+/* Create sysfs device */
+ ret = iio_device_register_sysfs(dev_info);
+ if (ret)
+ goto error_free_idr;
+
+/* Interrupt triggered events setup */
+ ret = iio_device_register_eventset(dev_info);
+ if (ret)
+ goto error_free_sysfs;
+
+/* Ring buffer init if relevant */
+ if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY)) {
+
+ ret = iio_device_register_sw_ring(dev_info, 0);
+ if (ret)
+ goto error_free_eventset;
+ }
+/* Register ptimer if relevant */
+ if (dev_info->modes & INDIO_RING_POLLED) {
+ ret = iio_device_register_ptimer(dev_info);
+ if (ret)
+ goto error_unregister_sw_ring;
+ }
+
+ return 0;
+
+error_unregister_sw_ring:
+ iio_device_unregister_sw_ring(dev_info);
+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_GPL(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *dev_info)
+{
+ if (dev_info->modes & INDIO_RING_POLLED)
+ iio_device_unregister_ptimer(dev_info);
+ if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY))
+ iio_device_unregister_sw_ring(dev_info);
+ iio_device_unregister_eventset(dev_info);
+ iio_device_unregister_sysfs(dev_info);
+ iio_device_unregister_id(dev_info);
+
+}
+EXPORT_SYMBOL_GPL(iio_device_unregister);
+
+subsys_initcall(iio_init);
+module_exit(iio_exit);
--- a/drivers/industrialio/industrialio-rtc.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/industrialio-rtc.c 2008-07-23 15:08:43.000000000 +0100
@@ -0,0 +1,134 @@
+/* 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+#include <linux/industrialio_ptimer.h>
+/* This is a temporary stop gap until a more generic timer subsystem is in place
+ * within the kernel.
+ * See discussion (initial thoughts so far) on the rtc mailing list.
+ * Comments still welcomed though I may not do much about them!.
+ */
+int iio_ptimer_irq_set_state(struct iio_dev *indio_dev, bool state)
+{
+ return rtc_irq_set_state(indio_dev->ptimer->rtc,
+ &indio_dev->ptimer->task,
+ state);
+}
+EXPORT_SYMBOL(iio_ptimer_irq_set_state);
+
+int iio_ptimer_set_freq(struct iio_periodic *ptimer,
+ unsigned frequency)
+{
+ int ret;
+
+ ret = rtc_irq_set_freq(ptimer->rtc, &ptimer->task, frequency);
+ if (ret == 0)
+ ptimer->frequency = frequency;
+
+ return ret;
+}
+EXPORT_SYMBOL(iio_ptimer_set_freq);
+
+static ssize_t iio_ptimer_show_samp_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct iio_periodic *ptimer = dev_info->ptimer;
+ return sprintf(buf, "%u\n", ptimer->frequency);
+}
+
+static ssize_t iio_ptimer_store_samp_freq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct iio_periodic *ptimer = dev_info->ptimer;
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = iio_ptimer_set_freq(ptimer, val);
+ if (ret)
+ goto error_ret;
+ return len;
+
+error_ret:
+ return ret;
+}
+
+
+IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+ iio_ptimer_show_samp_freq,
+ iio_ptimer_store_samp_freq);
+
+int iio_ptimer_request_periodic_timer(char *name,
+ struct iio_dev *indio_dev)
+{
+ int ret;
+
+ indio_dev->ptimer = kmalloc(sizeof(struct iio_periodic), GFP_KERNEL);
+ indio_dev->ptimer->rtc = rtc_class_open(name);
+ if (indio_dev->ptimer == NULL) {
+ ret = -EINVAL;
+ goto error_free_ptimer;
+ }
+ indio_dev->ptimer->task.func = indio_dev->ring_poll_func;
+ indio_dev->ptimer->task.private_data = indio_dev;
+ ret = rtc_irq_register(indio_dev->ptimer->rtc,
+ &indio_dev->ptimer->task);
+ if (ret)
+ goto error_close_class;
+
+ ret = sysfs_add_file_to_group(&indio_dev->dev->kobj,
+ &iio_dev_attr_sampling_frequency
+ .dev_attr.attr,
+ "ring_buffer");
+ if (ret)
+ goto error_unregister_irq;
+
+ return 0;
+
+error_unregister_irq:
+ rtc_irq_unregister(indio_dev->ptimer->rtc, &indio_dev->ptimer->task);
+
+error_close_class:
+ rtc_class_close(indio_dev->ptimer->rtc);
+error_free_ptimer:
+ kfree(indio_dev->ptimer);
+ indio_dev->ptimer = NULL;
+ return ret;
+}
+EXPORT_SYMBOL(iio_ptimer_request_periodic_timer);
+
+void iio_ptimer_unrequest_periodic_timer(struct iio_dev *indio_dev)
+{
+
+ sysfs_remove_file_from_group(&indio_dev->dev->kobj,
+ &iio_dev_attr_sampling_frequency
+ .dev_attr.attr,
+ "ring_buffer");
+
+ if (indio_dev->ptimer->rtc) {
+ rtc_irq_set_state(indio_dev->ptimer->rtc,
+ &indio_dev->ptimer->task, 0);
+ rtc_irq_unregister(indio_dev->ptimer->rtc,
+ &indio_dev->ptimer->task);
+ flush_scheduled_work();
+ rtc_class_close(indio_dev->ptimer->rtc);
+ flush_scheduled_work();
+ }
+ kfree(indio_dev->ptimer);
+}
+EXPORT_SYMBOL(iio_ptimer_unrequest_periodic_timer);
--- a/drivers/industrialio/industrialio-ring.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/industrialio-ring.c 2008-07-23 15:10:39.000000000 +0100
@@ -0,0 +1,770 @@
+/* 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.
+ *
+ * Handling of ring allocation / resizing.
+ *
+ *
+ * Things to look at here.
+ * - Better memory allocation techniques?
+ * - Alternative access techniques?
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+
+/* Prevent resizing of the ring if it might break anything */
+void iio_mark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring)
+{
+ spin_lock(ring->use_lock);
+ ring->use_count++;
+ spin_unlock(ring->use_lock);
+}
+EXPORT_SYMBOL_GPL(iio_mark_sw_ring_buffer_in_use);
+
+void iio_unmark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring)
+{
+ spin_lock(ring->use_lock);
+ ring->use_count--;
+ spin_unlock(ring->use_lock);
+}
+EXPORT_SYMBOL_GPL(iio_unmark_sw_ring_buffer_in_use);
+
+/* Mark that a resize is needed */
+static void iio_mark_sw_ring_buffer_need_update(struct iio_sw_ring_buffer *ring)
+{
+ spin_lock(ring->use_lock);
+ ring->update_needed = 1;
+ spin_unlock(ring->use_lock);
+}
+
+/* Event handling for the ring - allows escallation of events */
+int iio_put_ring_event(struct iio_ring_buffer *ring_buf,
+ int event_code,
+ s64 timestamp)
+{
+ return __iio_put_event(&ring_buf->ev_int,
+ event_code,
+ timestamp,
+ &ring_buf->shared_ev_pointer);
+}
+EXPORT_SYMBOL(iio_put_ring_event);
+
+int iio_put_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
+ int event_code,
+ s64 timestamp)
+{
+ if (ring_buf->shared_ev_pointer.ev_p)
+ __iio_change_event(ring_buf->shared_ev_pointer.ev_p,
+ event_code,
+ timestamp);
+ else
+ return iio_put_ring_event(ring_buf,
+ event_code,
+ timestamp);
+ return 0;
+}
+EXPORT_SYMBOL(iio_put_or_escallate_ring_event);
+
+
+/* Ring buffer related functionality */
+/* Store to ring is typically called in the bh of a data ready interrupt handler
+ * in the device driver */
+/* Lock always held if their is a chance this may be called */
+int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
+ unsigned char *data,
+ s64 timestamp)
+{
+ bool init_read = true;
+ int ret;
+ int code;
+
+ /* initial store */
+ if (unlikely(ring->write_p == 0)) {
+ ring->write_p = ring->data;
+ /* doesn't actually matter if this is out of the set */
+ ring->half_p = ring->data - ring->buf.length*ring->buf.size/2;
+ init_read = false;
+ }
+ memcpy(ring->write_p, data, ring->buf.size);
+ barrier();
+ ring->last_written_p = ring->write_p;
+ barrier();
+ ring->write_p += ring->buf.size;
+ /* End of ring, back to the beginning */
+ if (ring->write_p == ring->data + ring->buf.length*ring->buf.size) {
+ ring->write_p = ring->data;
+ ring->buf.loopcount++;
+ }
+ if (ring->read_p == 0)
+ ring->read_p = ring->data;
+ /* Buffer full - move the read pointer and create / escalate
+ * ring event */
+ else if (ring->write_p == ring->read_p) {
+ ring->read_p += ring->buf.size;
+ if (ring->read_p
+ == ring->data + ring->buf.length*ring->buf.size)
+ ring->read_p = ring->data;
+
+ spin_lock(ring->buf.shared_ev_pointer.lock);
+ if (ring->buf.shared_ev_pointer.ev_p) {
+ /* Event escalation - probably quicker to let this
+ keep running than check if it is necessary */
+ code = IIO_EVENT_CODE_RING_100_FULL;
+ __iio_change_event(ring
+ ->buf.shared_ev_pointer.ev_p,
+ code,
+ timestamp);
+ } else {
+ code = IIO_EVENT_CODE_RING_100_FULL;
+ ret = __iio_put_event(&ring->buf.ev_int,
+ code,
+ timestamp,
+ &ring
+ ->buf.shared_ev_pointer);
+ if (ret) {
+ spin_unlock(ring->buf.shared_ev_pointer.lock);
+ goto error_ret;
+ }
+ }
+ spin_unlock(ring->buf.shared_ev_pointer.lock);
+ }
+ /* investigate if our event barrier has been passed */
+ /* There are definite 'issues' with this and chances of
+ * simultaneous read */
+ /* Also need to use loop count to ensure this only happens once */
+ ring->half_p += ring->buf.size;
+ if (ring->half_p == ring->data + ring->buf.length*ring->buf.size)
+ ring->half_p = ring->data;
+ if (ring->half_p == ring->read_p) {
+ spin_lock(ring->buf.shared_ev_pointer.lock);
+ code = IIO_EVENT_CODE_RING_50_FULL;
+ ret = __iio_put_event(&ring->buf.ev_int,
+ code,
+ timestamp,
+ &ring->buf.shared_ev_pointer);
+ spin_unlock(ring->buf.shared_ev_pointer.lock);
+
+ if (ret)
+ goto error_ret;
+ }
+ return 0;
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_store_to_sw_ring);
+
+
+/*doesn't currently read the timestamp */
+/* For software ring buffers this function is needed to get the latest
+ * reading without preventing it from ending up in the ring buffer.
+*/
+int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
+ unsigned char *data)
+{
+ int loopcount_copy;
+ unsigned char *last_written_p_copy;
+ iio_mark_sw_ring_buffer_in_use(ring);
+again:
+ loopcount_copy = ring->buf.loopcount;
+ barrier();
+ last_written_p_copy = ring->last_written_p;
+ barrier(); /*unnessecary? */
+
+ memcpy(data, last_written_p_copy, ring->buf.size);
+
+ if (unlikely(loopcount_copy != ring->buf.loopcount)) {
+ if (unlikely(ring->last_written_p >= last_written_p_copy))
+ goto again;
+ }
+ iio_unmark_sw_ring_buffer_in_use(ring);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_read_last_from_sw_ring);
+
+/* Ring buffer access fileops */
+int iio_ring_open(struct inode *inode, struct file *filp)
+{
+ struct iio_sw_ring_buffer *ring = filp->private_data;
+
+ iio_mark_sw_ring_buffer_in_use(ring);
+ try_module_get(ring->buf.access_minor_attr.dev_attr.attr.owner);
+
+ return 0;
+}
+
+int iio_ring_release(struct inode *inode, struct file *filp)
+{
+ struct iio_sw_ring_buffer *ring = filp->private_data;
+
+ module_put(ring->buf.access_minor_attr.dev_attr.attr.owner);
+ clear_bit(IIO_BUSY_BIT_POS, &ring->buf.access_handler.flags);
+ iio_unmark_sw_ring_buffer_in_use(ring);
+
+ return 0;
+}
+
+/* no point in ripping more than nearest number of whole records below count */
+/* Depending on movement of pointers in the meantime this may return a lot
+ * less than count*/
+/* Also, we aren't going to wait for enough data to be available */
+
+/* Can only occur currently when the ring buffer is marked
+ - from userspace call */
+ssize_t iio_ring_rip(struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *f_ps)
+{
+ unsigned char *initial_read_p, *initial_write_p,
+ *current_read_p, *end_read_p;
+
+ struct iio_sw_ring_buffer *ring = filp->private_data;
+ unsigned char *data_cpy;
+ int ret;
+ int dead_offset;
+ int bytes_to_rip = 0;
+ int max_copied;
+ /* Round down to nearest datum boundary */
+ bytes_to_rip = (count - count % ring->buf.size);
+ /* Limit size to whole of ring buffer */
+ if (bytes_to_rip > ring->buf.size*ring->buf.length)
+ bytes_to_rip = ring->buf.size*ring->buf.length;
+ data_cpy = kmalloc(bytes_to_rip, GFP_KERNEL);
+ if (data_cpy == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ /* build local copy */
+ initial_read_p = ring->read_p;
+ if (unlikely(initial_read_p == 0)) {
+ /* No data here as yet */
+ ret = 0;
+ goto error_free_data_cpy;
+ }
+ initial_write_p = ring->write_p;
+
+ /* Need a consistent pair */
+ while (initial_read_p != ring->read_p
+ || initial_write_p != ring->write_p) {
+ initial_read_p = ring->read_p;
+ initial_write_p = ring->write_p;
+ }
+ if (initial_write_p == initial_read_p) {
+ /* No new data available.*/
+ ret = 0;
+ goto error_free_data_cpy;
+ }
+
+ if (initial_write_p > initial_read_p + bytes_to_rip) {
+ /* write_p is greater than necessary, all is easy */
+ max_copied = bytes_to_rip;
+ memcpy(data_cpy, initial_read_p, max_copied);
+ end_read_p = initial_read_p + max_copied;
+ } else if (initial_write_p > initial_read_p) {
+ /*not enough data to cpy */
+ max_copied = initial_write_p - initial_read_p;
+ memcpy(data_cpy, initial_read_p, max_copied);
+ end_read_p = initial_write_p;
+ } else { /* going through 'end' of ring buffer */
+ max_copied = ring->data
+ + ring->buf.length*ring->buf.size - initial_read_p;
+ memcpy(data_cpy, initial_read_p, max_copied);
+ if (initial_write_p > ring->data + bytes_to_rip - max_copied) {
+ /* enough data to finish */
+ memcpy(data_cpy + max_copied, ring->data,
+ bytes_to_rip - max_copied);
+ max_copied = bytes_to_rip;
+ end_read_p = ring->data + (bytes_to_rip - max_copied);
+ } else { /* not enough data */
+ memcpy(data_cpy + max_copied, ring->data,
+ initial_write_p - ring->data);
+ max_copied += initial_write_p - ring->data;
+ end_read_p = initial_write_p;
+ }
+ }
+ /* Now to verify which section was cleanly copied - i.e. how far
+ * read pointer has been pushed */
+ current_read_p = ring->read_p;
+
+ if (initial_read_p <= current_read_p)
+ dead_offset = current_read_p - initial_read_p;
+ else
+ dead_offset = ring->buf.length*ring->buf.size
+ - (initial_read_p - current_read_p);
+
+ /* possible issue if the initial write has been lapped or indeed
+ * the point we were reading to has been passed */
+ /* No valid data read.
+ * In this case the read pointer is already correct having been
+ * pushed further than we would look. */
+ if (max_copied - dead_offset < 0) {
+ ret = 0;
+ goto error_free_data_cpy;
+ }
+
+ /* setup the next read position */
+ ring->read_p = end_read_p;
+
+ if (copy_to_user(buf, data_cpy + dead_offset,
+ max_copied - dead_offset)) {
+ ret = -EFAULT;
+ goto error_free_data_cpy;
+ }
+ kfree(data_cpy);
+
+ return max_copied - dead_offset;
+
+error_free_data_cpy:
+ kfree(data_cpy);
+error_ret:
+ return 0;
+}
+
+static const struct file_operations iio_ring_fileops = {
+ .read = iio_ring_rip,
+ .release = iio_ring_release,
+ .open = iio_ring_open,
+ .owner = THIS_MODULE,
+};
+
+inline int __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
+ int id,
+ struct module *owner,
+ struct device *dev)
+{
+ int ret;
+
+/* Create and register the event character device */
+ buf->event_minor_name = kmalloc(20, GFP_KERNEL);
+ if (buf->event_minor_name == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ sprintf(buf->event_minor_name, "ring_buffer%d_ev_minor", id);
+ ret = iio_setup_ev_int(&(buf->ev_int),
+ (const char *)(buf->event_minor_name),
+ owner,
+ dev);
+ if (ret)
+ goto error_free_event_minor_name;
+
+ return 0;
+
+error_free_event_minor_name:
+ kfree(buf->event_minor_name);
+error_ret:
+ return ret;
+}
+
+inline void __iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
+ struct device *dev)
+{
+ iio_free_ev_int(&(buf->ev_int), dev);
+ kfree(buf->event_minor_name);
+}
+
+inline int
+__iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
+ int id,
+ struct module *owner,
+ struct device *dev,
+ const struct file_operations *fops)
+{
+ int ret;
+/* Create and register the access character device */
+ buf->access_minor_name = kmalloc(20, GFP_KERNEL);
+ if (buf->access_minor_name == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ sprintf(buf->access_minor_name, "ring_buffer%d_access_minor", id);
+
+ ret = iio_allocate_chrdev(&buf->access_handler);
+ if (ret)
+ goto error_free_access_minor_name;
+ buf->access_handler.fops = fops;
+ buf->access_handler.flags = 0;
+
+ __init_iio_chrdev_minor_attr(&buf->access_minor_attr,
+ (const char *)(buf->access_minor_name),
+ owner,
+ buf->access_handler.id);
+
+ ret = sysfs_create_file(&dev->kobj,
+ &(buf->access_minor_attr.dev_attr.attr));
+ if (ret)
+ goto error_deallocate_chrdev;
+ return 0;
+
+error_deallocate_chrdev:
+ iio_deallocate_chrdev(&buf->access_handler);
+error_free_access_minor_name:
+ kfree(buf->access_minor_name);
+error_ret:
+ return ret;
+}
+
+inline void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
+ struct device *dev)
+{
+ sysfs_remove_file(&dev->kobj,
+ &buf->access_minor_attr.dev_attr.attr);
+ iio_deallocate_chrdev(&buf->access_handler);
+ kfree(buf->access_minor_name);
+}
+
+int iio_request_hw_ring_buffer(int bytes_per_datum,
+ int length,
+ struct iio_hw_ring_buffer **ring,
+ int id,
+ struct module *owner,
+ struct device *dev,
+ const struct file_operations *fops,
+ void *private)
+{
+ int ret;
+
+ *ring = kmalloc(sizeof(struct iio_hw_ring_buffer),
+ GFP_KERNEL);
+
+ if (*ring == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ (*ring)->private = private;
+ INIT_IIO_RING_BUFFER(&((*ring)->buf), bytes_per_datum, length);
+ ret = __iio_request_ring_buffer_event_chrdev(&(*ring)->buf,
+ id,
+ owner,
+ dev);
+
+ if (ret)
+ goto error_free_ring_data;
+ ret = __iio_request_ring_buffer_access_chrdev(&(*ring)->buf,
+ id,
+ owner,
+ dev,
+ fops);
+ if (ret)
+ goto error_free_ring_buffer_event_chrdev;
+ (*ring)->buf.ev_int.private = (*ring);
+ (*ring)->buf.access_handler.private = (*ring);
+
+ return 0;
+
+error_free_ring_buffer_event_chrdev:
+ __iio_free_ring_buffer_event_chrdev(&(*ring)->buf, dev);
+error_free_ring_data:
+ /* there isn't any!*/
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_request_hw_ring_buffer);
+
+void iio_free_hw_ring_buffer(struct iio_hw_ring_buffer *ring,
+ struct device *dev)
+{
+ __iio_free_ring_buffer_access_chrdev(&(ring->buf), dev);
+ __iio_free_ring_buffer_event_chrdev(&(ring->buf), dev);
+ kfree(ring);
+}
+EXPORT_SYMBOL_GPL(iio_free_hw_ring_buffer);
+
+
+/* Resize the ring if requested - run whenever ring buffer mode entered */
+int __iio_request_update_sw_ring_buffer(int bytes_per_datum,
+ int length,
+ struct iio_sw_ring_buffer *ring,
+ int id,
+ struct module *owner,
+ struct device *dev)
+{
+/* Reference count the ring - if anyone is using it this will fail!*/
+ int ret = 0;
+/* Need to sanity check if this is necessary? */
+ spin_lock(ring->use_lock);
+
+ if (ring->use_count || !ring->update_needed) {
+ ret = -EAGAIN;
+ goto error_ret;
+ }
+ kfree(ring->data);
+ /* keeps clear of chr devs etc - so fine to use here - I THINK!*/
+ INIT_IIO_SW_RING_BUFFER(ring, bytes_per_datum, length);
+ if (ring->data == NULL)
+ ret = -ENOMEM;
+
+error_ret:
+ spin_unlock(ring->use_lock);
+ return ret;
+}
+
+int iio_request_update_sw_ring_buffer(struct iio_dev *dev_info, int id)
+{
+ return __iio_request_update_sw_ring_buffer(dev_info
+ ->ring_bytes_per_datum,
+ dev_info->ring_length,
+ dev_info->ring,
+ id,
+ dev_info->driver_module,
+ dev_info->dev);
+}
+EXPORT_SYMBOL_GPL(iio_request_update_sw_ring_buffer);
+
+/* Should only occur on init so no locking needed */
+int iio_request_sw_ring_buffer(int bytes_per_datum,
+ int length,
+ struct iio_sw_ring_buffer **ring,
+ int id,
+ struct module *owner,
+ struct device *dev)
+{
+ int ret;
+
+ /* Actually do the ring buffer initialization */
+ *ring = kzalloc(sizeof(struct iio_sw_ring_buffer),
+ GFP_KERNEL);
+ if (*ring == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* Moved to an allocation on demand model.*/
+ iio_mark_sw_ring_buffer_need_update(*ring);
+ ret = __iio_request_ring_buffer_event_chrdev(&(*ring)->buf,
+ id,
+ owner,
+ dev);
+ if (ret)
+ goto error_free_ring_data;
+
+ ret = __iio_request_ring_buffer_access_chrdev(&(*ring)->buf,
+ id,
+ owner,
+ dev,
+ &iio_ring_fileops);
+ if (ret)
+ goto error_free_ring_buffer_event_chrdev;
+
+ /* Setup the private pointer so the fileoperations will work */
+ (*ring)->buf.ev_int.private = (*ring);
+ (*ring)->buf.access_handler.private = (*ring);
+
+ return 0;
+
+error_free_ring_buffer_event_chrdev:
+ __iio_free_ring_buffer_event_chrdev(&(*ring)->buf, dev);
+error_free_ring_data:
+ FREE_IIO_SW_RING_BUFFER(*ring);
+ kfree(*ring);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_request_sw_ring_buffer);
+
+void iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
+ struct device *dev)
+{
+ __iio_free_ring_buffer_access_chrdev(&(ring->buf), dev);
+ __iio_free_ring_buffer_event_chrdev(&(ring->buf), dev);
+ FREE_IIO_SW_RING_BUFFER(ring);
+ kfree(ring);
+}
+EXPORT_SYMBOL_GPL(iio_free_sw_ring_buffer);
+
+static ssize_t iio_read_ring_length(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+ len = sprintf(buf, "%d\n", dev_info->ring_length);
+
+ return len;
+}
+
+static ssize_t iio_write_ring_length(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ long val;
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ /* Ring length stored here and in ring? */
+ if (val != dev_info->ring_length) {
+ dev_info->ring_length = val;
+ iio_mark_sw_ring_buffer_need_update(dev_info->ring);
+ }
+
+ return len;
+error_ret:
+ return ret;
+}
+
+static ssize_t iio_read_ring_bps(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+ len = sprintf(buf, "%d\n", dev_info->ring_bytes_per_datum);
+
+ return len;
+}
+
+
+DEVICE_ATTR(length, S_IRUGO | S_IWUSR,
+ iio_read_ring_length,
+ iio_write_ring_length);
+/* The software ring buffers aren't currently capable of changing the
+ * storage accuracy so this is read only.
+ */
+DEVICE_ATTR(bps, S_IRUGO,
+ iio_read_ring_bps,
+ NULL);
+
+ssize_t iio_store_ring_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ bool requested_state, current_state;
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+ mutex_lock(&dev_info->mlock);
+ requested_state = (buf[0] == '0') ? 0 : 1;
+ current_state = (dev_info->currentmode
+ & (INDIO_RING_DATA_RDY | INDIO_RING_POLLED))
+ ? 1: 0;
+ if (current_state == requested_state)
+ goto done;
+ if (requested_state) {
+ if (dev_info->ring_preenable) {
+ ret = dev_info->ring_preenable(dev_info);
+ if (ret)
+ goto error_ret;
+ }
+ ret = iio_request_update_sw_ring_buffer(dev_info, 0);
+ if (ret)
+ goto error_ret;
+ iio_mark_sw_ring_buffer_in_use(dev_info->ring);
+ if (dev_info->modes & INDIO_RING_DATA_RDY)
+ dev_info->currentmode = INDIO_RING_DATA_RDY;
+ else if (dev_info->modes & INDIO_RING_POLLED)
+ dev_info->currentmode = INDIO_RING_POLLED;
+ else { /* should never be reached */
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ if (dev_info->ring_postenable) {
+ ret = dev_info->ring_postenable(dev_info);
+ if (ret)
+ goto error_ret;
+ }
+ } else {
+ if (dev_info->ring_predisable) {
+ ret = dev_info->ring_predisable(dev_info);
+ if (ret)
+ goto error_ret;
+ }
+ iio_unmark_sw_ring_buffer_in_use(dev_info->ring);
+ dev_info->currentmode = INDIO_DIRECT_MODE;
+ if (dev_info->ring_postdisable) {
+ ret = dev_info->ring_postdisable(dev_info);
+ if (ret)
+ goto error_ret;
+ }
+ }
+done:
+ mutex_unlock(&dev_info->mlock);
+ return len;
+error_ret:
+ mutex_unlock(&dev_info->mlock);
+ return ret;
+}
+
+static ssize_t iio_show_ring_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+
+ int len;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ if (indio_dev->currentmode & (INDIO_RING_DATA_RDY | INDIO_RING_POLLED))
+ len = sprintf(buf, "1\n");
+ else
+ len = sprintf(buf, "0\n");
+
+ return len;
+}
+
+DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR,
+ iio_show_ring_enable,
+ iio_store_ring_enable);
+
+static struct attribute *iio_ring_attributes[] = {
+ &dev_attr_length.attr,
+ &dev_attr_bps.attr,
+ &dev_attr_sw_ring_enable.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_ring_attribute_group = {
+ .name = "ring_buffer",
+ .attrs = iio_ring_attributes,
+};
+
+int iio_device_register_sw_ring(struct iio_dev *dev_info, int id)
+{
+ int ret;
+
+ ret = iio_request_sw_ring_buffer(dev_info->ring_bytes_per_datum,
+ dev_info->ring_length,
+ &dev_info->ring,
+ id,
+ dev_info->driver_module,
+ dev_info->dev);
+ if (ret < 0)
+ goto error_ret;
+
+ ret = sysfs_create_group(&dev_info->dev->kobj,
+ &iio_ring_attribute_group);
+ if (ret)
+ goto error_free_ring;
+
+ return 0;
+
+error_free_ring:
+ iio_free_sw_ring_buffer(dev_info->ring, dev_info->dev);
+error_ret:
+ return ret;
+}
+
+void iio_device_unregister_sw_ring(struct iio_dev *dev_info)
+{
+ sysfs_remove_group(&dev_info->dev->kobj,
+ &iio_ring_attribute_group);
+ /* deallocate ring buffer related stuff */
+ if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY))
+ iio_free_sw_ring_buffer(dev_info->ring, dev_info->dev);
+
+}
From: Jonathan Cameron <[email protected]>
Add support for a number of Maxim ADCs.
(max1361, max1362, max1363, max1364, max1136, max1136, max1137, max1138,
max1139, max1236, max1237, max11238, max1239)
Supplies direct access interfaces and a software ring buffer using a periodic
timer to provide the triggering interrupt.
Signed-off-by: Jonathan Cameron <[email protected]>
---
Patch depends on iio_core patch
drivers/industrialio/Kconfig | 2
drivers/industrialio/Makefile | 2
drivers/industrialio/adc/Kconfig | 12
drivers/industrialio/adc/Makefile | 4
drivers/industrialio/adc/max1363.c | 1008 +++++++++++++++++++++++++++++++++++++
5 files changed, 1028 insertions(+)
--- a/drivers/industrialio/Kconfig 2008-07-23 16:05:18.000000000 +0100
+++ b/drivers/industrialio/Kconfig 2008-07-23 15:44:45.000000000 +0100
@@ -16,4 +16,6 @@ config INDUSTRIALIO_PTIMER_BOARDINFO
boolean
default y
+source drivers/industrialio/adc/Kconfig
+
endif
--- a/drivers/industrialio/Makefile 2008-07-23 16:05:18.000000000 +0100
+++ b/drivers/industrialio/Makefile 2008-07-23 16:24:31.000000000 +0100
@@ -5,3 +5,5 @@ industrialio-objs := industrialio-core.o
obj-$(CONFIG_INDUSTRIALIO) += industrialio.o
obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
+
+obj-y += adc/
--- a/drivers/industrialio/adc/Kconfig 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/adc/Kconfig 2008-07-23 16:16:13.000000000 +0100
@@ -0,0 +1,12 @@
+#
+# ADC drivers
+#
+
+config MAX1363
+ tristate "MAXIM max1363 ADC driver"
+ 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 and sw ring buffer using
+ a periodic timer if available.
--- a/drivers/industrialio/adc/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/adc/Makefile 2008-07-23 16:16:44.000000000 +0100
@@ -0,0 +1,4 @@
+
+# Makefile for industrial I/O ADC drivers
+#
+obj-$(CONFIG_MAX1363) += max1363.o
--- a/drivers/industrialio/adc/max1363.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/adc/max1363.c 2008-07-23 16:47:19.000000000 +0100
@@ -0,0 +1,1008 @@
+ /*
+ * linux/drivers/industrialio/adc/max1363.c
+ * Copyright (C) 2008 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * 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.
+ *
+ * max1363.c
+ */
+
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+#include <linux/industrialio_sysfs.h>
+
+
+#define MAX1363_SETUP_BYTE(a) ((a) | 0x80)
+
+/* see data sheets */
+/* max1363 and max1236, max1237, max1238, max1239 */
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00
+#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40
+#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60
+#define MAX1363_SETUP_POWER_UP_INT_REF 0x10
+#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00
+
+
+#define MAX1363_SETUP_EXT_CLOCK 0x08
+#define MAX1363_SETUP_INT_CLOCK 0x00
+#define MAX1363_SETUP_UNIPOLAR 0x00
+#define MAX1363_SETUP_BIPOLAR 0x04
+#define MAX1363_SETUP_RESET 0x00
+#define MAX1363_SETUP_NORESET 0x02
+/* max1363 only - though don't care on others */
+#define MAX1363_SETUP_MONITOR_SETUP 0x01
+
+/* Specific to the max1363 */
+#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4))
+#define MAX1363_MON_CONV_RATE_133ksps 0
+#define MAX1363_MON_CONV_RATE_66_5ksps 0x02
+#define MAX1363_MON_CONV_RATE_33_3ksps 0x04
+#define MAX1363_MON_CONV_RATE_16_6ksps 0x06
+#define MAX1363_MON_CONV_RATE_8_3ksps 0x08
+#define MAX1363_MON_CONV_RATE_4_2ksps 0x0A
+#define MAX1363_MON_CONV_RATE_2_0ksps 0x0C
+#define MAX1363_MON_CONV_RATE_1_0ksps 0x0E
+#define MAX1363_MON_INT_ENABLE 0x01
+
+/* defined for readability reasons */
+/* All chips */
+#define MAX1363_CONFIG_BYTE(a) ((a))
+
+#define MAX1363_CONFIG_SE 0x01
+#define MAX1363_CONFIG_DE 0x00
+
+#define MAX1363_CONFIG_SCAN_TO_CS 0x00
+#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20
+#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40
+#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60
+/* max123{6-9} only */
+#define MAX1236_SCAN_MID_TO_CHANNEL 0x40
+
+/* max1363 only - merely part of channel selects or don't care for others*/
+#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18
+
+#define MAX1363_CHANNEL_SEL(a) ((a) << 1)
+
+/* max1363 strictly 0x06 - but doesn't matter */
+#define MAX1363_CHANNEL_SEL_MASK 0x1E
+#define MAX1363_SCAN_MASK 0x60
+#define MAX1363_SE_DE_MASK 0x01
+
+struct max1363_mode {
+ const char *name;
+ int8_t conf;
+ /* The number of values to be read in a single scan */
+ int numvals;
+};
+
+#define MAX1363_MODE_SINGLE(_num) { \
+ .name = #_num, \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_SE, \
+ .numvals = 1, \
+ }
+
+#define MAX1363_MODE_SINGLE_TIMES_8(_num) { \
+ .name = #_num"x8", \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_SINGLE_8 \
+ | MAX1363_CONFIG_SE, \
+ .numvals = 8, \
+ }
+
+#define MAX1363_MODE_SCAN_TO_CHANNEL(_num) { \
+ .name = "0..."#_num, \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_SE, \
+ .numvals = _num + 1, \
+ }
+
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num) { \
+ .name = #_mid"..."#_num, \
+ .conf =MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .numvals = _num - _mid + 1 \
+}
+
+#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm) { \
+ .name = #_nump"-"#_numm, \
+ .conf = MAX1363_CHANNEL_SEL(_nump) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_DE, \
+ .numvals = 1, \
+ }
+
+#define MAX1363_MODE_DIFF_SINGLE_TIMES_8(_nump, _numm) { \
+ .name = #_nump"-"#_numm, \
+ .conf = MAX1363_CHANNEL_SEL(_nump) \
+ | MAX1363_CONFIG_SCAN_SINGLE_8 \
+ | MAX1363_CONFIG_DE, \
+ .numvals = 1, \
+ }
+
+/* Can't think how to automate naming so specify for now */
+#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(_name, _num, _numvals) { \
+ .name = #_name, \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_DE, \
+ .numvals = _numvals, \
+ }
+
+/* note only available for max1363 hence naming */
+#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(_name, _num, _numvals) { \
+ .name = #_name, \
+ .conf =MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .numvals = _numvals, \
+}
+
+/* TODO: make this configurable */
+#define MAX1363_MODE_MONITOR { \
+ .name = "monitor", \
+ .conf = MAX1363_CHANNEL_SEL(3) \
+ | MAX1363_CONFIG_SCAN_MONITOR_MODE \
+ | MAX1363_CONFIG_SE, \
+ .numvals = 10, \
+ }
+
+/* This may seem an overly long winded way to do this, but at least it makes
+ * clear what all the various options actually do. Alternative suggestions
+ * that don't require user to have intimate knowledge of the chip welcomed.
+ */
+
+/* This must be maintained along side the max1363_mode_table below */
+enum max1363_modes {
+ /* Single read of a single channel */
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ /* Eight reads of a single channel */
+ se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11,
+ /* Scan to channel */
+ s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+ s0to7, s0to8, s0to9, s0to10, s0to11,
+ /* Differential single read */
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ /* Differential single read 8 times */
+ de0m1, de2m3, de4m5, de6m7, de8m9, de10m11,
+ de1m0, de3m2, de5m4, de7m6, de9m8, de11m10,
+ /* Differential scan to channel */
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+ /* Scan mid to channel max123{6-9} only */
+ s2to3, s6to7, s6to8, s6to9, s6to10, s6to11,
+ /* Differential scan mid to channel */
+ s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10,
+} max1363_mode_t;
+
+
+static const struct max1363_mode max1363_mode_table[] = {
+ MAX1363_MODE_SINGLE(0),
+ MAX1363_MODE_SINGLE(1),
+ MAX1363_MODE_SINGLE(2),
+ MAX1363_MODE_SINGLE(3),
+ MAX1363_MODE_SINGLE(4),
+ MAX1363_MODE_SINGLE(5),
+ MAX1363_MODE_SINGLE(6),
+ MAX1363_MODE_SINGLE(7),
+ MAX1363_MODE_SINGLE(8),
+ MAX1363_MODE_SINGLE(9),
+ MAX1363_MODE_SINGLE(10),
+ MAX1363_MODE_SINGLE(11),
+
+ MAX1363_MODE_SINGLE_TIMES_8(0),
+ MAX1363_MODE_SINGLE_TIMES_8(1),
+ MAX1363_MODE_SINGLE_TIMES_8(2),
+ MAX1363_MODE_SINGLE_TIMES_8(3),
+ MAX1363_MODE_SINGLE_TIMES_8(4),
+ MAX1363_MODE_SINGLE_TIMES_8(5),
+ MAX1363_MODE_SINGLE_TIMES_8(6),
+ MAX1363_MODE_SINGLE_TIMES_8(7),
+ MAX1363_MODE_SINGLE_TIMES_8(8),
+ MAX1363_MODE_SINGLE_TIMES_8(9),
+ MAX1363_MODE_SINGLE_TIMES_8(10),
+ MAX1363_MODE_SINGLE_TIMES_8(11),
+
+ MAX1363_MODE_SCAN_TO_CHANNEL(1),
+ MAX1363_MODE_SCAN_TO_CHANNEL(2),
+ MAX1363_MODE_SCAN_TO_CHANNEL(3),
+ MAX1363_MODE_SCAN_TO_CHANNEL(4),
+ MAX1363_MODE_SCAN_TO_CHANNEL(5),
+ MAX1363_MODE_SCAN_TO_CHANNEL(6),
+ MAX1363_MODE_SCAN_TO_CHANNEL(7),
+ MAX1363_MODE_SCAN_TO_CHANNEL(8),
+ MAX1363_MODE_SCAN_TO_CHANNEL(9),
+ MAX1363_MODE_SCAN_TO_CHANNEL(10),
+ MAX1363_MODE_SCAN_TO_CHANNEL(11),
+
+ MAX1363_MODE_DIFF_SINGLE(0, 1),
+ MAX1363_MODE_DIFF_SINGLE(2, 3),
+ MAX1363_MODE_DIFF_SINGLE(4, 5),
+ MAX1363_MODE_DIFF_SINGLE(6, 7),
+ MAX1363_MODE_DIFF_SINGLE(8, 9),
+ MAX1363_MODE_DIFF_SINGLE(10, 11),
+ MAX1363_MODE_DIFF_SINGLE(1, 0),
+ MAX1363_MODE_DIFF_SINGLE(3, 2),
+ MAX1363_MODE_DIFF_SINGLE(5, 4),
+ MAX1363_MODE_DIFF_SINGLE(7, 6),
+ MAX1363_MODE_DIFF_SINGLE(9, 8),
+ MAX1363_MODE_DIFF_SINGLE(11, 10),
+
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(0, 1),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(2, 3),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(4, 5),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(6, 7),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(8, 9),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(10, 11),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(1, 0),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(3, 2),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(5, 4),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(7, 6),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(9, 8),
+ MAX1363_MODE_DIFF_SINGLE_TIMES_8(11, 10),
+
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...2-3, 2, 2),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...4-5, 4, 3),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...6-7, 6, 4),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...8-9, 8, 5),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...10-11, 10, 6),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...3-2, 3, 2),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...5-4, 5, 3),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...7-6, 7, 4),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...9-8, 9, 5),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...11-10, 11, 6),
+
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11),
+
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...8-9, 8, 2),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...10-11, 10, 3),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...9-8, 9, 2),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...11-10, 11, 3),
+};
+
+/* Applies to max1363 */
+static const enum max1363_modes max1363_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ se0, se1, se2, se3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ de0m1, de2m3, de1m0, de3m2,
+ d0m1to2m3, d1m0to3m2,
+};
+
+/* Appies to max1236, max1237 */
+static const enum max1363_modes max1236_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ se0, se1, se2, se3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ de0m1, de2m3, de1m0, de3m2,
+ d0m1to2m3, d1m0to3m2,
+ s2to3,
+};
+
+/* Applies to max1238, max1239 */
+static const enum max1363_modes max1238_mode_list[] = {
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11,
+ s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+ s0to7, s0to8, s0to9, s0to10, s0to11,
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ de0m1, de2m3, de4m5, de6m7, de8m9, de10m11,
+ de1m0, de3m2, de5m4, de7m6, de9m8, de11m10,
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+ s2to3, s6to7, s6to8, s6to9, s6to10, s6to11,
+ s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10,
+};
+struct max1363_chip_info {
+ const char *name;
+ u8 num_inputs;
+ u16 int_vref_mv;
+ bool monitor_mode;
+ const enum max1363_modes *mode_list;
+ int num_modes;
+ enum max1363_modes default_mode;
+};
+
+
+enum { max1361,
+ max1362,
+ max1363,
+ max1364,
+
+ max1136,
+ max1137,
+ max1138,
+ max1139,
+ max1236,
+ max1237,
+ max1238,
+ max1239,
+};
+
+/* max1363 and max1368 tested - rest from data sheet */
+static const struct max1363_chip_info max1363_chip_info_tbl[] = {
+ {
+ .name = "max1361",
+ .num_inputs = 4,
+ .monitor_mode = 1,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1362",
+ .num_inputs = 4,
+ .monitor_mode = 1,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1363",
+ .num_inputs = 4,
+ .monitor_mode = 1,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1364",
+ .num_inputs = 4,
+ .monitor_mode = 1,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1136",
+ .num_inputs = 4,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1137",
+ .num_inputs = 4,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1138",
+ .num_inputs = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ }, {
+ .name = "max1139",
+ .num_inputs = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ }, {
+ .name = "max1236",
+ .num_inputs = 4,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1237",
+ .num_inputs = 4,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ }, {
+ .name = "max1238",
+ .num_inputs = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ }, {
+ .name = "max1239",
+ .num_inputs = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ },
+};
+
+struct max1363_data {
+ struct iio_dev *indio_dev;
+ struct iio_work_cont work_cont_thresh;
+ struct i2c_client *client;
+ /* Device does not support reading config bytes so keep copies here */
+ char setupbyte;
+ char configbyte;
+ const struct max1363_chip_info *chip_info;
+ const struct max1363_mode *current_mode;
+/* periodic interrupt related */
+ struct work_struct poll_work;
+ atomic_t protect_ring;
+};
+
+static int max1363_write_basic_config(struct i2c_client *client,
+ unsigned char d1,
+ unsigned char d2)
+{
+ int ret;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ };
+ unsigned char *tx_buf;
+
+ tx_buf = kmalloc(2, GFP_KERNEL);
+ tx_buf[0] = d1;
+ tx_buf[1] = d2;
+ msg.buf = tx_buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ ret = 0;
+
+ kfree(tx_buf);
+ return ret;
+}
+
+/* Monitor mode config */
+static int max1363_write_monitor_config(struct i2c_client *client,
+ unsigned char d1,
+ unsigned char d2)
+{
+ int ret;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 15,
+ };
+ unsigned char *tx_buf;
+ tx_buf = kmalloc(15, GFP_KERNEL);
+ msg.buf = tx_buf;
+ tx_buf[0] = d2;
+ tx_buf[1] = d1 | MAX1363_SETUP_MONITOR_SETUP;
+ /* Always reset alarms for now */
+ tx_buf[2] = MAX1363_MON_RESET_CHAN(0)
+ | MAX1363_MON_RESET_CHAN(1)
+ | MAX1363_MON_RESET_CHAN(2)
+ | MAX1363_MON_RESET_CHAN(3)
+ | MAX1363_MON_CONV_RATE_1_0ksps
+ | MAX1363_MON_INT_ENABLE;
+ /*theshold are interleved gah! */
+ /* lower thesh very little, upper, quite a bit */
+ tx_buf[3] = 0x00;
+ tx_buf[4] = 0x0E;
+ tx_buf[5] = 0x00;
+ /*Channel 1 disable */
+ tx_buf[6] = 0x00;
+ tx_buf[7] = 0x0F;
+ tx_buf[8] = 0x0F;
+ /*Channel 2 disable */
+ tx_buf[9] = 0x00;
+ tx_buf[10] = 0x0F;
+ tx_buf[11] = 0xFF;
+ /* Channel 3 disbale */
+ tx_buf[12] = 0x00;
+ tx_buf[13] = 0x0F;
+ tx_buf[14] = 0xFF;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ ret = 0;
+
+ kfree(tx_buf);
+ return ret;
+
+}
+
+static int max1363_set_scan_mode(struct device *dev,
+ struct max1363_data *data)
+{
+ int ret;
+
+ data->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+ | MAX1363_SCAN_MASK
+ | MAX1363_SE_DE_MASK);
+ data->configbyte |= data->current_mode->conf;
+
+ ret = max1363_write_basic_config(data->client,
+ data->setupbyte,
+ data->configbyte);
+/* TO FINISH IMPLEMENTING
+ ret = max1363_write_monitor_config(data->client,
+ data->setupbyte,
+ data->configbyte);*/
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void max1363_thresh_handler_bh_no_check(struct work_struct *work_s)
+{
+}
+
+static void max1363_thresh_handler_bh(struct work_struct *work_s)
+{
+}
+
+static int max1363_power_down(struct max1363_data *data)
+{
+ return 0;
+}
+
+static int max1363_initial_setup(struct max1363_data *data)
+{
+ int ret;
+
+ data->setupbyte
+ = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD
+ | MAX1363_SETUP_POWER_UP_INT_REF
+ | MAX1363_SETUP_INT_CLOCK
+ | MAX1363_SETUP_UNIPOLAR
+ | MAX1363_SETUP_NORESET;
+ data->setupbyte = MAX1363_SETUP_BYTE(data->setupbyte);
+ data->current_mode = &max1363_mode_table[data->chip_info->default_mode];
+ data->configbyte = MAX1363_CONFIG_BYTE(data->configbyte);
+ /* Set scan mode writes the config anyway so don't explicitly do it */
+ ret = max1363_set_scan_mode(data->indio_dev->dev, data);
+
+ return ret;
+}
+
+static ssize_t max1363_show_av_scan_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct max1363_data *data = dev_info->dev_data;
+ int i, len = 0;
+
+ for (i = 0; i < data->chip_info->num_modes; i++)
+ len += sprintf(buf + len, "%s ",
+ max1363_mode_table[data->chip_info
+ ->mode_list[i]].name);
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static ssize_t max1363_scan_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct max1363_data *info = dev_info->dev_data;
+ int i, ret, len = 0;
+ char *ring_data;
+
+ ring_data = kmalloc(info->current_mode->numvals*2, GFP_KERNEL);
+ if (ring_data == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ ret = iio_read_last_from_sw_ring(dev_info->ring, ring_data);
+ if (ret)
+ goto error_free_ring_data;
+ len += sprintf(buf+len, "ring ");
+ for (i = 0; i < info->current_mode->numvals; i++)
+ len += sprintf(buf + len, "%d ",
+ ((int)(ring_data[i*2 + 0]&0x0F) << 8)
+ + ((int)(ring_data[i*2 + 1])));
+ len += sprintf(buf + len, "\n");
+ kfree(ring_data);
+
+ return len;
+
+error_free_ring_data:
+ kfree(ring_data);
+error_ret:
+ return ret;
+}
+
+static ssize_t max1363_scan_direct(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct max1363_data *data = dev_info->dev_data;
+ int len = 0, ret, i;
+ struct i2c_client *client = to_i2c_client(dev);
+
+
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = data->current_mode->numvals*2,
+ };
+ char *rxbuf;
+
+ if (data->current_mode->numvals == 0)
+ return 0;
+ rxbuf = kmalloc(data->current_mode->numvals*2, GFP_KERNEL);
+ if (rxbuf == NULL)
+ return -ENOMEM;
+ msg.buf = rxbuf;
+ /* Interpretation depends on whether these are signed or not!*/
+ /* Assume not for now */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ ret = 0;
+ for (i = 0; i < data->current_mode->numvals; i++)
+ len += sprintf(buf+len, "%d ",
+ ((int)(rxbuf[i*2+0]&0x0F) << 8)
+ + ((int)(rxbuf[i*2+1])));
+ kfree(rxbuf);
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static ssize_t max1363_scan(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&dev_info->mlock);
+ if (dev_info->currentmode == INDIO_RING_POLLED)
+ ret = max1363_scan_from_ring(dev, attr, buf);
+ else
+ ret = max1363_scan_direct(dev, attr, buf);
+ mutex_unlock(&dev_info->mlock);
+
+ return ret;
+}
+
+/* Cannot query the device, so use local copy of state */
+static ssize_t max1363_show_scan_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0;
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct max1363_data *data = dev_info->dev_data;
+
+ len += sprintf(buf, "%s\n", data->current_mode->name);
+
+ return len;
+}
+
+static ssize_t max1363_store_scan_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct max1363_data *data = dev_info->dev_data;
+ int i, ret;
+
+ mutex_lock(&dev_info->mlock);
+ data->current_mode = NULL;
+ for (i = 0; i < data->chip_info->num_modes; i++) {
+ if (strncmp(max1363_mode_table[data->chip_info->mode_list[i]]
+ .name, buf, strlen(buf)-1) == 0) {
+ data->current_mode
+ = &max1363_mode_table[data
+ ->chip_info
+ ->mode_list[i]];
+ break;
+ }
+ }
+ if (!data->current_mode) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ ret = max1363_set_scan_mode(dev, data);
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&dev_info->mlock);
+
+ return len;
+
+error_ret:
+ mutex_unlock(&dev_info->mlock);
+
+ return ret;
+}
+
+IIO_DEV_ATTR_AVAIL_SCAN_MODES(max1363_show_av_scan_modes);
+IIO_DEV_ATTR_SCAN_MODE(S_IRUGO | S_IWUSR,
+ max1363_show_scan_mode,
+ max1363_store_scan_mode);
+
+IIO_DEV_ATTR_SCAN(max1363_scan);
+
+
+static int max1363_ring_preenable(struct iio_dev *indio_dev)
+{
+ struct max1363_data *data = indio_dev->dev_data;
+ indio_dev->ring_bytes_per_datum
+ = data->current_mode->numvals*2 + sizeof(s64);
+ return 0;
+}
+
+static int max1363_ring_postenable(struct iio_dev *indio_dev)
+{
+ return iio_ptimer_irq_set_state(indio_dev, 1);
+}
+
+static int max1363_ring_predisable(struct iio_dev *indio_dev)
+{
+ return iio_ptimer_irq_set_state(indio_dev, 0);
+}
+
+static struct attribute *max1363_attributes[] = {
+ &iio_dev_attr_available_scan_modes.dev_attr.attr,
+ &iio_dev_attr_scan_mode.dev_attr.attr,
+ &iio_dev_attr_scan.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max1363_attribute_group = {
+ .attrs = max1363_attributes,
+};
+
+static struct attribute *max1363_event_attributes[] = {
+ NULL,
+};
+
+static struct attribute_group max1363_event_attribute_group = {
+ .attrs = max1363_event_attributes,
+};
+
+void max1363_poll_func_th(void *private_data)
+{
+ struct iio_dev *indio_dev = private_data;
+ struct max1363_data *data = indio_dev->dev_data;
+ schedule_work(&data->poll_work);
+ return;
+}
+
+/* Could we have two of these running at once -
+ * yes unfortunately and we can't let that happen
+ * as the ring buffer does not support concurrent writes
+ * hence check if one is running already and stop if it is.*/
+
+static void max1363_poll_bh_to_ring(struct work_struct *work_s)
+{
+ /* This shares a lot with a normal scan read - combine them? */
+ struct max1363_data *data = container_of(work_s, struct max1363_data,
+ poll_work);
+ struct iio_dev *indio_dev = data->indio_dev;
+ struct iio_sw_ring_buffer *ring = indio_dev->ring;
+ s64 time_ns;
+
+ struct i2c_msg msg = {
+ .addr = data->client->addr,
+ .flags = I2C_M_RD,
+ .len = data->current_mode->numvals*2,
+ };
+ __u8 *rxbuf;
+ int ret;
+
+ if (atomic_inc_return(&data->protect_ring) > 1)
+ return;
+
+ /* Monitor mode prevents reading */
+ if (data->current_mode->numvals == 0)
+ return;
+ /* FIXME - missmatch in size somewhere */
+ rxbuf = kmalloc(data->current_mode->numvals*2+8, GFP_KERNEL);
+ if (rxbuf == NULL)
+ return;
+ msg.buf = rxbuf;
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+ if (ret == 1)
+ ret = 0;
+ else if (ret)
+ goto done;
+ time_ns = iio_get_time_ns();
+ memcpy(rxbuf + data->current_mode->numvals*2,
+ &time_ns, sizeof(time_ns));
+ iio_store_to_sw_ring(ring, rxbuf, time_ns);
+ done:
+ kfree(rxbuf);
+ atomic_dec(&data->protect_ring);
+}
+
+
+static int __devinit max1363_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret, i;
+ struct max1363_data *data;
+
+ data = kzalloc(sizeof(struct max1363_data), GFP_KERNEL);
+
+ if (data == NULL) {
+ dev_err(&client->dev, "Memory allocation error \n");
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ atomic_set(&data->protect_ring, 0);
+
+ data->chip_info = NULL;
+ for (i = 0; i < ARRAY_SIZE(max1363_chip_info_tbl); i++)
+ if (!strcmp(max1363_chip_info_tbl[i].name, id->name)) {
+ data->chip_info = &max1363_chip_info_tbl[i];
+ break;
+ };
+
+ if (!data->chip_info) {
+ dev_err(&client->dev, "%s is not supported\n", id->name);
+ ret = -ENODEV;
+ goto error_free_data;
+ }
+
+ data->client = client;
+ data->indio_dev = kzalloc(sizeof(struct iio_dev), GFP_KERNEL);
+ if (data->indio_dev == NULL) {
+ dev_err(&client->dev, "Memory allocation error\n");
+ ret = -ENOMEM;
+ goto error_free_data;
+ }
+
+ data->indio_dev->dev = &client->dev;
+ data->indio_dev->num_interrupt_lines = 1;
+ data->indio_dev->event_attrs = &max1363_event_attribute_group;
+ data->indio_dev->attrs = &max1363_attribute_group;
+ data->indio_dev->dev_data = (void *)(data);
+
+ data->indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data->indio_dev->ring_postenable = &max1363_ring_postenable;
+ data->indio_dev->ring_preenable = &max1363_ring_preenable;
+ data->indio_dev->ring_predisable = &max1363_ring_predisable;
+ data->indio_dev->ring_poll_func = &max1363_poll_func_th;
+ data->indio_dev->ring_length = 500;
+ INIT_WORK(&data->poll_work, &max1363_poll_bh_to_ring);
+/* As this is set a timer will be requested by industrialio-core */
+ data->indio_dev->modes |= INDIO_RING_POLLED;
+
+ /* Poll filled ring buffer - using periodic rtc interrupt for now.
+ * See discussion with David Brownell on rtc list for other options.
+ */
+
+ ret = iio_device_register(data->indio_dev);
+ if (ret)
+ goto error_free_device;
+ /* set a sensible default frequency for the timer */
+ if (data->indio_dev->ptimer)
+ iio_ptimer_set_freq(data->indio_dev->ptimer, 128);
+ if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0) {
+
+ INIT_IIO_WORK_CONT(&data->work_cont_thresh,
+ max1363_thresh_handler_bh_no_check,
+ max1363_thresh_handler_bh,
+ 0,
+ 0,
+ data);
+ ret = iio_register_interrupt_line(client->irq,
+ data->indio_dev,
+ 0,
+ IRQF_TRIGGER_RISING,
+ "max1363");
+ if (ret)
+ goto error_unregister_dev;
+ }
+
+
+ ret = max1363_initial_setup(data);
+ if (ret)
+ goto error_unregister_line;
+
+ return 0;
+
+error_unregister_line:
+ if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+ iio_unregister_interrupt_line(data->indio_dev, 0);
+
+error_unregister_dev:
+ iio_device_unregister(data->indio_dev);
+error_free_device:
+ kfree(data->indio_dev);
+error_free_data:
+ kfree(data);
+error_ret:
+ return ret;
+}
+
+static int max1363_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct max1363_data *data = indio_dev->dev_data;
+ int ret;
+ /* Stop the device */
+ ret = max1363_power_down(data);
+ if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+ iio_unregister_interrupt_line(data->indio_dev, 0);
+
+ iio_device_unregister(indio_dev);
+ kfree(indio_dev);
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id max1363_id[] = {
+ { "max1361", max1361 },
+ { "max1362", max1362 },
+ { "max1363", max1363 },
+ { "max1364", max1364 },
+ { "max1136", max1136 },
+ { "max1137", max1137 },
+ { "max1138", max1138 },
+ { "max1139", max1139 },
+ { "max1236", max1236 },
+ { "max1237", max1237 },
+ { "max1238", max1238 },
+ { "max1239", max1239 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max1363_id);
+
+static struct i2c_driver max1363_driver = {
+ .driver = {
+ .name = "max1363",
+ },
+ .probe = max1363_probe,
+ .remove = max1363_remove,
+ .id_table = max1363_id,
+};
+
+
+static __init int max1363_init(void)
+{
+ return i2c_add_driver(&max1363_driver);
+}
+
+static __exit void max1363_exit(void)
+{
+ i2c_del_driver(&max1363_driver);
+}
+
+MODULE_AUTHOR("Jonathan Cameron <[email protected]");
+MODULE_DESCRIPTION("Maxim 1363 ADC");
+MODULE_LICENSE("GPL v2");
+
+module_init(max1363_init);
+module_exit(max1363_exit);
From: Jonathan Cameron <[email protected]>
Add support for ST LIS3L02DQ accelerometer as found on the xbow imote2 sensor
board. Provides direct access via sysfs interfaces and a software ring buffer
using the chips datardy interrupt as a trigger. Motion detection and data ready
events are available on a chrdev as are ring buffer 50% and 100% full events.
Signed-off-by: Jonathan Cameron <[email protected]>
---
Patch depends on iio_core patch
drivers/industrialio/Kconfig | 1
drivers/industrialio/Makefile | 1
drivers/industrialio/accelerometer/Kconfig | 19
drivers/industrialio/accelerometer/Makefile | 5
drivers/industrialio/accelerometer/lis3l02dq.c | 1371 +++++++++++++++++++++++++
drivers/industrialio/accelerometer/lis3l02dq.h | 168 +++
6 files changed, 1565 insertions(+)
--- a/drivers/industrialio/Makefile 2008-07-23 16:49:28.000000000 +0100
+++ b/drivers/industrialio/Makefile 2008-07-23 16:54:42.000000000 +0100
@@ -7,3 +7,4 @@ obj-$(CONFIG_INDUSTRIALIO) += industrial
obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
obj-y += adc/
+obj-y += accelerometer/
--- a/drivers/industrialio/Kconfig 2008-07-23 16:52:14.000000000 +0100
+++ b/drivers/industrialio/Kconfig 2008-07-23 15:44:45.000000000 +0100
@@ -16,6 +16,7 @@ config INDUSTRIALIO_PTIMER_BOARDINFO
boolean
default y
+source drivers/industrialio/accelerometer/Kconfig
source drivers/industrialio/adc/Kconfig
endif
--- a/drivers/industrialio/accelerometer/Kconfig 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/accelerometer/Kconfig 2008-07-23 16:57:39.000000000 +0100
@@ -0,0 +1,19 @@
+#
+# Accelerometer drivers
+#
+
+config LIS3L02DQ
+ tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
+ 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.
+
--- a/drivers/industrialio/accelerometer/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/accelerometer/Makefile 2008-07-14 17:26:34.000000000 +0100
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+obj-$(CONFIG_LIS3L02DQ_SPI) += lis3l02dq.o
--- a/drivers/industrialio/accelerometer/lis3l02dq.h 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/accelerometer/lis3l02dq.h 2008-07-21 17:29:56.000000000 +0100
@@ -0,0 +1,168 @@
+/*
+ * LISL02DQ.h -- support STMicroelectronics LISD02DQ
+ * 3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <[email protected]>
+ *
+ * Loosely based upon tle62x0.c
+ *
+ * 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 SPI_LIS3L02DQ_H_
+#define SPI_LIS3L02DQ_H_
+#define LIS3L02DQ_READ_REG(a) ((a) | 0x80)
+#define LIS3L02DQ_WRITE_REG(a) a
+
+/* Calibration parameters */
+#define LIS3L02DQ_REG_OFFSET_X_ADDRESS 0x16
+#define LIS3L02DQ_REG_OFFSET_Y_ADDRESS 0x17
+#define LIS3L02DQ_REG_OFFSET_Z_ADDRESS 0x18
+
+#define LIS3L02DQ_REG_GAIN_X_ADDRESS 0x19
+#define LIS3L02DQ_REG_GAIN_Y_ADDRESS 0x1A
+#define LIS3L02DQ_REG_GAIN_Z_ADDRESS 0x1B
+
+/* Control Register (1 of 2) */
+#define LIS3L02DQ_REG_CTRL_1_ADDRESS 0x20
+/* Power ctrl - either bit set corresponds to on*/
+#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0
+
+/* Decimation Factor */
+#define LIS3L02DQ_DEC_MASK 0x30
+#define LIS3L02DQ_REG_CTRL_1_DF_128 0x00
+#define LIS3L02DQ_REG_CTRL_1_DF_64 0x10
+#define LIS3L02DQ_REG_CTRL_1_DF_32 0x20
+#define LIS3L02DQ_REG_CTRL_1_DF_8 (0x10 | 0x20)
+
+/* Self Test Enable */
+#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON 0x08
+
+/* Axes enable ctrls */
+#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE 0x04
+#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE 0x02
+#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE 0x01
+
+/* Control Register (2 of 2) */
+#define LIS3L02DQ_REG_CTRL_2_ADDRESS 0x21
+
+/* Block Data Update only after MSB and LSB read */
+#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE 0x40
+
+/* Set to big endian output */
+#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN 0x20
+
+/* Reboot memory content */
+#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY 0x10
+
+/* Interupt Enable - applies data ready to the RDY pad */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT 0x08
+
+/* Enable Data Ready Generation - relationship with previous unclear in docs */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04
+
+/* SPI 3 wire mode */
+#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE 0x02
+
+/* Data alignment, default is 12 bit right justified
+ * - option for 16 bit left justified */
+#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED 0x01
+
+/* Interupt related stuff */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDRESS 0x23
+
+/* Switch from or combination fo conditions to and */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND 0x80
+
+/* Latch interupt request,
+ * if on ack must be given by reading the ack register */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC 0x40
+
+/* Z Interupt on High (above threshold)*/
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH 0x20
+/* Z Interupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW 0x10
+/* Y Interupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH 0x08
+/* Y Interupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW 0x04
+/* X Interupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH 0x02
+/* X Interupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01
+
+/* Register that gives description of what caused interupt
+ * - latched if set in CFG_ADDRES */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDRESS 0x24
+/* top bit ignored */
+/* Interupt Active */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED 0x40
+/* Interupts that have been triggered */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH 0x20
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW 0x10
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH 0x08
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW 0x04
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH 0x02
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW 0x01
+
+#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDRESS 0x25
+
+/* Status register */
+#define LIS3L02DQ_REG_STATUS_ADDRESS 0x27
+/* XYZ axis data overrun - first is all overrun? */
+#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN 0x80
+#define LIS3L02DQ_REG_STATUS_Z_OVERRUN 0x40
+#define LIS3L02DQ_REG_STATUS_Y_OVERRUN 0x20
+#define LIS3L02DQ_REG_STATUS_X_OVERRUN 0x10
+/* XYZ new data available - first is all 3 available? */
+#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
+#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA 0x04
+#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA 0x02
+#define LIS3L02DQ_REG_STATUS_X_NEW_DATA 0x01
+
+/* The accelerometer readings - low and high bytes.
+Form of high byte dependant on justification set in ctrl reg */
+#define LIS3L02DQ_REG_OUT_X_L_ADDRESS 0x28
+#define LIS3L02DQ_REG_OUT_X_H_ADDRESS 0x29
+#define LIS3L02DQ_REG_OUT_Y_L_ADDRESS 0x2A
+#define LIS3L02DQ_REG_OUT_Y_H_ADDRESS 0x2B
+#define LIS3L02DQ_REG_OUT_Z_L_ADDRESS 0x2C
+#define LIS3L02DQ_REG_OUT_Z_H_ADDRESS 0x2D
+
+/* Threshold values for all axes and both above and below thresholds
+ * - i.e. there is only one value */
+#define LIS3L02DQ_REG_THS_L_ADDRESS 0x2E
+#define LIS3L02DQ_REG_THS_H_ADDRESS 0x2F
+
+#define LIS3L02DQ_DEFAULT_CTRL1 (LIS3L02DQ_REG_CTRL_1_PD_ON \
+ | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_DF_128)
+
+#define LIS3L02DQ_DEFAULT_CTRL2 0
+
+#define LIS3L02DQ_DIRECT_ONLY_MODE -1
+#define LIS3L02DQ_DIRECT_MODE 0
+#define LIS3L02DQ_INTERRUPT_MODE 1
+
+#define LIS3L02DQ_BUFFER_LENGTH 100
+
+
+
+struct lis3l02dq_state {
+ struct spi_device *us;
+ struct work_struct work_data_rdy_ring;
+ struct work_struct work_data_rdy_event;
+ struct iio_work_cont work_cont_thresh;
+
+ /* Interrupt caught event - used as part of the datardy to ring bh
+ in ensuring interrupt line does not become locked high */
+ bool inter;
+ s64 last_timestamp;
+ struct iio_dev *indio_dev;
+
+};
+#endif /* SPI_LIS3L02DQ_H_ */
--- a/drivers/industrialio/accelerometer/lis3l02dq.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/accelerometer/lis3l02dq.c 2008-07-23 17:02:12.000000000 +0100
@@ -0,0 +1,1371 @@
+/*
+ * lis3l02dq.c support STMicroelectronics LISD02DQ
+ * 3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <[email protected]>
+ *
+ * Loosely based upon tle62x0.c
+ *
+ * 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.
+ *
+ * FIXME: MORE DOCS
+ *
+ * Settings:
+ * Latch on interrupt generation enabled as it simplifies when to reenable
+ * the interrupt.
+ * 16 bit left justified mode used.
+ *
+ * Not implemented as yet - 'channel' selection for scan. This will probably
+ * look quite similar to the mode selection code in max1363 but will affect only
+ * which channels are read and pushed to the ring.
+ * Complexities arise in preventing reads from ring for elements that are not
+ * there. Could move to an entirely 'scan' based interface, but at the cost
+ * of allowing direct reading (problem or not?).
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+#include <linux/industrialio_sysfs.h>
+
+#include "lis3l02dq.h"
+
+/* Somewhat wasteful under arm type alignments - endianess issues
+ as well?*/
+union lis3l02dq_channel {
+ char data[2];
+ int16_t val;
+};
+
+struct lis3l02dq_datum {
+ union lis3l02dq_channel el[3];
+ s64 time;
+};
+
+/* Read all inputs in one spi message */
+static const char read_all_tx_array[] =
+{
+ 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDRESS),
+ 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDRESS),
+ 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDRESS),
+ 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDRESS),
+ 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDRESS),
+ 0, LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDRESS),
+};
+
+static int lis3l02dq_read_all(struct lis3l02dq_state *st,
+ unsigned char *rx_array)
+{
+ /* Sadly the device appears to require deselection between
+ * reading the different registers - hence the somewhat
+ * convoluted nature of this transfer
+ */
+ struct spi_transfer xfers[] = {
+ /* x low byte */
+ {
+ .tx_buf = read_all_tx_array,
+ .rx_buf = rx_array,
+ .bits_per_word = 16,
+ .len = 2,
+ .cs_change = 1,
+ },
+ /* x high byte */
+ {
+ .tx_buf = read_all_tx_array+2,
+ .rx_buf = rx_array + 2,
+ .bits_per_word = 16,
+ .len = 2,
+ .cs_change = 1,
+ },
+ /* y low byte */
+ {
+ .tx_buf = read_all_tx_array+4,
+ .rx_buf = rx_array + 4,
+ .bits_per_word = 16,
+ .len = 2,
+ .cs_change = 1,
+ },
+ /* y high byte */
+ {
+ .tx_buf = read_all_tx_array+6,
+ .rx_buf = rx_array + 6,
+ .bits_per_word = 16,
+ .len = 2,
+ .cs_change = 1,
+ },
+ /* z low byte */
+ {
+ .tx_buf = read_all_tx_array+8,
+ .rx_buf = rx_array + 8,
+ .bits_per_word = 16,
+ .len = 2,
+ .cs_change = 1,
+ },
+ /* z high byte */
+ {
+ .tx_buf = read_all_tx_array+10,
+ .rx_buf = rx_array + 10,
+ .bits_per_word = 16,
+ .len = 2,
+ .cs_change = 0,
+ },
+ };
+ struct spi_message msg;
+ int ret;
+ /* After these are trasmitted, the rx_buff should have
+ * values in alternate bytes
+ */
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[2], &msg);
+ spi_message_add_tail(&xfers[4], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ spi_message_add_tail(&xfers[3], &msg);
+ spi_message_add_tail(&xfers[5], &msg);
+ ret = spi_sync(st->us, &msg);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with get all accels");
+ goto err_ret;
+ }
+
+err_ret:
+ return ret;
+}
+
+static int lis3l02dq_spi_read_reg_int8_t(struct device *dev,
+ uint8_t reg_address,
+ int8_t *val)
+{
+ int ret;
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ struct spi_transfer xfer = {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ };
+
+ xfer.tx_buf = kmalloc(4, GFP_KERNEL);
+ if (xfer.tx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ xfer.rx_buf = kmalloc(4, GFP_KERNEL);
+ if (xfer.rx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_free_tx;
+ }
+ ((unsigned char *)(xfer.tx_buf))[1] = LIS3L02DQ_READ_REG(reg_address);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(st->us, &msg);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with get x offset");
+ goto error_free_rx;
+ }
+ *val = ((unsigned char *)(xfer.rx_buf))[0];
+ kfree(xfer.rx_buf);
+ kfree(xfer.tx_buf);
+ return ret;
+error_free_rx:
+ kfree(xfer.rx_buf);
+error_free_tx:
+ kfree(xfer.tx_buf);
+error_ret:
+ return ret;
+}
+
+/*Returns into to allow full 0/255 range with error codes in negative range */
+static int lis3l02dq_spi_read_reg_uint8_t(struct device *dev,
+ uint8_t reg_address)
+{
+ uint8_t val;
+ int8_t ret;
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ unsigned char *local_tx_buf;
+ unsigned char *local_rx_buf;
+ struct spi_transfer xfer = {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ };
+
+ local_rx_buf = kmalloc(4, GFP_KERNEL);
+ local_tx_buf = kmalloc(4, GFP_KERNEL);
+
+ local_tx_buf[1] = LIS3L02DQ_READ_REG(reg_address);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(st->us, &msg);
+ kfree(local_tx_buf);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with get x offset");
+ goto err_ret;
+ }
+ val = local_rx_buf[0];
+ kfree(local_rx_buf);
+
+ return val;
+err_ret:
+ kfree(local_rx_buf);
+
+ return ret;
+}
+
+static int lis3l02dq_spi_write_reg_int8_t(struct device *dev,
+ uint8_t reg_address,
+ int value)
+{
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ unsigned char *local_tx_buf;
+ struct spi_transfer xfer = {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ };
+ int ret;
+
+ local_tx_buf = kmalloc(4, GFP_KERNEL);
+ if (local_tx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ xfer.tx_buf = local_tx_buf;
+ local_tx_buf[1] = LIS3L02DQ_WRITE_REG(reg_address);
+ local_tx_buf[0] = value;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(st->us, &msg);
+ kfree(local_tx_buf);
+
+ if (ret) {
+ dev_err(&st->us->dev, "problem with writing 8 bit register");
+ goto error_ret;
+
+ }
+
+ return 0;
+error_ret:
+ return ret;
+}
+
+/* Relies on the MSB being one higher adress than the LSB */
+static int lis3l02dq_spi_write_reg_int16_t(struct device *dev,
+ uint8_t lower_reg_address,
+ int value)
+{
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ unsigned char *local_tx_buf;
+ int ret;
+ union lis3l02dq_channel tval;
+ struct spi_transfer xfers [] = { {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ }, {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ },
+ };
+
+ tval.val = value;
+ local_tx_buf = kmalloc(4, GFP_KERNEL);
+ if (local_tx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ xfers[0].tx_buf = local_tx_buf;
+ xfers[1].tx_buf = local_tx_buf + 2;
+ local_tx_buf[1] = LIS3L02DQ_WRITE_REG(lower_reg_address);
+ local_tx_buf[0] = tval.data[0];
+ local_tx_buf[3] = LIS3L02DQ_WRITE_REG(lower_reg_address+1);
+ local_tx_buf[2] = tval.data[1];
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ ret = spi_sync(st->us, &msg);
+ kfree(local_tx_buf);
+ if (ret) {
+ dev_err(&st->us->dev, "problem when writing 16 bit register");
+ return ret;
+ }
+
+ return 0;
+error_ret:
+ return ret;
+}
+
+static int lis3l02dq_spi_read_reg_int16_t(struct device *dev,
+ uint8_t lower_reg_address,
+ int16_t *val)
+{
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ unsigned char *local_tx_buf;
+ unsigned char *local_rx_buf;
+ int ret;
+ /* slight abuse, but same form */
+ union lis3l02dq_channel tval;
+ struct spi_transfer xfers [] = { {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ }, {
+ .tx_buf = NULL,
+ .rx_buf = NULL,
+ .bits_per_word = 16,
+ .len = 2,
+ },
+ };
+ local_tx_buf = kmalloc(4, GFP_KERNEL);
+ if (local_tx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ local_rx_buf = kmalloc(4, GFP_KERNEL);
+ if (local_rx_buf == NULL) {
+ ret = -ENOMEM;
+ goto error_free_tx_buf;
+ }
+ xfers[0].tx_buf = local_tx_buf;
+ xfers[0].rx_buf = local_rx_buf;
+ xfers[1].tx_buf = local_tx_buf + 2;
+ xfers[1].rx_buf = local_rx_buf + 2;
+ local_tx_buf[0] = 0;
+ local_tx_buf[1] = LIS3L02DQ_READ_REG(lower_reg_address);
+ local_tx_buf[2] = 0;
+ local_tx_buf[3] = LIS3L02DQ_READ_REG(lower_reg_address+1);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ ret = spi_sync(st->us, &msg);
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register");
+ goto error_free_rx_buf;
+ }
+ /* FIXME - endianness problem? */
+ tval.data[0] = local_rx_buf[0];
+ tval.data[1] = local_rx_buf[2];
+ kfree(local_rx_buf);
+ kfree(local_tx_buf);
+ *val = tval.val;
+ return 0;
+error_free_rx_buf:
+ kfree(local_rx_buf);
+error_free_tx_buf:
+ kfree(local_tx_buf);
+error_ret:
+ return ret;
+}
+
+
+
+static ssize_t lis3l02dq_read_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len, ret;
+ int8_t val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = lis3l02dq_spi_read_reg_int8_t(dev, this_attr->address, &val);
+ if (ret < 0)
+ goto err_ret;
+ len = sprintf(buf, "%d\n", val);
+
+ return len;
+
+err_ret:
+ return ret;
+}
+
+static ssize_t lis3l02dq_read_unsigned(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int val, len;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ val = lis3l02dq_spi_read_reg_uint8_t(dev, this_attr->address);
+ if (val < 0) {
+ dev_err(dev, "problem reading an unsigned 8 bit value");
+ goto err_ret;
+ }
+
+ len = sprintf(buf, "%d\n", val);
+ return len;
+err_ret:
+ return val;
+}
+/* Used for offsets etc so no need for lock. */
+static ssize_t lis3l02dq_write_signed(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ long val;
+ int ret;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto err_ret;
+ ret = lis3l02dq_spi_write_reg_int8_t(dev, this_attr->address, val);
+ if (ret)
+ goto err_ret;
+
+ return len;
+
+err_ret:
+ return ret;
+}
+/* Used for gains etc so no need for lock. */
+static ssize_t lis3l02dq_write_unsigned(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ ulong val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ goto err_ret;
+
+ ret = lis3l02dq_spi_write_reg_int8_t(dev, this_attr->address, val);
+ if (ret)
+ goto err_ret;
+
+ return len;
+
+err_ret:
+ return ret;
+}
+
+static ssize_t lis3l02dq_read_16bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len, ret;
+ int16_t val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = lis3l02dq_spi_read_reg_int16_t(dev, this_attr->address, &val);
+ if (ret < 0) {
+ dev_err(dev, "problem reading a signed 16 bit value from chip");
+ return ret;
+ }
+ len = sprintf(buf, "%d\n", val);
+
+ return len;
+}
+
+/* As the ring buffer contents are device dependent this functionality
+ * must remain part of the driver and not the ring buffer subsystem */
+static ssize_t
+lis3l02dq_read_accel_from_ring(struct iio_sw_ring_buffer *ring,
+ int element, char *buf)
+{
+ int ret, len;
+ struct lis3l02dq_datum data;
+
+ ret = iio_read_last_from_sw_ring(ring, (char *)(&data));
+ if (ret)
+ return ret;
+ len = sprintf(buf, "ring %d\n", data.el[element].val);
+
+ return len;
+}
+/* Prevents mode switching when running */
+static ssize_t lis3l02dq_read_accel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ ssize_t returnval;
+ int element;
+
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_RING_DATA_RDY) {
+ switch (this_attr->address) {
+ case LIS3L02DQ_REG_OUT_X_L_ADDRESS:
+ element = 0;
+ break;
+ case LIS3L02DQ_REG_OUT_Y_L_ADDRESS:
+ element = 1;
+ break;
+ case LIS3L02DQ_REG_OUT_Z_L_ADDRESS:
+ element = 2;
+ break;
+ default:
+ returnval = -EINVAL;
+ goto error_ret;
+ }
+ returnval = lis3l02dq_read_accel_from_ring(indio_dev->ring,
+ element, buf);
+ } else
+ returnval = lis3l02dq_read_16bit_signed(dev, attr, buf);
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return returnval;
+}
+
+/* For this device this is only relevant to the threshold for interrupt
+ * generation */
+static ssize_t lis3l02dq_write_16bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int ret;
+ long val;
+
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_write_reg_int16_t(dev, this_attr->address, val);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t lis3l02dq_read_av_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "280 560 1120 4480\n");
+}
+
+static ssize_t lis3l02dq_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret, len;
+ int8_t t;
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_CTRL_1_ADDRESS,
+ &t);
+ if (ret)
+ goto error_ret;
+ t &= LIS3L02DQ_DEC_MASK;
+ if (t == LIS3L02DQ_REG_CTRL_1_DF_128)
+ len = sprintf(buf, "280");
+ else if (t == LIS3L02DQ_REG_CTRL_1_DF_64)
+ len = sprintf(buf, "560");
+ else if (t == LIS3L02DQ_REG_CTRL_1_DF_32)
+ len = sprintf(buf, "1120");
+ else
+ len = sprintf(buf, "4480");
+ len += sprintf(buf+len, "\n");
+
+ return len;
+
+error_ret:
+ return ret;
+}
+
+static ssize_t lis3l02dq_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ long val;
+ int ret;
+ int8_t t;
+
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_CTRL_1_ADDRESS,
+ &t);
+ if (ret)
+ goto error_ret;
+ /* Wipe the bits clean */
+ t &= ~LIS3L02DQ_DEC_MASK;
+ switch (val) {
+ case 280:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_128;
+ break;
+ case 560:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_64;
+ break;
+ case 1120:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_32;
+ break;
+ case 4480:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret;
+ };
+
+ ret = lis3l02dq_spi_write_reg_int8_t(dev,
+ LIS3L02DQ_REG_CTRL_1_ADDRESS,
+ t);
+
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&indio_dev->mlock);
+ return len;
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+
+}
+
+static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)
+{
+ int ret, val;
+
+ st->us->mode = SPI_MODE_3;
+ spi_setup(st->us);
+ /* Write suitable defaults to ctrl1 */
+ ret = lis3l02dq_spi_write_reg_int8_t(&st->us->dev,
+ LIS3L02DQ_REG_CTRL_1_ADDRESS,
+ LIS3L02DQ_DEFAULT_CTRL1);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 1");
+ goto err_ret;
+ }
+ ret = lis3l02dq_spi_write_reg_int8_t(&st->us->dev,
+ LIS3L02DQ_REG_CTRL_2_ADDRESS,
+ LIS3L02DQ_DEFAULT_CTRL2);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 2");
+ goto err_ret;
+ }
+ val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC;
+ ret = lis3l02dq_spi_write_reg_int8_t(&st->us->dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDRESS,
+ val);
+ if (ret)
+ dev_err(&st->us->dev, "problem with interrupt cfg register");
+
+err_ret:
+
+ return ret;
+}
+
+
+
+/* These are all a case of reading / writing directly to the chip */
+
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_signed,
+ lis3l02dq_write_signed,
+ LIS3L02DQ_REG_OFFSET_X_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_signed,
+ lis3l02dq_write_signed,
+ LIS3L02DQ_REG_OFFSET_Y_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_signed,
+ lis3l02dq_write_signed,
+ LIS3L02DQ_REG_OFFSET_Z_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_X_GAIN(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_unsigned,
+ lis3l02dq_write_unsigned,
+ LIS3L02DQ_REG_GAIN_X_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_Y_GAIN(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_unsigned,
+ lis3l02dq_write_unsigned,
+ LIS3L02DQ_REG_GAIN_Y_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_Z_GAIN(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_unsigned,
+ lis3l02dq_write_unsigned,
+ LIS3L02DQ_REG_GAIN_Z_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_THRESH(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_16bit_signed,
+ lis3l02dq_write_16bit_signed,
+ LIS3L02DQ_REG_THS_L_ADDRESS);
+
+/* Obviously the reading method for these will change depending on whether
+ ring buffer capture is in use. Allow specification here of alternate
+ function? Or take the approach used above of making this a driver issue
+ rather than part of industrialio */
+static IIO_DEV_ATTR_ACCEL_X(lis3l02dq_read_accel,
+ LIS3L02DQ_REG_OUT_X_L_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_Y(lis3l02dq_read_accel,
+ LIS3L02DQ_REG_OUT_Y_L_ADDRESS);
+
+static IIO_DEV_ATTR_ACCEL_Z(lis3l02dq_read_accel,
+ LIS3L02DQ_REG_OUT_Z_L_ADDRESS);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_frequency,
+ lis3l02dq_write_frequency);
+
+static IIO_DEV_ATTR_AVAIL_SAMP_FREQ(lis3l02dq_read_av_freq);
+
+static ssize_t
+lis3l02dq_read_interrupt_config(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len, ret, set;
+ int8_t val;
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDRESS,
+ &val);
+ if (ret < 0)
+ return ret;
+ set = (val & this_attr->mask) ? 1 : 0;
+ len = sprintf(buf, "%d\n", set);
+
+ return len;
+}
+
+static ssize_t lis3l02dq_write_interrupt_config(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int ret, currentlyset, addr, changed = 0;
+ int8_t valold, controlold;
+ bool val;
+
+ val = (buf[0] == '0') ? 0 : 1;
+
+ mutex_lock(&indio_dev->mlock);
+ /* read current value */
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDRESS,
+ &valold);
+ if (ret)
+ goto error_mutex_unlock;
+
+ /* read current control */
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_CTRL_2_ADDRESS,
+ &controlold);
+ if (ret)
+ goto error_mutex_unlock;
+ currentlyset = valold & this_attr->mask ? 1 : 0;
+ if (val == false && currentlyset) {
+ valold &= ~this_attr->mask;
+ changed = 1;
+ ret = iio_remove_event_from_list(this_attr->listel);
+ if (ret)
+ goto error_mutex_unlock;
+ } else if (val == true && !currentlyset) {
+ changed = 1;
+ valold |= this_attr->mask;
+ /* move to a line spec rather than this? */
+ ret = iio_add_event_to_list(&indio_dev->interrupts[0]->ev_list,
+ this_attr->listel);
+ if (ret)
+ goto error_mutex_unlock;
+ }
+
+ if (changed) {
+ addr = LIS3L02DQ_REG_WAKE_UP_CFG_ADDRESS;
+ ret = lis3l02dq_spi_write_reg_int8_t(dev, addr, valold);
+ if (ret)
+ goto error_mutex_unlock;
+ /* This always enables the interrupt, even if we've remove the
+ * last thing using it. For this device we can use the reference
+ * count on the handler to tell us if anyone
+ * wants the interrupt */
+ controlold = this_attr->listel->refcount ?
+ (controlold | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT):
+ (controlold & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
+ addr = LIS3L02DQ_REG_CTRL_2_ADDRESS;
+ ret = lis3l02dq_spi_write_reg_int8_t(dev, addr, controlold);
+ if (ret)
+ goto error_mutex_unlock;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return len;
+
+error_mutex_unlock:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static ssize_t lis3l02dq_read_data_ready_config(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret, set, len;
+ int8_t val;
+
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_CTRL_2_ADDRESS,
+ &val);
+ if (ret < 0)
+ return ret;
+ set = (val & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION) ? 1 : 0;
+ len = sprintf(buf, "%d\n", set);
+
+ return len;
+}
+
+/* Caller responsible for locking as necessary. */
+static int __lis3l02dq_write_data_ready_config(struct device *dev,
+ struct iio_event_handler_list
+ *list,
+ long state)
+{
+ int ret;
+ int8_t valold, addr;
+ bool currentlyset, changed = 0;
+
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ ret = lis3l02dq_spi_read_reg_int8_t(dev,
+ LIS3L02DQ_REG_CTRL_2_ADDRESS,
+ &valold);
+ if (ret)
+ goto error_ret;
+
+ currentlyset
+ = valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION
+ ? 1 : 0;
+ /* if set, disable requested and ring buffer not in use (FIXME) */
+ if (state == 0 && currentlyset) {
+ valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+ ret = iio_remove_event_from_list(list);
+ if (ret)
+ goto error_ret;
+ changed = true;
+ } else if (state != 0 && !currentlyset) {
+ /* if not set, enable requested */
+ valold |= LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+ ret = iio_add_event_to_list(&indio_dev->interrupts[0]->ev_list,
+ list);
+ if (ret)
+ goto error_ret;
+ changed = true;
+ }
+ if (changed) {
+ addr = LIS3L02DQ_REG_CTRL_2_ADDRESS;
+ ret = lis3l02dq_spi_write_reg_int8_t(dev, addr, valold);
+ if (ret)
+ goto error_ret;
+ }
+
+ return 0;
+error_ret:
+ return ret;
+}
+
+/* Does nothing if the ring is in use */
+static ssize_t lis3l02dq_write_data_ready_config(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ long val;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_RING_DATA_RDY)
+ return len;
+
+ /* fixme not the simplest method out!*/
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ ret = __lis3l02dq_write_data_ready_config(dev, this_attr->listel, val);
+
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&indio_dev->mlock);
+
+ return len;
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
+ * specific to be rolled into the core.
+ */
+static void lis3l02dq_data_rdy_bh_to_ring(struct work_struct *work_s)
+{
+ struct lis3l02dq_state *st
+ = container_of(work_s, struct lis3l02dq_state,
+ work_data_rdy_ring);
+ unsigned char *rx_array;
+ int i, j;
+ struct lis3l02dq_datum ring_data;
+
+ rx_array = kmalloc(12, GFP_KERNEL);
+ if (rx_array == NULL) {
+ dev_err(&st->us->dev, "memory alloc failed in ring bh");
+ return;
+ }
+ st->inter = 0;
+
+ if (lis3l02dq_read_all(st, rx_array) >= 0) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
+ ring_data.el[i].data[j] = rx_array[i*4+j*2];
+ ring_data.time = st->last_timestamp;
+ iio_store_to_sw_ring(&st->indio_dev->ring[0],
+ (char *)(&ring_data),
+ st->last_timestamp);
+ }
+ /* so the data should now be in the rx array */
+try_again:
+ while (gpio_get_value(irq_to_gpio(st->us->irq)))
+ if (lis3l02dq_read_all(st, rx_array) >= 0) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
+ ring_data.el[i].data[j]
+ = rx_array[i*4+j*2];
+ ring_data.time = 0;
+ /* Passing a negated time stamp to indicate that
+ * we don't actually know when this occured! */
+ iio_store_to_sw_ring(&st->indio_dev->ring[0],
+ (char *)(&ring_data),
+ 0);
+ }
+ /* push data into ring buffer before trying renable */
+ enable_irq(st->us->irq);
+ if (gpio_get_value(irq_to_gpio(st->us->irq)))
+ if (st->inter == 0) {
+ disable_irq_nosync(st->us->irq);
+ goto try_again;
+ }
+ kfree(rx_array);
+
+ return;
+}
+
+static void lis3l02dq_data_rdy_bh_to_event(struct work_struct *work_s)
+{
+ struct lis3l02dq_state *st
+ = container_of(work_s, struct lis3l02dq_state,
+ work_data_rdy_event);
+
+ /* send an event up to user space */
+ iio_put_event(st->indio_dev, 0, IIO_EVENT_CODE_DATA_RDY,
+ st->last_timestamp);
+ enable_irq(st->us->irq);
+
+ return;
+}
+
+static int lis3l02dq_data_rdy_event_th(struct iio_dev *dev_info,
+ int index,
+ s64 timestamp,
+ int no_test)
+{
+ struct lis3l02dq_state *st = dev_info->dev_data;
+
+ st->last_timestamp = timestamp;
+ schedule_work(&st->work_data_rdy_event);
+
+ return IRQ_HANDLED;
+}
+
+static int lis3l02dq_data_rdy_ring_th(struct iio_dev *dev_info,
+ int index,
+ s64 timestamp,
+ int no_test)
+{
+ struct lis3l02dq_state *st = dev_info->dev_data;
+
+ st->last_timestamp = timestamp;
+ schedule_work(&st->work_data_rdy_ring);
+ st->inter = 1;
+
+ return IRQ_HANDLED;
+}
+
+static int lis3l02dq_thresh_handler_th(struct iio_dev *dev_info,
+ int index,
+ s64 timestamp,
+ int no_test)
+{
+ struct lis3l02dq_state *st = dev_info->dev_data;
+
+ /* Stash the timestamp somewhere convenient for the bh */
+ st->last_timestamp = timestamp;
+ schedule_work(&st->work_cont_thresh.ws);
+
+ return 0;
+}
+
+
+/* Unforunately it appears the interrupt won't clear unless you read from the
+ * src register.
+ */
+static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)
+{
+ struct iio_work_cont *wc
+ = container_of(work_s, struct iio_work_cont, ws_nocheck);
+ struct lis3l02dq_state *st = wc->st;
+
+ int8_t t;
+
+ lis3l02dq_spi_read_reg_int8_t(st->indio_dev->dev,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDRESS,
+ &t);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
+ iio_put_event(st->indio_dev, 0,
+ IIO_EVENT_CODE_ACCEL_Z_HIGH,
+ st->last_timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
+ iio_put_event(st->indio_dev, 0,
+ IIO_EVENT_CODE_ACCEL_Z_LOW,
+ st->last_timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
+ iio_put_event(st->indio_dev, 0,
+ IIO_EVENT_CODE_ACCEL_Y_HIGH,
+ st->last_timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
+ iio_put_event(st->indio_dev, 0,
+ IIO_EVENT_CODE_ACCEL_Y_LOW,
+ st->last_timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
+ iio_put_event(st->indio_dev, 0,
+ IIO_EVENT_CODE_ACCEL_X_HIGH,
+ st->last_timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
+ iio_put_event(st->indio_dev, 0,
+ IIO_EVENT_CODE_ACCEL_X_LOW,
+ st->last_timestamp);
+
+ enable_irq(st->us->irq);
+ /* Ack (and allow for new interrupts? (not clear on data sheet ) )*/
+ lis3l02dq_spi_read_reg_int8_t(st->indio_dev->dev,
+ LIS3L02DQ_REG_WAKE_UP_ACK_ADDRESS,
+ &t);
+
+ return;
+}
+
+/* This only enables direct output of data ready as an event - not the ring*/
+IIO_EVENT_ATTR_DATA_RDY(lis3l02dq_read_data_ready_config,
+ lis3l02dq_write_data_ready_config,
+ 0,
+ &lis3l02dq_data_rdy_event_th);
+
+/* This is an event as it is a response to a physical interrupt */
+IIO_EVENT_SH(sw_ring_enable, &lis3l02dq_data_rdy_ring_th);
+
+static int lis3l02dq_data_rdy_ring_preenable(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ __lis3l02dq_write_data_ready_config(&st->us->dev,
+ &iio_event_data_rdy,
+ 0);
+ return 0;
+}
+
+static int lis3l02dq_data_rdy_ring_postenable(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ __lis3l02dq_write_data_ready_config(&st->us->dev,
+ &iio_event_sw_ring_enable,
+ 1);
+ return 0;
+}
+
+static int lis3l02dq_data_rdy_ring_predisable(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ __lis3l02dq_write_data_ready_config(&st->us->dev,
+ &iio_event_sw_ring_enable,
+ 0);
+ return 0;
+}
+
+/* A shared handler for a number of threshold types */
+IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th);
+
+IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_threshold,
+ lis3l02dq_read_interrupt_config,
+ lis3l02dq_write_interrupt_config,
+ LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH);
+
+IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_threshold,
+ lis3l02dq_read_interrupt_config,
+ lis3l02dq_write_interrupt_config,
+ LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH);
+
+IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_threshold,
+ lis3l02dq_read_interrupt_config,
+ lis3l02dq_write_interrupt_config,
+ LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH);
+
+IIO_EVENT_ATTR_ACCEL_X_LOW_SH(iio_event_threshold,
+ lis3l02dq_read_interrupt_config,
+ lis3l02dq_write_interrupt_config,
+ LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW);
+
+IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(iio_event_threshold,
+ lis3l02dq_read_interrupt_config,
+ lis3l02dq_write_interrupt_config,
+ LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW);
+
+IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(iio_event_threshold,
+ lis3l02dq_read_interrupt_config,
+ lis3l02dq_write_interrupt_config,
+ LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW);
+
+static struct attribute *lis3l02dq_event_attributes[] = {
+ &iio_event_attr_x_high.dev_attr.attr,
+ &iio_event_attr_y_high.dev_attr.attr,
+ &iio_event_attr_z_high.dev_attr.attr,
+ &iio_event_attr_x_low.dev_attr.attr,
+ &iio_event_attr_y_low.dev_attr.attr,
+ &iio_event_attr_z_low.dev_attr.attr,
+ &iio_event_attr_data_rdy.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group lis3l02dq_event_attribute_group = {
+ .attrs = lis3l02dq_event_attributes,
+};
+
+static struct attribute *lis3l02dq_attributes[] = {
+ &iio_dev_attr_x_offset.dev_attr.attr,
+ &iio_dev_attr_y_offset.dev_attr.attr,
+ &iio_dev_attr_z_offset.dev_attr.attr,
+ &iio_dev_attr_x_gain.dev_attr.attr,
+ &iio_dev_attr_y_gain.dev_attr.attr,
+ &iio_dev_attr_z_gain.dev_attr.attr,
+ &iio_dev_attr_thresh.dev_attr.attr,
+ &iio_dev_attr_x.dev_attr.attr,
+ &iio_dev_attr_y.dev_attr.attr,
+ &iio_dev_attr_z.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_available_sampling_frequency.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lis3l02dq_attribute_group = {
+ .attrs = lis3l02dq_attributes,
+};
+
+
+static int __devinit lis3l02dq_probe(struct spi_device *spi)
+{
+ struct lis3l02dq_state *st;
+ int ret;
+ /* must init this somewhere */
+ INIT_LIST_HEAD(&iio_event_sw_ring_enable.list);
+ st = kzalloc(sizeof(struct lis3l02dq_state), GFP_KERNEL);
+ if (st == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+
+ st->us = spi;
+ /* setup the industrialio driver allocated elements */
+ st->indio_dev = kzalloc(sizeof(struct iio_dev), GFP_KERNEL);
+ if (st->indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_free_st;
+ }
+
+ st->indio_dev->dev = &spi->dev;
+ st->indio_dev->num_interrupt_lines = 1;
+ st->indio_dev->event_attrs = &lis3l02dq_event_attribute_group;
+ st->indio_dev->attrs = &lis3l02dq_attribute_group;
+ st->indio_dev->dev_data = (void *)(st);
+ /* setup parameters of the ring buffer */
+ st->indio_dev->ring_bytes_per_datum = sizeof(struct lis3l02dq_datum);
+ st->indio_dev->ring_length = 500;
+ st->indio_dev->driver_module = THIS_MODULE;
+ st->indio_dev->modes = INDIO_DIRECT_MODE | INDIO_RING_DATA_RDY;
+ st->indio_dev->ring_preenable = &lis3l02dq_data_rdy_ring_preenable;
+ st->indio_dev->ring_postenable = &lis3l02dq_data_rdy_ring_postenable;
+ st->indio_dev->ring_predisable = &lis3l02dq_data_rdy_ring_predisable;
+
+ ret = iio_device_register(st->indio_dev);
+ if (ret < 0)
+ goto error_free_dev;
+
+ if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+ INIT_WORK(&st->work_data_rdy_ring,
+ lis3l02dq_data_rdy_bh_to_ring);
+ INIT_WORK(&st->work_data_rdy_event,
+ lis3l02dq_data_rdy_bh_to_event);
+
+ /* This is a little unusual, in that the device seems
+ to need a full read of the interrupt source reg before
+ the interrupt will reset.
+ Hence the two handlers are the same */
+
+ INIT_IIO_WORK_CONT(&(st->work_cont_thresh),
+ lis3l02dq_thresh_handler_bh_no_check,
+ lis3l02dq_thresh_handler_bh_no_check,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDRESS,
+ 0,
+ st);
+ st->inter = 0;
+ ret = iio_register_interrupt_line(spi->irq,
+ st->indio_dev,
+ 0,
+ IRQF_TRIGGER_RISING,
+ "lis3l02dq");
+ if (ret)
+ goto error_unregister_dev;
+ } else
+ st->indio_dev->modes &= ~INDIO_RING_DATA_RDY;
+
+ /* Get the device into a sane initial state */
+ ret = lis3l02dq_initial_setup(st);
+ if (ret)
+ goto error_unregister_line;
+ return 0;
+
+error_unregister_line:
+ if (st->indio_dev->modes & INDIO_RING_DATA_RDY)
+ iio_unregister_interrupt_line(st->indio_dev, 0);
+error_unregister_dev:
+ iio_device_unregister(st->indio_dev);
+error_free_dev:
+ kfree(st->indio_dev);
+error_free_st:
+ kfree(st);
+ error_ret:
+ return ret;
+}
+
+/* Power down the device */
+static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_write_reg_int8_t(&st->us->dev,
+ LIS3L02DQ_REG_CTRL_1_ADDRESS,
+ 0);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with turning device off: ctrl1");
+ goto err_ret;
+ }
+
+ ret = lis3l02dq_spi_write_reg_int8_t(&st->us->dev,
+ LIS3L02DQ_REG_CTRL_2_ADDRESS,
+ 0);
+ if (ret)
+ dev_err(&st->us->dev, "problem with turning device off: ctrl2");
+err_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int lis3l02dq_remove(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct lis3l02dq_state *st = indio_dev->dev_data;
+
+ /* stop the device*/
+ /* Amongst other things this must ensure no more interrupts are
+ * generated by the device.
+ */
+ ret = lis3l02dq_stop_device(indio_dev);
+ /* Make sure all bottom halfs of interrupts are done */
+ flush_scheduled_work();
+ if (ret)
+ goto err_ret;
+ /* Fixme slightly misleading test condition - even if valid */
+ if (indio_dev->modes & INDIO_RING_DATA_RDY)
+ iio_unregister_interrupt_line(indio_dev, 0);
+ iio_device_unregister(indio_dev);
+ kfree(indio_dev);
+ kfree(st);
+ return 0;
+
+err_ret:
+ return ret;
+}
+
+static struct spi_driver lis3l02dq_driver = {
+ .driver = {
+ .name = "lis3l02dq",
+ .owner = THIS_MODULE,
+ },
+ .probe = lis3l02dq_probe,
+ .remove = __devexit_p(lis3l02dq_remove),
+};
+
+static __init int lis3l02dq_init(void)
+{
+ return spi_register_driver(&lis3l02dq_driver);
+}
+
+static __exit void lis3l02dq_exit(void)
+{
+ spi_unregister_driver(&lis3l02dq_driver);
+}
+
+module_init(lis3l02dq_init);
+module_exit(lis3l02dq_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <[email protected]>");
+MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
+MODULE_LICENSE("GPL v2");
From: Jonathan Cameron <[email protected]>
Add support for the sca3000 series of accelerometers from VTI using the
industrialio subsystem. Includes event interfaces (via chrdevs) for general
events for events relating to the hardware ring buffer. Chrdev access to the
hardware ring buffer and direct access to and control of the device via sysfs
interfaces within the industrialio class.
Signed-off-by: Jonathan Cameron <[email protected]>
---
drivers/industrialio/accelerometer/Kconfig | 7
drivers/industrialio/accelerometer/Makefile | 1
drivers/industrialio/accelerometer/sca3000.c | 1547 +++++++++++++++++++++++++++
include/linux/industrialio/sca3000.h | 22
4 files changed, 1577 insertions(+)
--- a/drivers/industrialio/accelerometer/Kconfig 2008-07-23 17:03:01.000000000 +0100
+++ b/drivers/industrialio/accelerometer/Kconfig 2008-07-23 16:57:39.000000000 +0100
@@ -17,3 +17,10 @@ config LIS3L02DQ_SPI
Say yes here to build support for the ST LIS3L02DQ accelerometer via
an SPI bus.
+config SCA3000
+ 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.
--- a/drivers/industrialio/accelerometer/Makefile 2008-07-23 17:03:01.000000000 +0100
+++ b/drivers/industrialio/accelerometer/Makefile 2008-07-14 17:26:34.000000000 +0100
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_LIS3L02DQ_SPI) += lis3l02dq.o
+obj-$(CONFIG_SCA3000) += sca3000.o
--- a/drivers/industrialio/accelerometer/sca3000.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/industrialio/accelerometer/sca3000.c 2008-07-23 17:09:42.000000000 +0100
@@ -0,0 +1,1547 @@
+/*
+ * sca3000.c -- support VTI sca3000 series accelerometers
+ * via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <[email protected]>
+ *
+ * See industrialio/accels/sca3000.h for comments.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/rtc.h>
+#include <linux/industrialio.h>
+#include <linux/industrialio_sysfs.h>
+
+/* Platform data definitions */
+#include <linux/industrialio/sca3000.h>
+/* Local to driver defintions - to be shared with i2c driver */
+#include "sca3000.h"
+
+#define sca3000_state_from_dev(dev) \
+ (((struct iio_dev *)(spi_get_drvdata(to_spi_device(dev))))->dev_data)
+
+/* Note where option modes are not defined, the chip simply does not
+ * support any.
+ */
+static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = {
+ {
+ .name = "sca3000-d01",
+ .temp_output = true,
+ .measurement_mode_freq = 250,
+ .option_mode_1 = SCA3000_OP_MODE_BYPASS,
+ /* Not given on data sheet assuming same as for measurement
+ * mode.
+ */
+ .option_mode_1_freq = 250,
+ }, {
+ /* No data sheet available - may be the same as the 3100-d03?*/
+ .name = "sca3000-d03",
+ .temp_output = true,
+ }, {
+ .name = "sca3000-e02",
+ .measurement_mode_freq = 125,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 63,
+ }, {
+ .name = "sca3000-e04",
+ .measurement_mode_freq = 100,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ }, {
+ .name = "sca3000-e05",
+ .measurement_mode_freq = 200,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ }, {
+ /* No data sheet available.
+ * Frequencies are unknown.
+ */
+ .name = "sca3000-l01",
+ .temp_output = true,
+ .option_mode_1 = SCA3000_OP_MODE_BYPASS,
+ },
+};
+
+/* Remove dynamic alloc of tx?*/
+static int sca3000_write_reg(struct device *dev,
+ uint8_t address,
+ uint8_t val)
+{
+ int ret;
+ struct spi_device *spi_dev = to_spi_device(dev);
+ struct spi_transfer xfer = {
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ };
+ struct spi_message msg;
+ char *tx;
+
+ tx = kmalloc(2, GFP_KERNEL);
+ if (tx == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ xfer.tx_buf = tx;
+ tx[0] = SCA3000_WRITE_REG(address);
+ tx[1] = val;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(spi_dev, &msg);
+ if (ret)
+ dev_err(dev, "problem writing register");
+ kfree(tx);
+
+error_ret:
+ return ret;
+}
+
+/* Note that the responsibility for deallocating rx_p is left
+ * to the caller.
+ */
+static int sca3000_read_data(struct device *dev,
+ uint8_t reg_address_high,
+ char **rx_p,
+ int len)
+{
+ int ret;
+ struct spi_message msg;
+ struct spi_device *spi_dev = to_spi_device(dev);
+ struct spi_transfer xfer = {
+ .bits_per_word = 8,
+ .len = len + 1,
+ .cs_change = 0,
+ };
+ char *tx;
+
+ *rx_p = kmalloc(len + 1, GFP_KERNEL);
+ if (*rx_p == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ tx = kzalloc(len + 1, GFP_KERNEL);
+ if (tx == NULL) {
+ ret = -ENOMEM;
+ goto error_free_rx;
+ }
+ xfer.tx_buf = tx;
+ xfer.rx_buf = *rx_p;
+
+ tx[0] = SCA3000_READ_REG(reg_address_high);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(spi_dev, &msg);
+ kfree(tx);
+ if (ret) {
+ dev_err(dev, "problem reading register");
+ goto error_free_rx;
+ }
+
+ return 0;
+error_free_rx:
+ kfree(*rx_p);
+error_ret:
+ return ret;
+
+}
+
+static int sca3000_reg_lock_on(struct device *dev)
+{
+
+ char *rx;
+ int ret = 0;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_STATUS, &rx, 1);
+ if (ret < 0)
+ goto error_ret;
+ ret = (rx[1] & SCA3000_LOCKED) ? 0 : 1;
+ kfree(rx);
+
+error_ret:
+ return ret;
+}
+
+/* Unlock the control registers. Note the device does not appear to support
+ * doing this in a single transfer. Should only ever be used as part of a ctrl
+ * reg read. Note that on the pxa27x the cs_change settings have no effect.
+ */
+
+static int __sca3000_unlock_reg_lock(struct device *dev)
+{
+ int ret;
+ struct spi_device *spi_dev = to_spi_device(dev);
+ struct spi_message msg;
+ struct spi_transfer xfer[3] = {
+ {
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ },
+ };
+ char *tx;
+
+ tx = kmalloc(6, GFP_KERNEL);
+ if (tx == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ tx[0] = tx[2] = tx[4] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ tx[1] = 0x00;
+ tx[3] = 0x50;
+ tx[5] = 0xA0;
+ xfer[0].tx_buf = tx;
+ xfer[1].tx_buf = tx+2;
+ xfer[2].tx_buf = tx+4;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer[0], &msg);
+ spi_message_add_tail(&xfer[1], &msg);
+ spi_message_add_tail(&xfer[2], &msg);
+ ret = spi_sync(spi_dev, &msg);
+ kfree(tx);
+
+error_ret:
+ return ret;
+}
+
+/* Certain control registers are protected against overwriting by the lock
+ * register and use a shared write address. This function allows writing of
+ * these registers.
+ */
+static int sca3000_write_ctrl_reg(struct device *dev, uint8_t sel, uint8_t val)
+{
+
+ int ret;
+
+
+ /* Check whether the lock is on and unlock if needed. */
+ ret = sca3000_reg_lock_on(dev);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(dev);
+ if (ret)
+ goto error_ret;
+ }
+
+ /* Set the control select register */
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_CTRL_SEL, sel);
+ if (ret)
+ goto error_ret;
+
+ /* Write the actual value into the register */
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_CTRL_DATA, val);
+
+error_ret:
+ return ret;
+}
+
+/* Crucial that lock is called before calling this */
+static int sca3000_read_ctrl_reg(struct device *dev,
+ uint8_t ctrl_reg,
+ char **rx_p)
+{
+ int ret;
+
+ ret = sca3000_reg_lock_on(dev);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(dev);
+ if (ret)
+ goto error_ret;
+ }
+
+ /* Set the control select register */
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_CTRL_SEL, ctrl_reg);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_CTRL_DATA, rx_p, 1);
+
+error_ret:
+ return ret;
+}
+
+/* This status check function is only needed for debugging */
+static int sca3000_check_status(struct device *dev)
+{
+ char *rx;
+ int ret;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_STATUS, &rx, 1);
+ if (ret < 0)
+ goto error_ret;
+ if (rx[1] & SCA3000_EEPROM_CS_ERROR)
+ dev_err(dev, "eeprom error \n");
+ if (rx[1] & SCA3000_SPI_FRAME_ERROR)
+ dev_err(dev, "Previous SPI Frame was corrupt\n");
+ kfree(rx);
+
+error_ret:
+ return ret;
+}
+
+/* All of these are designed to give equivalent values */
+/* FIXME: Is there a cleaner way of doing this? */
+inline int sca3000_13bit_convert(uint8_t msb, uint8_t lsb)
+{
+ int16_t val;
+
+ val = ((lsb >> 3) & 0x1F) | (msb << 5);
+ /* sign fill */
+ val |= (val & (1 << 12)) ? 0xE000 : 0;
+
+ return val;
+}
+
+
+/* These are described as signed 12 bit on the data sheet, which appears
+ * to be a conventional 2's complement 13 bit.
+ */
+static ssize_t sca3000_read_13bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0, ret;
+ int val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ char *rx;
+
+ ret = sca3000_read_data(dev, this_attr->address, &rx, 2);
+ if (ret < 0)
+ goto error_ret;
+ /* Process the data */
+ val = sca3000_13bit_convert(rx[1], rx[2]);
+ len += sprintf(buf + len, "%d\n", val);
+ kfree(rx);
+
+ return len;
+error_ret:
+ return ret;
+}
+
+static ssize_t sca3000_show_rev(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0, ret;
+ char *rx;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_REVID, &rx, 1);
+ if (ret < 0)
+ goto error_ret;
+ len += sprintf(buf + len,
+ "major=%d, minor=%d\n",
+ rx[1] & SCA3000_REVID_MAJOR_MASK,
+ rx[1] & SCA3000_REVID_MINOR_MASK);
+ kfree(rx);
+
+ return len;
+
+error_ret:
+ return ret;
+}
+
+static ssize_t
+sca3000_show_available_measurement_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int len = 0;
+
+ len += sprintf(buf + len, "0 - normal mode");
+ switch (st->info->option_mode_1) {
+ case SCA3000_OP_MODE_NARROW:
+ len += sprintf(buf + len, ", 1 - narrow mode");
+ break;
+ case SCA3000_OP_MODE_BYPASS:
+ len += sprintf(buf + len, ", 1 - bypass mode");
+ break;
+ };
+ switch (st->info->option_mode_2) {
+ case SCA3000_OP_MODE_WIDE:
+ len += sprintf(buf + len, ", 2 - wide mode");
+ break;
+ }
+ /* always supported */
+ len += sprintf(buf + len, " 3 - motion detection \n");
+
+ return len;
+}
+
+static ssize_t
+sca3000_show_measurement_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int len = 0, ret;
+ char *rx;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ /* mask bottom 2 bits - only ones that are relevant */
+ rx[1] &= 0x03;
+ switch (rx[1]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "0 - normal mode\n");
+ break;
+ case SCA3000_MEAS_MODE_MOT_DET:
+ len += sprintf(buf + len, "3 - motion detection\n");
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ switch (st->info->option_mode_1) {
+ case SCA3000_OP_MODE_NARROW:
+ len += sprintf(buf + len, "1 - narrow mode\n");
+ break;
+ case SCA3000_OP_MODE_BYPASS:
+ len += sprintf(buf + len, "1 - bypass mode\n");
+ break;
+ };
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ switch (st->info->option_mode_2) {
+ case SCA3000_OP_MODE_WIDE:
+ len += sprintf(buf + len, "2 - wide mode\n");
+ break;
+ }
+ break;
+ };
+
+ return len;
+
+error_ret:
+ return ret;
+}
+
+static ssize_t
+sca3000_store_measurement_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int ret;
+ char *rx;
+ int mask = 0x03;
+ long val;
+
+ mutex_lock(&st->lock);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ rx[1] &= ~mask;
+ rx[1] |= (val & mask);
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE, rx[1]);
+ if (ret)
+ goto error_free_rx;
+ mutex_unlock(&st->lock);
+
+ return len;
+
+error_free_rx:
+ kfree(rx);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+
+/* A couple of decidedly non standard attributes - if I see them enough they
+ * will make it into the sysfs header if not, made available like this. */
+static IIO_DEVICE_ATTR(available_measurement_modes, S_IRUGO,
+ sca3000_show_available_measurement_modes,
+ NULL, 0);
+
+static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR,
+ sca3000_show_measurement_mode,
+ sca3000_store_measurement_mode,
+ 0);
+
+/* More standard parameters */
+static IIO_DEV_ATTR_REV(sca3000_show_rev);
+
+static IIO_DEV_ATTR_ACCEL_X(sca3000_read_13bit_signed,
+ SCA3000_REG_ADDR_X_MSB);
+static IIO_DEV_ATTR_ACCEL_Y(sca3000_read_13bit_signed,
+ SCA3000_REG_ADDR_Y_MSB);
+static IIO_DEV_ATTR_ACCEL_Z(sca3000_read_13bit_signed,
+ SCA3000_REG_ADDR_Z_MSB);
+
+/* This is only relevant to the ring buffer - and is dependent on measurement
+ * mode. Note that data sheet gives rather wide tollerances for these so int
+ * devision will give good enough answer and not all chips have them specified
+ * at all.
+ */
+static ssize_t sca3000_read_av_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int len = 0, ret;
+ char *rx;
+ /* What mode are we in? */
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ rx[1] &= 0x03;
+ switch (rx[1]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->measurement_mode_freq,
+ st->info->measurement_mode_freq/2,
+ st->info->measurement_mode_freq/4);
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_1_freq,
+ st->info->option_mode_1_freq/2,
+ st->info->option_mode_1_freq/4);
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_2_freq,
+ st->info->option_mode_2_freq/2,
+ st->info->option_mode_2_freq/4);
+ break;
+ };
+ kfree(rx);
+ return len;
+error_ret:
+ return ret;
+}
+static IIO_DEV_ATTR_AVAIL_SAMP_FREQ(sca3000_read_av_freq);
+
+static ssize_t sca3000_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int ret, len = 0, base_freq = 0;
+ char *rx;
+
+ /* What mode are we in? */
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ switch (0x03 & rx[1]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ base_freq = st->info->measurement_mode_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ base_freq = st->info->option_mode_1_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ base_freq = st->info->option_mode_2_freq;
+ break;
+ };
+ kfree(rx);
+ mutex_lock(&st->lock);
+ ret = sca3000_read_ctrl_reg(dev, SCA3000_REG_CTRL_SEL_OUT_CTRL, &rx);
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+ if (base_freq > 0)
+ switch (rx[1]&0x03) {
+ case 0x00:
+ case 0x03:
+ len = sprintf(buf, "%d\n", base_freq);
+ break;
+ case 0x01:
+ len = sprintf(buf, "%d\n", base_freq/2);
+ break;
+ case 0x02:
+ len = sprintf(buf, "%d\n", base_freq/4);
+ break;
+ };
+ kfree(rx);
+ return len;
+error_ret:
+ return ret;
+}
+
+static ssize_t sca3000_set_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int ret, base_freq = 0;
+ char *rx;
+ long val;
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ /* What mode are we in? */
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ switch (0x03 & rx[1]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ base_freq = st->info->measurement_mode_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ base_freq = st->info->option_mode_1_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ base_freq = st->info->option_mode_2_freq;
+ break;
+ };
+ kfree(rx);
+ mutex_lock(&st->lock);
+
+ ret = sca3000_read_ctrl_reg(dev, SCA3000_REG_CTRL_SEL_OUT_CTRL, &rx);
+ if (ret)
+ goto error_free_lock;
+ /* clear the bits */
+ rx[1] &= ~0x03;
+ if (val == base_freq/2) {
+ rx[1] |= SCA3000_OUT_CTRL_BUF_DIV_2;
+ } else if (val == base_freq/4) {
+ rx[1] |= SCA3000_OUT_CTRL_BUF_DIV_4;
+ } else if (val != base_freq) {
+ ret = -EINVAL;
+ goto error_free_lock;
+ }
+ ret = sca3000_write_ctrl_reg(dev, SCA3000_REG_CTRL_SEL_OUT_CTRL, rx[1]);
+ if (ret)
+ goto error_free_lock;
+ mutex_unlock(&st->lock);
+ return len;
+
+error_free_lock:
+ mutex_unlock(&st->lock);
+error_ret:
+ return ret;
+}
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ sca3000_read_frequency,
+ sca3000_set_frequency);
+/* The alignment of data in here is downright odd. See data sheet */
+/* Converting this into a meaningful value is left to inline functions in
+ * userspace part of header */
+static ssize_t sca3000_read_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0, ret;
+ int val;
+ char *rx;
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_TEMP_MSB, &rx, 2);
+ if (ret < 0)
+ goto error_ret;
+ val = ((rx[1]&0x3F) << 3) | ((rx[2] & 0xE0) >> 5);
+ len += sprintf(buf + len, "%d\n", val);
+ kfree(rx);
+
+ return len;
+
+error_ret:
+ return ret;
+}
+static IIO_DEV_ATTR_TEMP(sca3000_read_temp);
+
+static ssize_t sca3000_show_ring_bps_available(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "8, 11\n");;
+}
+static IIO_DEV_ATTR_RING_BPS_AVAILABLE(sca3000_show_ring_bps_available);
+
+static ssize_t sca3000_show_ring_bps(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret, len;
+ char *rx;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ if (rx[1] & SCA3000_RING_BUF_8BIT)
+ len = sprintf(buf, "8\n");
+ else
+ len = sprintf(buf, "11\n");
+ return len;
+
+error_ret:
+ return ret;
+}
+
+static ssize_t sca3000_store_ring_bps(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int ret;
+ char *rx;
+ long val;
+
+ mutex_lock(&st->lock);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ switch (val) {
+ case 8:
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ rx[1] | SCA3000_RING_BUF_8BIT);
+ st->bps = 8;
+ break;
+ case 11:
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ rx[1] & ~SCA3000_RING_BUF_8BIT);
+ st->bps = 11;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&st->lock);
+ return len;
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static IIO_DEV_ATTR_RING_BPS(S_IRUGO | S_IWUSR,
+ sca3000_show_ring_bps,
+ sca3000_store_ring_bps);
+
+static ssize_t sca3000_show_thresh(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int len = 0, ret;
+ char *rx;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_ctrl_reg(dev,
+ this_attr->address,
+ &rx);
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+ len += sprintf(buf+len, "%d\n", rx[1]);
+ kfree(rx);
+
+ return len;
+error_ret:
+ return ret;
+}
+
+static ssize_t sca3000_write_thresh(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ long val;
+
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ mutex_lock(&st->lock);
+ ret = sca3000_write_ctrl_reg(dev, this_attr->address, val);
+ mutex_unlock(&st->lock);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t sca3000_show_ring_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0, ret, val;
+ char *rx;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ val = (rx[1] & SCA3000_RING_BUF_ENABLE) ? 1 : 0;
+ len += sprintf(buf + len, "%d\n", val);
+ kfree(rx);
+
+ return len;
+error_ret:
+ return ret;
+}
+
+static ssize_t sca3000_store_ring_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ long val;
+ int ret;
+ char *rx;
+
+ mutex_lock(&st->lock);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+
+ if (val)
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ (rx[1] | SCA3000_RING_BUF_ENABLE));
+ else
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ (rx[1] & ~SCA3000_RING_BUF_ENABLE));
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&st->lock);
+
+ return len;
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static IIO_DEV_ATTR_ACCEL_THRESH_X(S_IRUGO | S_IWUSR,
+ sca3000_show_thresh,
+ sca3000_write_thresh,
+ SCA3000_REG_CTRL_SEL_MD_X_TH);
+static IIO_DEV_ATTR_ACCEL_THRESH_Y(S_IRUGO | S_IWUSR,
+ sca3000_show_thresh,
+ sca3000_write_thresh,
+ SCA3000_REG_CTRL_SEL_MD_Y_TH);
+static IIO_DEV_ATTR_ACCEL_THRESH_Z(S_IRUGO | S_IWUSR,
+ sca3000_show_thresh,
+ sca3000_write_thresh,
+ SCA3000_REG_CTRL_SEL_MD_Z_TH);
+
+/* FIXME: Move into the ring registration stuff ? */
+static IIO_DEV_ATTR_HW_RING_ENABLE(sca3000_show_ring_enable,
+ sca3000_store_ring_enable);
+/*fixme, cleaner and readable way of handling these two subtly different tables?
+*/
+static struct attribute *sca3000_attributes[] = {
+ &iio_dev_attr_revision.dev_attr.attr,
+ &iio_dev_attr_x.dev_attr.attr,
+ &iio_dev_attr_y.dev_attr.attr,
+ &iio_dev_attr_z.dev_attr.attr,
+ &iio_dev_attr_thresh_x.dev_attr.attr,
+ &iio_dev_attr_thresh_y.dev_attr.attr,
+ &iio_dev_attr_thresh_z.dev_attr.attr,
+ &iio_dev_attr_hw_ring_enable.dev_attr.attr,
+ &iio_dev_attr_available_measurement_modes.dev_attr.attr,
+ &iio_dev_attr_measurement_mode.dev_attr.attr,
+ &iio_dev_attr_ring_bps.dev_attr.attr,
+ &iio_dev_attr_ring_bps_available.dev_attr.attr,
+ &iio_dev_attr_available_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute *sca3000_attributes_with_temp[] = {
+ &iio_dev_attr_revision.dev_attr.attr,
+ &iio_dev_attr_x.dev_attr.attr,
+ &iio_dev_attr_y.dev_attr.attr,
+ &iio_dev_attr_z.dev_attr.attr,
+ &iio_dev_attr_thresh_x.dev_attr.attr,
+ &iio_dev_attr_thresh_y.dev_attr.attr,
+ &iio_dev_attr_thresh_z.dev_attr.attr,
+ &iio_dev_attr_hw_ring_enable.dev_attr.attr,
+ &iio_dev_attr_available_measurement_modes.dev_attr.attr,
+ &iio_dev_attr_measurement_mode.dev_attr.attr,
+ &iio_dev_attr_ring_bps.dev_attr.attr,
+ &iio_dev_attr_ring_bps_available.dev_attr.attr,
+ &iio_dev_attr_available_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ /* Only present if temp sensor is */
+ &iio_dev_attr_temp.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group sca3000_attribute_group = {
+ .attrs = sca3000_attributes,
+};
+
+static const struct attribute_group sca3000_attribute_group_with_temp = {
+ .attrs = sca3000_attributes_with_temp,
+};
+
+/* depending on event, push to the ring buffer event chrdev or the event one */
+static void sca3000_interrupt_handler_bh(struct work_struct *work_s)
+{
+ struct sca3000_state *st
+ = container_of(work_s, struct sca3000_state,
+ interrupt_handler_ws);
+ struct spi_device *spi_dev = to_spi_device(st->indio_dev->dev);
+ char *rx;
+
+
+ enable_irq(spi_dev->irq);
+ sca3000_read_data(&spi_dev->dev, SCA3000_REG_ADDR_INT_STATUS, &rx, 1);
+
+ /* may need to escalate an event depending on what has previously
+ * occured. Note no events are actually sent if no-one is listening.*/
+ if (rx[1] & SCA3000_INT_STATUS_THREE_QUARTERS)
+ iio_put_or_escallate_ring_event(&st->hw_ring->buf,
+ IIO_EVENT_CODE_RING_75_FULL, 0);
+ else if (rx[1] & SCA3000_INT_STATUS_HALF)
+ iio_put_ring_event(&st->hw_ring->buf,
+ IIO_EVENT_CODE_RING_50_FULL, 0);
+ return;
+}
+
+/* Device deploys a unified interrupt status register */
+static int sca3000_handler_th(struct iio_dev *dev_info,
+ int index,
+ s64 timestamp,
+ int no_test)
+{
+ struct sca3000_state *st = dev_info->dev_data;
+ st->last_timestamp = timestamp;
+ /* have to read the interrupt status to acknowledge the interrupt so
+ no check is irrelvant */
+ schedule_work(&st->interrupt_handler_ws);
+ return 0;
+}
+
+/* First query if motion detection is enabled, then if this axis is on */
+static ssize_t sca3000_query_mo_det(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ int ret, len = 0;
+ char *rx;
+ uint8_t protect_mask = 0x03;
+ /* read current value of mode register */
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+
+ if ((rx[1]&protect_mask) != SCA3000_MEAS_MODE_MOT_DET)
+ len += sprintf(buf + len, "0\n");
+ else {
+ kfree(rx);
+ ret = sca3000_read_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ &rx);
+ if (ret)
+ goto error_ret;
+ /* only supporting logical or's for now */
+ if (rx[1]&this_attr->mask)
+ len += sprintf(buf + len, "1\n");
+ else
+ len += sprintf(buf + len, "0\n");
+ }
+ mutex_unlock(&st->lock);
+ kfree(rx);
+
+ return len;
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+
+static ssize_t sca3000_query_free_fall_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int val, ret, len = 0;
+ char *rx;
+ /* read current value of mode register */
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ val = rx[1]&SCA3000_FREE_FALL_DETECT ? 1 : 0;
+ len += sprintf(buf + len, "%d\n", val);
+ kfree(rx);
+
+ return len;
+error_ret:
+ return ret;
+}
+
+static ssize_t sca3000_query_ring_int(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int val, ret, len = 0;
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ char *rx;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_INT_MASK, &rx, 1);
+ if (ret)
+ goto error_ret;
+
+ val = (rx[1] & this_attr->mask) ? 1 : 0;
+ len += sprintf(buf + len, "%d\n", val);
+ kfree(rx);
+ return len;
+
+error_ret:
+ return ret;
+
+}
+
+static ssize_t sca3000_set_ring_int(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+ long val;
+ int ret;
+ char *rx;
+
+ mutex_lock(&st->lock);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_INT_MASK, &rx, 1);
+ if (ret)
+ goto error_ret;
+
+ if (val)
+ ret = sca3000_write_reg(dev,
+ SCA3000_REG_ADDR_INT_MASK,
+ rx[1] | this_attr->mask);
+ else
+ ret = sca3000_write_reg(dev,
+ SCA3000_REG_ADDR_INT_MASK,
+ rx[1] & ~this_attr->mask);
+
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&st->lock);
+
+ return len;
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+/* assumes that a 'mode' has been specified in the attribute */
+static ssize_t sca3000_set_free_fall_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ long val;
+ int ret;
+ char *rx;
+ uint8_t protect_mask = SCA3000_FREE_FALL_DETECT;
+
+ mutex_lock(&st->lock);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ /* read current value of mode register */
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+
+ /*if off and should be on*/
+ if (val && !(rx[1]&protect_mask))
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ (rx[1] | SCA3000_FREE_FALL_DETECT));
+ /* if on and should be off */
+ else if (!val && (rx[1]&protect_mask))
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ (rx[1]&(~protect_mask)));
+
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&st->lock);
+
+ return len;
+
+error_ret:
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+/* FIXME - can I simplify this? */
+/* assumes that a 'mode' has been specified in the attribute */
+static ssize_t sca3000_set_mo_det(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+ long val;
+ int ret;
+ char *rx;
+ uint8_t protect_mask = 0x03;
+ mutex_lock(&st->lock);
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ /* First read the motion detector config to find out if
+ * this axis is on*/
+ ret = sca3000_read_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ &rx);
+ if (ret)
+ goto error_ret;
+ /* Off and should be on */
+ if (val && !(rx[1]&this_attr->mask)) {
+ ret = sca3000_write_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ rx[1] | this_attr->mask);
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+ st->mo_det_use_count++;
+ } else if (!val && (rx[1]&this_attr->mask)) {
+ ret = sca3000_write_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ rx[1] & ~(this_attr->mask));
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+ st->mo_det_use_count--;
+ } else { /* relies on clean state for device on boot */
+ kfree(rx);
+ return len;
+ }
+ /* read current value of mode register */
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+ if (ret)
+ goto error_ret;
+ /*if off and should be on*/
+ if ((st->mo_det_use_count)
+ && ((rx[1]&protect_mask) != SCA3000_MEAS_MODE_MOT_DET))
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ (rx[1]&(~protect_mask))
+ | SCA3000_MEAS_MODE_MOT_DET);
+
+ /* if on and should be off */
+ /*CARE HERE as need to know what mode to switch
+ to - at the moment assuming back to normal */
+ else if (!(st->mo_det_use_count)
+ && ((rx[1]&protect_mask) == SCA3000_MEAS_MODE_MOT_DET))
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+ (rx[1]&(~protect_mask)));
+
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+
+ mutex_unlock(&st->lock);
+ return len;
+
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+
+
+IIO_EVENT_SH(all, &sca3000_handler_th);
+
+/* Free fall detector related event attribute */
+IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(iio_event_all,
+ sca3000_query_free_fall_mode,
+ sca3000_set_free_fall_mode,
+ 0)
+
+/* Motion detector related event attributes */
+IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_all,
+ sca3000_query_mo_det,
+ sca3000_set_mo_det,
+ SCA3000_MD_CTRL_OR_X);
+
+IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_all,
+ sca3000_query_mo_det,
+ sca3000_set_mo_det,
+ SCA3000_MD_CTRL_OR_Y);
+
+IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_all,
+ sca3000_query_mo_det,
+ sca3000_set_mo_det,
+ SCA3000_MD_CTRL_OR_Z);
+
+/* Hardware ring buffer related event attributes */
+IIO_EVENT_ATTR_RING_50_FULL_SH(iio_event_all,
+ sca3000_query_ring_int,
+ sca3000_set_ring_int,
+ SCA3000_INT_MASK_RING_HALF);
+
+IIO_EVENT_ATTR_RING_75_FULL_SH(iio_event_all,
+ sca3000_query_ring_int,
+ sca3000_set_ring_int,
+ SCA3000_INT_MASK_RING_THREE_QUARTER);
+
+static struct attribute *sca3000_event_attributes[] = {
+ &iio_event_attr_free_fall.dev_attr.attr,
+ &iio_event_attr_x_high.dev_attr.attr,
+ &iio_event_attr_y_high.dev_attr.attr,
+ &iio_event_attr_z_high.dev_attr.attr,
+ &iio_event_attr_ring_50_full.dev_attr.attr,
+ &iio_event_attr_ring_75_full.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group sca3000_event_attribute_group = {
+ .attrs = sca3000_event_attributes,
+};
+
+/* File ops for direct access to the ring buffer */
+int sca3000_ring_open(struct inode *inode, struct file *filp)
+{
+ try_module_get(THIS_MODULE);
+
+ return 0;
+}
+
+int sca3000_ring_release(struct inode *inode, struct file *filp)
+{
+ struct iio_hw_ring_buffer *ring = filp->private_data;
+
+ module_put(THIS_MODULE);
+ clear_bit(IIO_BUSY_BIT_POS, &ring->buf.access_handler.flags);
+
+ return 0;
+}
+
+/* FIXME - ioctl to not bother checking whether there is enough in the ring?
+ * Afterall, if we are responding to an interrupt we have a minimum content
+ * guaranteed*/
+/* FIXME: allow for ring buffering of single / pairs of directions */
+ssize_t sca3000_ring_rip(struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *f_ps)
+{
+
+ /* let us actually rip half the ring!*/
+ struct iio_hw_ring_buffer *hw_ring = filp->private_data;
+ struct sca3000_state *st = hw_ring->private;
+
+ char *rx;
+ int ret;
+ int num_available;
+ int num_read;
+ int bytes_per_sample = 1;
+
+ if (st->bps == 11)
+ bytes_per_sample = 2;
+ /* Check how much data is available!*/
+ ret = sca3000_read_data(st->indio_dev->dev,
+ SCA3000_REG_ADDR_BUF_COUNT,
+ &rx, 1);
+ if (ret)
+ goto error_ret;
+ else
+ num_available = rx[1];
+ /* NB. The num_available is the total number of samples available
+ * i.e. Number of time points * number of channels.
+ */
+ kfree(rx);
+ if (count > num_available*bytes_per_sample)
+ num_read = num_available*bytes_per_sample;
+ else
+ num_read = count - (count % (bytes_per_sample));
+ /* FIXME: Can I do this directly into buf? - not easily because
+ * of the initial byte. Will think on this.*/
+ ret = sca3000_read_data(st->indio_dev->dev,
+ SCA3000_REG_ADDR_RING_OUT,
+ &rx, num_read);
+ if (ret)
+ goto error_ret;
+
+ memcpy(buf, &rx[1], num_read);
+ kfree(rx);
+ return num_read;
+error_ret:
+ return ret;
+}
+
+static const struct file_operations sca3000_hw_ring_fileops = {
+ .read = sca3000_ring_rip,
+ .release = sca3000_ring_release,
+ .open = sca3000_ring_open,
+ .owner = THIS_MODULE,
+};
+
+/* Device uses flash memory to store may of these things and
+ * hence can come up in somewhat unpredictable state. For now reset
+ * everything.
+ */
+
+static int sca3000_clean_setup(struct device *dev)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int ret;
+ char *rx;
+ /* Ensure all interrupts have been acknowledged */
+ ret = sca3000_read_data(dev,
+ SCA3000_REG_ADDR_INT_STATUS, &rx, 1);
+ if (ret)
+ goto error_ret;
+ kfree(rx);
+ mutex_lock(&st->lock);
+ /* Turn off all motion detection channels */
+ ret = sca3000_read_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ &rx);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ rx[1] & SCA3000_MD_CTRL_PROT_MASK);
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+
+ /* Disable ring buffer */
+ sca3000_read_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ &rx);
+ /* Frequency of ring buffer sampling deliberately restricted to make
+ * debugging easier - add control of this later */
+ ret = sca3000_write_ctrl_reg(dev,
+ SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ (rx[1] & SCA3000_OUT_CTRL_PROT_MASK)
+ | SCA3000_OUT_CTRL_BUF_X_EN
+ | SCA3000_OUT_CTRL_BUF_Y_EN
+ | SCA3000_OUT_CTRL_BUF_Z_EN
+ | SCA3000_OUT_CTRL_BUF_DIV_4);
+ kfree(rx);
+
+ if (ret)
+ goto error_ret;
+ /* Enable interrupts, relevant to mode and set up as active low */
+ ret = sca3000_read_data(dev,
+ SCA3000_REG_ADDR_INT_MASK,
+ &rx, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(dev,
+ SCA3000_REG_ADDR_INT_MASK,
+ (rx[1] & SCA3000_INT_MASK_PROT_MASK)
+ | SCA3000_INT_MASK_ACTIVE_LOW);
+ kfree(rx);
+ if (ret)
+ goto error_ret;
+ /* Select normal measurement mode, free fall off, ring off */
+ /* Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5
+ * as that occurs in one of the example on the datasheet */
+ ret = sca3000_read_data(dev,
+ SCA3000_REG_ADDR_MODE,
+ &rx, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(dev,
+ SCA3000_REG_ADDR_MODE,
+ (rx[1] & SCA3000_MODE_PROT_MASK));
+ kfree(rx);
+ st->bps = 11;
+
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int __devinit sca3000_probe(struct spi_device *spi)
+{
+ int ret;
+ const struct sca3000_platform_data *pdata = spi->dev.platform_data;
+ struct sca3000_state *st;
+
+ st = kzalloc(sizeof(struct sca3000_state), GFP_KERNEL);
+ if (st == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ mutex_init(&st->lock);
+ st->info = &sca3000_spi_chip_info_tbl[pdata->variant];
+
+ st->indio_dev = kzalloc(sizeof(struct iio_dev), GFP_KERNEL);
+ if (st->indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_st;
+ }
+
+ st->indio_dev->dev = &spi->dev;
+ st->indio_dev->num_interrupt_lines = 1;
+ st->indio_dev->event_attrs = &sca3000_event_attribute_group;
+ if (st->info->temp_output)
+ st->indio_dev->attrs = &sca3000_attribute_group_with_temp;
+ else
+ st->indio_dev->attrs = &sca3000_attribute_group;
+ st->indio_dev->dev_data = (void *)(st);
+ st->indio_dev->modes = INDIO_DIRECT_MODE | INDIO_RING_HARDWARE_BUFFER;
+
+ ret = iio_device_register(st->indio_dev);
+ if (ret < 0)
+ goto error_free_dev;
+
+ if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+ INIT_WORK(&st->interrupt_handler_ws,
+ sca3000_interrupt_handler_bh);
+ ret = iio_register_interrupt_line(spi->irq,
+ st->indio_dev,
+ 0,
+ IRQF_TRIGGER_FALLING,
+ "sca3000");
+ if (ret)
+ goto error_unregister_dev;
+ /* FIXME: --
+ * THIS IS PROBABLY A COMMON SITUATION - CLEAN UP REGISTRATION*/
+ /* As all interrupts need ack there is only one handler */
+ ret = iio_add_event_to_list(&st->indio_dev
+ ->interrupts[0]->ev_list,
+ iio_event_attr_z_high.listel);
+ if (ret < 0)
+ goto error_unregister_interrupt_line;
+ }
+
+ ret = iio_request_hw_ring_buffer(6,
+ 64,
+ &st->hw_ring,
+ 0,
+ THIS_MODULE,
+ &spi->dev,
+ &sca3000_hw_ring_fileops,
+ st);
+ if (ret)
+ goto error_unregister_interrupt_line;
+ ret = sca3000_clean_setup(&spi->dev);
+ if (ret)
+ goto error_free_hw_ring_buffer;
+ return 0;
+error_free_hw_ring_buffer:
+ iio_free_hw_ring_buffer(st->hw_ring, &spi->dev);
+error_unregister_interrupt_line:
+ if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+ iio_unregister_interrupt_line(st->indio_dev, 0);
+error_unregister_dev:
+ iio_device_unregister(st->indio_dev);
+error_free_dev:
+ kfree(st->indio_dev);
+error_clear_st:
+ kfree(st);
+error_ret:
+ return ret;
+}
+static int sca3000_stop_all_interrupts(struct device *dev)
+{
+ struct sca3000_state *st = sca3000_state_from_dev(dev);
+ int ret;
+ char *rx;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data(dev, SCA3000_REG_ADDR_INT_MASK, &rx, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_INT_MASK,
+ (rx[1] & ~(SCA3000_INT_MASK_RING_THREE_QUARTER
+ | SCA3000_INT_MASK_RING_HALF
+ | SCA3000_INT_MASK_ALL_INTS)));
+error_ret:
+ kfree(rx);
+ return ret;
+
+}
+static int sca3000_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct sca3000_state *st = indio_dev->dev_data;
+ int ret;
+ iio_free_hw_ring_buffer(st->hw_ring, &spi->dev);
+ /* Must ensure no interrupts can be generated after this!*/
+ ret = sca3000_stop_all_interrupts(&spi->dev);
+ if (ret)
+ return ret;
+ if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+ iio_unregister_interrupt_line(st->indio_dev, 0);
+ iio_device_unregister(st->indio_dev);
+
+ kfree(st->indio_dev);
+ kfree(st);
+
+ return 0;
+}
+static struct spi_driver sca3000_driver = {
+ .driver = {
+ .name = "sca3000",
+ .owner = THIS_MODULE,
+ },
+ .probe = sca3000_probe,
+ .remove = __devexit_p(sca3000_remove),
+};
+
+static __init int sca3000_init(void)
+{
+ return spi_register_driver(&sca3000_driver);
+}
+
+static __exit void sca3000_exit(void)
+{
+ spi_unregister_driver(&sca3000_driver);
+}
+
+module_init(sca3000_init);
+module_exit(sca3000_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <[email protected]>");
+MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
+MODULE_LICENSE("GPL v2");
--- a/include/linux/industrialio/sca3000.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/industrialio/sca3000.h 2008-07-23 17:12:00.000000000 +0100
@@ -0,0 +1,22 @@
+/* fixme, start these at zero and rebase chip_info array in driver */
+#define SCA3000_VARIANT_D01 0
+#define SCA3000_VARIANT_D03 1
+#define SCA3000_VARIANT_e02 2
+#define SCA3000_VARIANT_e04 3
+#define SCA3000_VARIANT_e05 4
+#define SCA3000_VARIANT_l01 5
+
+struct sca3000_platform_data {
+ int variant;
+};
+
+/* Conversion function for use with the ring buffer when in 11bit mode */
+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;
+};
Dear Alan,
>> +
>> + xfer.rx_buf = kmalloc(4, GFP_KERNEL);
>> + if (xfer.rx_buf == NULL) {
>> + ret = -ENOMEM;
>> + goto error_free_tx;
>> + }
>
> Do these really need to be two kmallocs
Sorry about that, legacy of a much older version. I definitely didn't clean
this driver up properly (iio core is still a fair way off going anywhere so
I'll admit I didn't didn't check the drivers using it as thoroughly as I
should
have)
>
>> + if (ret) {
>> + dev_err(&st->us->dev, "problem with get x offset");
>> + goto error_free_rx;
>> + }
>> + *val = ((unsigned char *)(xfer.rx_buf))[0];
>> + kfree(xfer.rx_buf);
>> + kfree(xfer.tx_buf);
>> + return ret;
>> +error_free_rx:
>> + kfree(xfer.rx_buf);
>> +error_free_tx:
>> + kfree(xfer.tx_buf);
>> +error_ret:
>> + return ret;
>
> That lot makes no sense. You could just drop through..
Oops.
>> +{
>> + uint8_t val;
>> + int8_t ret;
>
> Kernel types (u8, s8 etc)
To used to userspace programming! I'll fix them.
>
>> + local_rx_buf = kmalloc(4, GFP_KERNEL);
>> + local_tx_buf = kmalloc(4, GFP_KERNEL);
>> +
>> + local_tx_buf[1] = LIS3L02DQ_READ_REG(reg_address);
>
> OOPS on out of memory case
Good point.
>
>
> You seem to have a lot of almost identical routines here. It looks like
> with a few helpers this driver could be vastly shorter.
Again a good point. I'll clean it up and repost.
Thanks for the hints,
--
Jonathan Cameron
On Wed, 23 Jul 2008, Jonathan Cameron wrote:
> The subsystem is now in a functional state with a small set of drivers:
>
> Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
> - Uses a periodic timer to provide ring buffer mode.
> - All reads form these devices are scan modes so direct single element access
> is not provided.
> - Monitor mode on max1363 is not yet supported (need to do a bit debugging of
> the board I have so as to be able to test this).
>
> ST LIS3L02DQ - SPI accelerometer.
> - Uses a datardy interrupt to driver a software ring buffer.
> - Most functionality of this device is supported.
>
> VTI SCA3000 (tested with an e05)
> - Hardware ring buffer.
I'd like to see something done to have the common parts of interfaces of the
same class (e.g. accelerometers) be standard. Like hwmon does with
temp#_input, etc.
Otherwise you made it easier to write drivers, but did nothing to help
userspace to USE the drivers :-)
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
> + xfer.tx_buf = kmalloc(4, GFP_KERNEL);
> + if (xfer.tx_buf == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> +
> + xfer.rx_buf = kmalloc(4, GFP_KERNEL);
> + if (xfer.rx_buf == NULL) {
> + ret = -ENOMEM;
> + goto error_free_tx;
> + }
Do these really need to be two kmallocs
> + if (ret) {
> + dev_err(&st->us->dev, "problem with get x offset");
> + goto error_free_rx;
> + }
> + *val = ((unsigned char *)(xfer.rx_buf))[0];
> + kfree(xfer.rx_buf);
> + kfree(xfer.tx_buf);
> + return ret;
> +error_free_rx:
> + kfree(xfer.rx_buf);
> +error_free_tx:
> + kfree(xfer.tx_buf);
> +error_ret:
> + return ret;
That lot makes no sense. You could just drop through..
> +{
> + uint8_t val;
> + int8_t ret;
Kernel types (u8, s8 etc)
>
> + local_rx_buf = kmalloc(4, GFP_KERNEL);
> + local_tx_buf = kmalloc(4, GFP_KERNEL);
> +
> + local_tx_buf[1] = LIS3L02DQ_READ_REG(reg_address);
OOPS on out of memory case
> + struct spi_transfer xfer = {
> + .tx_buf = NULL,
> + .rx_buf = NULL,
> + .bits_per_word = 16,
> + .len = 2,
> + };
> + int ret;
> +
> + local_tx_buf = kmalloc(4, GFP_KERNEL);
> + if (local_tx_buf == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> + xfer.tx_buf = local_tx_buf;
You seem to have a lot of almost identical routines here. It looks like
with a few helpers this driver could be vastly shorter.
Hi Jonathan,
Sorry, I didn't comment on the first version. So my comments for
this version is below. Note, I only briefly looked in the code,
thus the comments are really superficial.
So... this subsystem is _huge_. And so it needs documentation. Yes,
the code is well commented, much thanks for this. But the
documentation is needed, for example, to answer these simple
questions:
- What userspace interface the subsystem provides (i.e. for use with
userspace tools).
- What kernel interface the subsystem provides (i.e. for use with
in-kernel drivers, for example touchscreens).
- What is iio_ring (as I see it, is one of basic concepts of this
subsystem), I'd like to see description of its basic usage.
- What is struct iio_event_data, what is event's "id"? How it is used
across subsystem?
There is a lot of questions more. Of course, half the questions I'll
answer myself through reading the code... But you really need to
describe basic ideas of 2431-LOC-subsystem.
Something like Documentation/gpio.txt would work.
On the subsystem itself. I would recommend you to split the
industrialio_{core,rtc,ring,ptimer_board_info} into separate modules
(if possible) and into separate patches (for better reviewing).
Few more comments below.
[...]
> +
> +#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) { \
> + (ring_buf)->size = _bytes_per_datum; \
> + (ring_buf)->length = _length; \
> + (ring_buf)->loopcount = 0; \
> + (ring_buf)->shared_ev_pointer.ev_p = 0; \
> + (ring_buf)->shared_ev_pointer.lock = \
> + __SPIN_LOCK_UNLOCKED((ring_buf) \
> + ->shared_ev_pointer->loc); \
> + }
Is it possible to convert this into function?
> +#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) { \
> + INIT_IIO_RING_BUFFER(&(ring)->buf, \
> + _bytes_per_datum, \
> + _length); \
> + (ring)->read_p = 0; \
> + (ring)->write_p = 0; \
> + (ring)->last_written_p = 0; \
> + (ring)->data = kmalloc(_length*(ring)->buf.size, \
> + GFP_KERNEL); \
> + (ring)->use_count = 0; \
> + (ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
> + }
> +
> +#define FREE_IIO_SW_RING_BUFFER(ring) kfree((ring)->data)
Ditto.
[...]
> +/* Vast majority of this is set by the industrialio subsystem on a
> + * call to iio_device_register. */
> +/* TODO Macros to simplify setting the relevant stuff in the driver. */
> +struct iio_dev {
> +/* generic handling data used by ind io */
> + int id;
> +/* device specific data */
> + void *dev_data;
> +
> +/* Modes the drivers supports */
> + int modes; /* Driver Set */
> + int currentmode;
> +/* Direct sysfs related functionality */
> + struct device *sysfs_dev;
> + struct device *dev; /* Driver Set */
> + /* General attributes */
> + const struct attribute_group *attrs;
> +
> +/* Interrupt handling related */
> + struct module *driver_module;
> + int num_interrupt_lines; /* Driver Set */
> +
> + struct iio_interrupt **interrupts;
> +
> +
> + /* Event control attributes */
> + struct attribute_group *event_attrs;
> + /* The character device related elements */
> + struct iio_event_interface *event_interfaces;
> +
> +/* Software Ring Buffer
> + - for now assuming only makes sense to have a single ring */
These comments are really hard to parse. Try to turn off syntax
highlighting (if you use), and see the result. It is really
unreadable.
So, I'd suggest to use kernel doc syntax to describe the fields.
For example, I very like how include/linux/mtd/nand.h is commented.
It is in kernel doc, plus explains what fields are
internal/driver-specific/e.t.c.
> + int ring_dimension;
> + int ring_bytes_per_datum;
> + int ring_length;
> + struct iio_sw_ring_buffer *ring;
> + struct attribute_group *ring_attrs_group;
> + struct iio_ring_attr *ring_attrs;
> + /* enabling / disabling related functions.
> + * post / pre refer to relative to the change of current_mode. */
> + int (*ring_preenable)(struct iio_dev *);
> + int (*ring_postenable)(struct iio_dev *);
> + int (*ring_predisable)(struct iio_dev *);
> + int (*ring_postdisable)(struct iio_dev *);
> + void (*ring_poll_func)(void *private_data);
> + struct iio_periodic *ptimer;
> +
> + /* Device state lock.
> + * Used to prevent simultaneous changes to device state.
> + * In here rather than modules as some ring buffer changes must occur
> + * with this locked.*/
> + struct mutex mlock;
> +
> + /* Name used to allow releasing of the relevant ptimer on exit.
> + * Ideally the ptimers will only be held when the driver is actually
> + * using them, but for now they have one the whole time they are loaded.
> + */
> + const char *ptimer_name;
> +};
[...]
> --- a/include/linux/industrialio_sysfs.h 1970-01-01 01:00:00.000000000 +0100
> +++ b/include/linux/industrialio_sysfs.h 2008-07-23 16:04:18.000000000 +0100
> @@ -0,0 +1,274 @@
> +/* 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.
> + *
> + * General attributes
> + */
> +
> +#ifndef _INDUSTRIAL_IO_SYSFS_H_
> +#define _INDUSTRIAL_IO_SYSFS_H_
> +
> +#include <linux/industrialio.h>
> +
> +
> +struct iio_event_attr {
> + struct device_attribute dev_attr;
> + int mask;
> + struct iio_event_handler_list *listel;
> +};
> +
> +
> +#define to_iio_event_attr(_dev_attr) \
> + container_of(_dev_attr, struct iio_event_attr, dev_attr)
> +
> +
> +struct iio_chrdev_minor_attr {
> + struct device_attribute dev_attr;
> + int minor;
> +};
> +
> +void
> +__init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
> + const char *name,
> + struct module *owner,
> + int id);
> +
> +
> +#define to_iio_chrdev_minor_attr(_dev_attr) \
> + container_of(_dev_attr, struct iio_chrdev_minor_attr, dev_attr);
> +
> +struct iio_dev_attr {
> + struct device_attribute dev_attr;
> + int address;
> +};
> +
> +
> +#define to_iio_dev_attr(_dev_attr) \
> + container_of(_dev_attr, struct iio_dev_attr, dev_attr)
> +
> +/* Some attributes will be hard coded (device dependant) and not require an
> + address, in these cases pass a negative */
> +#define IIO_ATTR(_name, _mode, _show, _store, _addr) \
> + { .dev_attr = __ATTR(_name, _mode, _show, _store), \
> + .address = _addr }
> +
> +#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \
> + struct iio_dev_attr iio_dev_attr_##_name \
> + = IIO_ATTR(_name, _mode, _show, _store, _addr)
> +
> +/* This may get broken down into separate files later */
> +
> +/* Generic attributes of onetype or another */
> +
> +/* Revision number for the device. As the form of this varies greatly from
> + * device to device, no particular form is specified. In most cases this will
> + * only be for information to the user, not to effect functionality etc.
> + */
> +#define IIO_DEV_ATTR_REV(_show) \
> + IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0)
> +
> +/* For devices with internal clocks - and possibly poling later */
> +
> +#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store) \
> + IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0)
> +
> +#define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show) \
> + IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0)
> +
> +/* ADC types of attibute */
> +
> +#define IIO_DEV_ATTR_AVAIL_SCAN_MODES(_show) \
> + IIO_DEVICE_ATTR(available_scan_modes, S_IRUGO, _show, NULL, 0)
> +
> +#define IIO_DEV_ATTR_SCAN_MODE(_mode, _show, _store) \
> + IIO_DEVICE_ATTR(scan_mode, _mode, _show, _store, 0)
> +
> +#define IIO_DEV_ATTR_INPUT(_number, _show) \
> + IIO_DEVICE_ATTR(in##_number, S_IRUGO, _show, NULL, _number)
> +
> +#define IIO_DEV_ATTR_SCAN(_show) \
> + IIO_DEVICE_ATTR(scan, S_IRUGO, _show, NULL, 0);
> +/* Accelerometer types of attribute */
> +
> +#define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(x_offset, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(y_offset, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(z_offset, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(x_gain, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(y_gain, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
> +
Why such a generic subsystem should be aware of X/Y/Z? ADC can measure
strength of wind, for example. But in fact it measures voltage and/or
current. Yes, it is convenient to tell user what exactly chip is
supposed to measure, but it is chip (and thus driver) specific,
subsystem should only provides means to expose that information, but
it should not be aware of what exactly we're measuring. At least that is
how I imagine the ADC subsystem.
Thanks,
--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2
On Wednesday 23 July 2008, Jonathan Cameron wrote:
> * Software ring buffer support to allow semi regular capture of data form the
> ? device. ?Typically this will be driven from either datardy events, or from
> ? a periodic timer interrupt (to this end a very simple wrapper for periodic
> ? RTC's is included. This will move to more generic timer interfaces as and when
> ? they become available. ?For now available rtc's must be registered with the
> ? subsystem via the industrialio_register_ptimer function form within a board
> ? init.
Could you elaborate a bit on why you'd want hardware supported
periodic timers, rather than either an hrtimer or a timer?
Maybe provide some real-world scenarios where those software
abstractions are inappropriate.
I can imagine numerous reasons, but those might not be yours...
- Dave
On Wed, Jul 23, 2008 at 06:00:29PM +0100, Jonathan Cameron wrote:
> Dear All,
>
> The need for an industrialio subsystem was discussed in
> http://lkml.org/lkml/2008/5/20/135
The name is really bad, this sounds like something for doing large
scale industrial process control.
> Firstly thanks to all the people who have contributed to the discussion
> of this in the past.
>
> In brief the intention is provide a kernel subsystem directed towards the
> handling on sensors (and later related output devices) such as ADC's,
> accelerometers and many others.
We've already got an perfectly good hwmon framework, do we really need
to do this again?
> Key features of the subsystem include:
>
> * Provision of sysfs access for direct reading from devices (similar to hwmon
> but without the buffering / update rate restrictions)
>
> * Provision of chrdevs through which events may be passed to userspace in a
> similar fashion to the input subsystem. These events may be anything from
> hardware thresholds set on the sensor itself to sw / hw ring buffer event
> notifications (50% full etc).
>
> * Provision of access via chrdevs to hardware ring buffers on devices that
> provide them.
>
> * Software ring buffer support to allow semi regular capture of data form the
> device. Typically this will be driven from either datardy events, or from
> a periodic timer interrupt (to this end a very simple wrapper for periodic
> RTC's is included. This will move to more generic timer interfaces as and when
> they become available. For now available rtc's must be registered with the
> subsystem via the industrialio_register_ptimer function form within a board
> init.
>
> * A set of sample drivers illustrating the main 'classes' of device. By classes
> I really mean devices that are interfaced with in a similar way.
>
> The subsystem is now in a functional state with a small set of drivers:
>
> Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
> - Uses a periodic timer to provide ring buffer mode.
> - All reads form these devices are scan modes so direct single element access
> is not provided.
> - Monitor mode on max1363 is not yet supported (need to do a bit debugging of
> the board I have so as to be able to test this).
>
> ST LIS3L02DQ - SPI accelerometer.
> - Uses a datardy interrupt to driver a software ring buffer.
> - Most functionality of this device is supported.
>
> VTI SCA3000 (tested with an e05)
> - Hardware ring buffer.
>
> More drivers in preparation.
>
> Next focus will be on cleaning up / implementing a more generic timer framework
> and allowing the system to partly run if not all dependencies are met
> (particularly availability of timers).
>
> An initial set of patches will be attached to this thread shortly.
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
On Wed, Jul 23, 2008 at 06:00:29PM +0100, Jonathan Cameron wrote:
> Dear All,
>
> The need for an industrialio subsystem was discussed in
> http://lkml.org/lkml/2008/5/20/135
>
> Firstly thanks to all the people who have contributed to the discussion
> of this in the past.
>
> In brief the intention is provide a kernel subsystem directed towards the
> handling on sensors (and later related output devices) such as ADC's,
> accelerometers and many others.
Thinking about this, basically we have an event buffer and management
system that could live in drivers/event. It manages the event sources
and buffering, including tagging the data and buffering it ready for
consumption.
One thing I'd like to see is that the reading application should be
able to choose to have events aggregated or not, so that if a few
events turn up very fast it can choose to take them as one single
item.
> Key features of the subsystem include:
>
> * Provision of sysfs access for direct reading from devices (similar to hwmon
> but without the buffering / update rate restrictions)
Having an update rate and buffering is possibly useful, given that if you
have say a number of ADC inputs but only one converter, then it is possible
that there will be some form of resource starvation.
> * Provision of chrdevs through which events may be passed to userspace in a
> similar fashion to the input subsystem. These events may be anything from
> hardware thresholds set on the sensor itself to sw / hw ring buffer event
> notifications (50% full etc).
>
> * Provision of access via chrdevs to hardware ring buffers on devices that
> provide them.
>
> * Software ring buffer support to allow semi regular capture of data form the
> device. Typically this will be driven from either datardy events, or from
> a periodic timer interrupt (to this end a very simple wrapper for periodic
> RTC's is included. This will move to more generic timer interfaces as and when
> they become available. For now available rtc's must be registered with the
> subsystem via the industrialio_register_ptimer function form within a board
> init.
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
On Wed, Jul 23, 2008 at 06:08:53PM +0100, Jonathan Cameron wrote:
>
> From: Jonathan Cameron <[email protected]>
>
> Industrialio subsystem core patch. This subsystem is intended to support the use
> of (initially) sensors within linux for the purposes of data capture and its use
> within control applications. The intention is to provide consistent interfaces
> (where it makes sense) with device control occuring through sysfs interfaces and
> provision of events to userspace via chrdevs. Currently software ring buffers
> are available if the sensor provides a data ready signal or a periodic rtc is
> available (and registered with the subsystem in board init code).
>
> Signed-off-by: Jonathan Cameron <[email protected]>
> ---
> The periodic timer code is a temporary stop gap until a more generic subsystem
> becomes available.
>
> The intention of publishing these patches is to generate feedback both at the
> high level of suggestions / comments on the general approach taken by the
> subsystem as a whole and at the low level of implementation details.
>
> Areas that in my view need attention are the software ring buffer (particularly
> careful analysis of corner cases and efficiency of it as a storage method).
> Although none of the current drivers is capable of filling it in interrupt
> context, I can envision some hardware may need to and this will clearly require
> some changes. The overall layout of the interfaces (and indeed the code) needs
> some work, particularly with a view to cutting down the dependancies if a
> given driver doesn't need all of the systems functionality.
>
> Additional test drivers will obviously assist in working out many of these
> issues and I hope to add several more over the comming weeks.
>
> My sincerest thanks goes to anyone who takes the time to read through or test
> this patch set.
>
> drivers/Kconfig | 3
> drivers/Makefile | 1
> drivers/industrialio/Kconfig | 19
> drivers/industrialio/Makefile | 7
> drivers/industrialio/industrialio-core.c | 787 ++++++++++++++++++
> drivers/industrialio/industrialio-ring.c | 770 +++++++++++++++++
> drivers/industrialio/industrialio-rtc.c | 134 +++
> drivers/industrialio/industrialio_ptimer_board_info.c | 44 +
> include/linux/industrialio.h | 374 ++++++++
> include/linux/industrialio_ptimer.h | 18
> include/linux/industrialio_sysfs.h | 274 ++++++
> 11 files changed, 2431 insertions(+)
>
> ------ a/drivers/Kconfig 2008-07-13 22:51:29.000000000 +0100
> +++ b/drivers/Kconfig 2008-07-14 17:26:34.000000000 +0100
> @@ -101,4 +101,7 @@ source "drivers/auxdisplay/Kconfig"
> source "drivers/uio/Kconfig"
>
> source "drivers/xen/Kconfig"
> +
> +source "drivers/industrialio/Kconfig"
> endmenu
> +
> --- a/drivers/Makefile 2008-07-13 22:51:29.000000000 +0100
> +++ b/drivers/Makefile 2008-07-14 17:26:34.000000000 +0100
> @@ -62,6 +62,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/
> --- a/drivers/industrialio/Kconfig 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/industrialio/Kconfig 2008-07-23 15:44:45.000000000 +0100
> @@ -0,0 +1,19 @@
> +#
> +# Industrial I/O subsytem configuration
> +#
> +
> +menuconfig INDUSTRIALIO
> + tristate "Industrial I/O support"
> + ---help---
> + The industrial IO subsystem provides a unified framework for drivers
> + for many different types of embedded sensors using a number of
> + different phyiscal interfaces (i2c, spi etc). See
> + Documentation/industrialio for more information.
> +
> +if INDUSTRIALIO
> +
> +config INDUSTRIALIO_PTIMER_BOARDINFO
> + boolean
> + default y
> +
> +endif
> --- a/drivers/industrialio/Makefile 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/industrialio/Makefile 2008-07-23 12:05:27.000000000 +0100
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for the industrial I/O core.
> +#
> +industrialio-objs := industrialio-core.o industrialio-ring.o industrialio-rtc.o
> +
> +obj-$(CONFIG_INDUSTRIALIO) += industrialio.o
> +obj-$(CONFIG_INDUSTRIALIO_PTIMER_BOARDINFO) += industrialio_ptimer_board_info.o
> --- a/include/linux/industrialio.h 1970-01-01 01:00:00.000000000 +0100
> +++ b/include/linux/industrialio.h 2008-07-23 15:20:19.000000000 +0100
> @@ -0,0 +1,374 @@
> +/* 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.
> + */
> +
> +#ifndef _INDUSTRIAL_IO_H_
> +#define _INDUSTRIAL_IO_H_
> +
> +#include <linux/device.h>
> +#include <linux/industrialio_sysfs.h>
> +
> +/* TODO LIST */
> +/* Static device specific elements (conversion factors etc)
> + should be exported via sysfs
> + Break up this header - some drivers only want a fraction of this.
> +*/
> +
> +
> +/* Event interface flags */
> +#define IIO_BUSY_BIT_POS 1
> +
> +
> +struct iio_handler {
> + const struct file_operations *fops;
> + int id;
> + unsigned long flags;
> + void *private;
> +};
> +
> +/* The actual event being pushed ot userspace */
> +struct iio_event_data {
> + int id;
> + s64 timestamp;
> +};
So is this an header for an event? Where is the data in this, this
is confusing... Also, should we have a framework to produce an
key/pair data, so that an single event can export multiple values
from one event...
ie:
struct event_header {
unsigned int id;
unsigned int nr_data; /* number of event_data after */
s64 timestamp;
struct event {
struct event_header header;
struct event_data data[0];
};
> +
> +struct iio_detected_event_list {
> + struct list_head list;
> + struct iio_event_data ev;
> + /* Part of shared event handling - (typicaly ring buffers) */
> + struct iio_shared_ev_pointer *shared_pointer;
> +};
the naming of these structures is rather long.
> +/* Requires high resolution timers */
> +/* TODO - provide alternative if not available? */
> +static inline s64 iio_get_time_ns(void)
> +{
> + struct timespec ts;
> + ktime_get_ts(&ts);
> + return timespec_to_ns(&ts);
> +}
do we really need something that accurate? we should at-least have
the option to remove the timestamp or choose something of lower
accuracy?
> +struct iio_dev;
> +
> +/* Each device has one of these per interrupt */
> +struct iio_event_handler_list {
> + struct list_head list;
> + int (*handler)(struct iio_dev *dev_io, int index, s64 timestamp,
> + int no_test);
> + /* This element may be shared */
> + int refcount;
> +};
> +
> +/* Wraps adding to lists and does reference counting to allowed shared
> + * handlers.
> + */
> +int iio_add_event_to_list(struct iio_event_handler_list *list,
> + struct iio_event_handler_list *el);
> +
> +int iio_remove_event_from_list(struct iio_event_handler_list *el);
> +
> +struct iio_sw_ring_buffer;
> +struct iio_hw_ring_buffer;
> +
> +#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) { \
> + (ring_buf)->size = _bytes_per_datum; \
> + (ring_buf)->length = _length; \
> + (ring_buf)->loopcount = 0; \
> + (ring_buf)->shared_ev_pointer.ev_p = 0; \
> + (ring_buf)->shared_ev_pointer.lock = \
> + __SPIN_LOCK_UNLOCKED((ring_buf) \
> + ->shared_ev_pointer->loc); \
> + }
> +
> +#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) { \
> + INIT_IIO_RING_BUFFER(&(ring)->buf, \
> + _bytes_per_datum, \
> + _length); \
> + (ring)->read_p = 0; \
> + (ring)->write_p = 0; \
> + (ring)->last_written_p = 0; \
> + (ring)->data = kmalloc(_length*(ring)->buf.size, \
> + GFP_KERNEL); \
> + (ring)->use_count = 0; \
> + (ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
> + }
these should be inlined functions.
> +#define FREE_IIO_SW_RING_BUFFER(ring) kfree((ring)->data)
> +
> +
> +
> +int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
> + unsigned char *data,
> + s64 timestamp);
> +
> +/* Edge cases :
> + 1) data at last_p is no longer valid - requires complete wrap around.
> + To detect, loop count has changed - if only by 1 then problem only
> + if current_lastp is equal to or greater than copy made at start.
> + If we have wrapped an entire int in this time (loopcount) then
> + something very very weird has occured!
> +*/
> +int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
> + unsigned char *data);
> +
> +/* Up to the drivers to mark the ring whenever it must not change size
> + * and unmark when it may without problems */
> +void iio_mark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring);
> +
> +void iio_unmark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring);
> +
> +int iio_request_sw_ring_buffer(int bytes_per_datum,
> + int length,
> + struct iio_sw_ring_buffer **ring,
> + int id,
> + struct module *owner,
> + struct device *dev);
> +
how about tying this to a driver, where you already know the owner
and the dev?
> +int iio_request_update_sw_ring_buffer(struct iio_dev *dev_info, int id);
> +
> +void iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
> + struct device *dev);
> +
> +int iio_request_hw_ring_buffer(int bytes_per_datum,
> + int length,
> + struct iio_hw_ring_buffer **ring,
> + int id,
> + struct module *owner,
> + struct device *dev,
> + const struct file_operations *fops,
> + void *private);
> +
> +void iio_free_hw_ring_buffer(struct iio_hw_ring_buffer *ring,
> + struct device *dev);
> +
> +/* Device operating modes */
> +#define INDIO_DIRECT_MODE 0x01
> +#define INDIO_RING_POLLED 0x02
> +#define INDIO_RING_DATA_RDY 0x04
> +#define INDIO_RING_HARDWARE_BUFFER 0x08
> +
> +struct iio_event_interface {
> + struct iio_handler handler;
> + wait_queue_head_t wait;
> + struct mutex event_list_lock;
> + struct iio_detected_event_list det_events;
> + int max_events;
> + int current_events;
> + /* Integer id, used to differentiate this one form any others */
> + int id;
> + struct iio_chrdev_minor_attr attr;
> + struct module *owner;
> + void *private;
> + /* used to store name for associated sysfs file */
> + char _name[20];
> +};
> +
> +struct iio_shared_ev_pointer {
> + struct iio_detected_event_list *ev_p;
> + spinlock_t lock;
> +};
> +
> +/* A general ring buffer structure
> + * Intended to be completely lock free as we always want fills from
> + * the interrupt handler to not have to wait. This obviously increases
> + * the possible time required to read from the buffer. */
> +struct iio_ring_buffer {
> + /* Number of datums */
> + int length;
> + /* length of single datum - including timestamp if there */
> + int size;
> + int loopcount;
> + /* accessing the ring buffer */
> + char *access_minor_name;
> + struct iio_chrdev_minor_attr access_minor_attr;
> + struct iio_handler access_handler;
> + /* events triggered by the ring buffer */
> + char *event_minor_name;
> + struct iio_event_interface ev_int;
> + /* a fully shared output event ? wtf?*/
> + struct iio_shared_ev_pointer shared_ev_pointer;
> +};
> +
> +int iio_put_ring_event(struct iio_ring_buffer *ring_buf,
> + int event_code,
> + s64 timestamp);
> +
> +int iio_put_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
> + int event_code,
> + s64 timestamp);
> +
> +struct iio_sw_ring_buffer {
> + struct iio_ring_buffer buf;
> + unsigned char *data;
> + unsigned char *read_p;
> + unsigned char *write_p;
> + unsigned char *last_written_p;
> + /* used to act as a point at which to signal an event */
> + unsigned char *half_p;
> + int use_count;
> + int update_needed;
> + spinlock_t use_lock;
> +};
> +
> +struct iio_hw_ring_buffer {
> + struct iio_ring_buffer buf;
> + void *private;
> +};
> +
> +/* Vast majority of this is set by the industrialio subsystem on a
> + * call to iio_device_register. */
> +/* TODO Macros to simplify setting the relevant stuff in the driver. */
> +struct iio_dev {
> +/* generic handling data used by ind io */
> + int id;
> +/* device specific data */
> + void *dev_data;
> +
> +/* Modes the drivers supports */
> + int modes; /* Driver Set */
> + int currentmode;
> +/* Direct sysfs related functionality */
> + struct device *sysfs_dev;
> + struct device *dev; /* Driver Set */
> + /* General attributes */
> + const struct attribute_group *attrs;
> +
> +/* Interrupt handling related */
> + struct module *driver_module;
> + int num_interrupt_lines; /* Driver Set */
> +
> + struct iio_interrupt **interrupts;
> +
> +
> + /* Event control attributes */
> + struct attribute_group *event_attrs;
> + /* The character device related elements */
> + struct iio_event_interface *event_interfaces;
> +
> +/* Software Ring Buffer
> + - for now assuming only makes sense to have a single ring */
> + int ring_dimension;
> + int ring_bytes_per_datum;
> + int ring_length;
> + struct iio_sw_ring_buffer *ring;
> + struct attribute_group *ring_attrs_group;
> + struct iio_ring_attr *ring_attrs;
> + /* enabling / disabling related functions.
> + * post / pre refer to relative to the change of current_mode. */
> + int (*ring_preenable)(struct iio_dev *);
> + int (*ring_postenable)(struct iio_dev *);
> + int (*ring_predisable)(struct iio_dev *);
> + int (*ring_postdisable)(struct iio_dev *);
> + void (*ring_poll_func)(void *private_data);
> + struct iio_periodic *ptimer;
> +
> + /* Device state lock.
> + * Used to prevent simultaneous changes to device state.
> + * In here rather than modules as some ring buffer changes must occur
> + * with this locked.*/
> + struct mutex mlock;
> +
> + /* Name used to allow releasing of the relevant ptimer on exit.
> + * Ideally the ptimers will only be held when the driver is actually
> + * using them, but for now they have one the whole time they are loaded.
> + */
> + const char *ptimer_name;
> +};
> +
> +int iio_device_register(struct iio_dev *dev_info);
> +void iio_device_unregister(struct iio_dev *dev_info);
> +
> +/* Wrapper class used to allow easy specification of different line numbers */
> +struct iio_interrupt {
> + struct iio_dev *dev_info;
> + int line_number;
> + int irq;
> + struct iio_event_handler_list ev_list;
> +};
> +
> +irqreturn_t iio_interrupt_handler(int irq, void *_int_info);
> +
> +int iio_register_interrupt_line(unsigned int irq,
> + struct iio_dev *dev_info,
> + int line_number,
> + unsigned long type,
> + const char *name);
> +
> +void iio_unregister_interrupt_line(struct iio_dev *dev_info,
> + int line_number);
> +
> +
> +/* Used to try inserting an event into the list for userspace reading via
> + * chrdev */
> +int iio_put_event(struct iio_dev *dev_info,
> + int ev_line,
> + int ev_code,
> + s64 timestamp);
> +
> +struct iio_work_cont {
> + struct work_struct ws;
> + struct work_struct ws_nocheck;
> + int address;
> + int mask;
> + void *st;
> +};
> +#define INIT_IIO_WORK_CONT(cont, _checkfunc, _nocheckfunc, _add, _mask, _st)\
> + do { \
> + INIT_WORK(&(cont)->ws, _checkfunc); \
> + INIT_WORK(&(cont)->ws_nocheck, _nocheckfunc); \
> + (cont)->address = _add; \
> + (cont)->mask = _mask; \
> + (cont)->st = _st; \
> + } while (0)
more nasty macros.
> +/* Ring buffer related */
> +int iio_device_register_sw_ring(struct iio_dev *dev_info, int id);
> +void iio_device_unregister_sw_ring(struct iio_dev *dev_info);
> +
> +int __iio_put_event(struct iio_event_interface *ev_int,
> + int ev_code,
> + s64 timestamp,
> + struct iio_shared_ev_pointer*
> + shared_pointer_p);
> +void __iio_change_event(struct iio_detected_event_list *ev,
> + int ev_code,
> + s64 timestamp);
> +
> +int iio_setup_ev_int(struct iio_event_interface *ev_int,
> + const char *name,
> + struct module *owner,
> + struct device *dev);
> +
> +void iio_free_ev_int(struct iio_event_interface *ev_int, struct device *dev);
> +
> +int iio_allocate_chrdev(struct iio_handler *handler);
> +void iio_deallocate_chrdev(struct iio_handler *handler);
> +
> +ssize_t iio_show_attr_minor(struct device *dev,
> + struct device_attribute *attr,
> + char *buf);
> +
> +/* For now this is on the type of periodic timer used*/
> +struct iio_periodic {
> + struct rtc_device *rtc;
> + int frequency;
> + struct rtc_task task;
> +};
> +
> +int iio_ptimer_request_periodic_timer(char *name, struct iio_dev *indio_dev);
> +void iio_ptimer_unrequest_periodic_timer(struct iio_dev *indio_dev);
> +int iio_ptimer_set_freq(struct iio_periodic *ptimer, unsigned frequency);
> +int iio_ptimer_irq_set_state(struct iio_dev *indio_dev, bool state);
> +
> +/* Board registration is handled by contents of
> + * industrialio_ptimer_board_info.c
> + */
> +extern struct mutex industrialio_ptimer_board_lock;
> +extern struct list_head industrialio_ptimer_board_info_list;
> +#endif /* _INDUSTRIAL_IO_H_ */
> --- a/include/linux/industrialio_ptimer.h 1970-01-01 01:00:00.000000000 +0100
> +++ b/include/linux/industrialio_ptimer.h 2008-07-23 15:41:29.000000000 +0100
> @@ -0,0 +1,18 @@
> +#ifndef _INDUSTRIALIO_PTIMER_H_
> +#define _INDUSTRIALIO_PTIMER_H_
> +
> +#define IIO_PTIMER_NAME_SIZE 10
> +
> +
> +struct ptimer_info {
> + char name[IIO_PTIMER_NAME_SIZE];
> +};
> +struct ptimer_info_listel {
> + struct list_head list;
> + bool inuse;
> + struct ptimer_info info;
> +};
> +
> +extern int
> +industrialio_register_ptimer(struct ptimer_info const *info, unsigned n);
> +#endif
> --- a/include/linux/industrialio_sysfs.h 1970-01-01 01:00:00.000000000 +0100
> +++ b/include/linux/industrialio_sysfs.h 2008-07-23 16:04:18.000000000 +0100
> @@ -0,0 +1,274 @@
> +/* 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.
> + *
> + * General attributes
> + */
> +
> +#ifndef _INDUSTRIAL_IO_SYSFS_H_
> +#define _INDUSTRIAL_IO_SYSFS_H_
> +
> +#include <linux/industrialio.h>
> +
> +
> +struct iio_event_attr {
> + struct device_attribute dev_attr;
> + int mask;
> + struct iio_event_handler_list *listel;
> +};
> +
> +
> +#define to_iio_event_attr(_dev_attr) \
> + container_of(_dev_attr, struct iio_event_attr, dev_attr)
> +
> +
> +struct iio_chrdev_minor_attr {
> + struct device_attribute dev_attr;
> + int minor;
> +};
> +
> +void
> +__init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
> + const char *name,
> + struct module *owner,
> + int id);
> +
> +
> +#define to_iio_chrdev_minor_attr(_dev_attr) \
> + container_of(_dev_attr, struct iio_chrdev_minor_attr, dev_attr);
> +
> +struct iio_dev_attr {
> + struct device_attribute dev_attr;
> + int address;
> +};
> +
> +
> +#define to_iio_dev_attr(_dev_attr) \
> + container_of(_dev_attr, struct iio_dev_attr, dev_attr)
> +
> +/* Some attributes will be hard coded (device dependant) and not require an
> + address, in these cases pass a negative */
> +#define IIO_ATTR(_name, _mode, _show, _store, _addr) \
> + { .dev_attr = __ATTR(_name, _mode, _show, _store), \
> + .address = _addr }
> +
> +#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \
> + struct iio_dev_attr iio_dev_attr_##_name \
> + = IIO_ATTR(_name, _mode, _show, _store, _addr)
> +
> +/* This may get broken down into separate files later */
> +
> +/* Generic attributes of onetype or another */
> +
> +/* Revision number for the device. As the form of this varies greatly from
> + * device to device, no particular form is specified. In most cases this will
> + * only be for information to the user, not to effect functionality etc.
> + */
> +#define IIO_DEV_ATTR_REV(_show) \
> + IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0)
> +
> +/* For devices with internal clocks - and possibly poling later */
> +
> +#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store) \
> + IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0)
> +
> +#define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show) \
> + IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0)
> +
> +/* ADC types of attibute */
> +
> +#define IIO_DEV_ATTR_AVAIL_SCAN_MODES(_show) \
> + IIO_DEVICE_ATTR(available_scan_modes, S_IRUGO, _show, NULL, 0)
> +
> +#define IIO_DEV_ATTR_SCAN_MODE(_mode, _show, _store) \
> + IIO_DEVICE_ATTR(scan_mode, _mode, _show, _store, 0)
> +
> +#define IIO_DEV_ATTR_INPUT(_number, _show) \
> + IIO_DEVICE_ATTR(in##_number, S_IRUGO, _show, NULL, _number)
> +
> +#define IIO_DEV_ATTR_SCAN(_show) \
> + IIO_DEVICE_ATTR(scan, S_IRUGO, _show, NULL, 0);
> +/* Accelerometer types of attribute */
> +
> +#define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(x_offset, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(y_offset, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(z_offset, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(x_gain, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(y_gain, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
> +
> +
> +/* The actual device readings are always going to be read only */
> +#define IIO_DEV_ATTR_ACCEL_X(_show, _addr) \
> + IIO_DEVICE_ATTR(x, S_IRUGO, _show, NULL, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Y(_show, _addr) \
> + IIO_DEVICE_ATTR(y, S_IRUGO, _show, NULL, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_Z(_show, _addr) \
> + IIO_DEVICE_ATTR(z, S_IRUGO, _show, NULL, _addr)
> +
> +#define IIO_DEV_ATTR_TEMP(_show) \
> + IIO_DEVICE_ATTR(temp, S_IRUGO, _show, NULL, 0)
> +/* Thresholds are somewhat chip dependent - may need quite a few defs here */
> +/* For unified thesholds (shared across all directions */
> +#define IIO_DEV_ATTR_ACCEL_THRESH(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(thresh, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_THRESH_X(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(thresh_x, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_THRESH_Y(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(thresh_y, _mode, _show, _store, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_THRESH_Z(_mode, _show, _store, _addr) \
> + IIO_DEVICE_ATTR(thresh_z, _mode, _show, _store, _addr)
> +
> +/* This is an event attr in some case and a dev in others - FIX*/
> +#define IIO_DEV_ATTR_SW_RING_ENABLE(_show, _store) \
> + IIO_DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
> +
> +/* Hardware ring buffer related attributes */
> +#define IIO_DEV_ATTR_HW_RING_ENABLE(_show, _store) \
> + IIO_DEVICE_ATTR(hw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
> +
> +#define IIO_DEV_ATTR_RING_BPS(_mode, _show, _store) \
> + IIO_DEVICE_ATTR(ring_bps, _mode, _show, _store, 0)
> +
> +/* Bits per sample */
> +#define IIO_DEV_ATTR_RING_BPS_AVAILABLE(_show) \
> + IIO_DEVICE_ATTR(ring_bps_available, S_IRUGO, _show, NULL, 0)
> +
> +/* Events that the device may generate */
> +
> +#define IIO_EVENT_SH(_name, _handler) \
> + static struct iio_event_handler_list \
> + iio_event_##_name = { \
> + .handler = _handler, \
> + .refcount = 0, \
> + };
> +#define IIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask) \
> + static struct iio_event_attr \
> + iio_event_attr_##_name \
> + = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store),\
> + .mask = _mask,\
> + .listel = &_ev_list };
> +
> +/*FIXME use the above to define this */
> +#define IIO_EVENT_ATTR(_name, _show, _store, _mask, _handler) \
> + static struct iio_event_handler_list \
> + iio_event_##_name = { \
> + .handler = _handler, \
> + }; \
> + static struct \
> + iio_event_attr \
> + iio_event_attr_##_name \
> + = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store), \
> + .mask = _mask, \
> + .listel = &iio_event_##_name }; \
> +/*FIXME, add line number to the above?*/
> +
> +/* In most of these cases, this actually corresponds to something with a
> + value attached */
> +
> +/* For some devices you can select whether all conditions or any condition
> + must be met for interrupt generation */
> +#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_CODE_DATA_RDY 100
> +
> +/* Threshold pass events */
> +#define IIO_EVENT_ATTR_ACCEL_X_HIGH(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(x_high, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_CODE_ACCEL_X_HIGH 1
> +
> +/* Shared handler version */
> +#define IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(_evlist, _show, _store, _mask)\
> + IIO_EVENT_ATTR_SH(x_high, _evlist, _show, _store, _mask)
> +
> +
> +#define IIO_EVENT_ATTR_ACCEL_Y_HIGH(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(y_high, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(_evlist, _show, _store, _mask)\
> + IIO_EVENT_ATTR_SH(y_high, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_CODE_ACCEL_Y_HIGH 2
> +
> +#define IIO_EVENT_ATTR_ACCEL_Z_HIGH(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(z_high, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(_evlist, _show, _store, _mask)\
> + IIO_EVENT_ATTR_SH(z_high, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_CODE_ACCEL_Z_HIGH 3
> +
> +#define IIO_EVENT_ATTR_ACCEL_X_LOW(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(x_low, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_ACCEL_X_LOW_SH(_evlist, _show, _store, _mask)\
> + IIO_EVENT_ATTR_SH(x_low, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_CODE_ACCEL_X_LOW 4
> +
> +#define IIO_EVENT_ATTR_ACCEL_Y_LOW(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(y_low, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(_evlist, _show, _store, _mask)\
> + IIO_EVENT_ATTR_SH(y_low, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_CODE_ACCEL_Y_LOW 5
> +
> +#define IIO_EVENT_ATTR_ACCEL_Z_LOW(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(z_low, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(_evlist, _show, _store, _mask) \
> + IIO_EVENT_ATTR_SH(z_low, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_CODE_ACCEL_Z_LOW 6
> +
> +#define IIO_EVENT_ATTR_FREE_FALL_DETECT(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(free_fall, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(_evlist, _show, _store, _mask) \
> + IIO_EVENT_ATTR_SH(free_fall, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_CODE_FREE_FALL 7
> +
> +/* These may be software or hardware events depending on type of ring buffer */
> +
> +#define IIO_EVENT_ATTR_RING_50_FULL(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(ring_50_full, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_ATTR_RING_50_FULL_SH(_evlist, _show, _store, _mask) \
> + IIO_EVENT_ATTR_SH(ring_50_full, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_ATTR_RING_75_FULL_SH(_evlist, _show, _store, _mask) \
> + IIO_EVENT_ATTR_SH(ring_75_full, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_ATTR_SW_RING_ENABLE(_show, _store, _mask, _handler) \
> + IIO_EVENT_ATTR(sw_ring_enable, _show, _store, _mask, _handler)
> +
> +#define IIO_EVENT_CODE_RING_50_FULL 100
> +#define IIO_EVENT_CODE_RING_75_FULL 101
> +#define IIO_EVENT_CODE_RING_100_FULL 102
> +/* HOW TO HANDLE COMPOSITE EVENTS? */
> +
> +#endif /* _INDUSTRIAL_IO_SYSFS_H_ */
> --- a/drivers/industrialio/industrialio_ptimer_board_info.c 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/industrialio/industrialio_ptimer_board_info.c 2008-07-23 14:16:28.000000000 +0100
> @@ -0,0 +1,44 @@
> +/* The industrial I/O periodic timer registration code
> + *
> + * 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.
> + *
> + */
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/industrialio_ptimer.h>
> +
> +LIST_HEAD(industrialio_ptimer_board_info_list);
> +EXPORT_SYMBOL_GPL(industrialio_ptimer_board_info_list);
> +
> +DEFINE_MUTEX(industrialio_ptimer_board_lock);
> +EXPORT_SYMBOL_GPL(industrialio_ptimer_board_lock);
> +
> +
> +int __init
> +industrialio_register_ptimer(struct ptimer_info const *info, unsigned n)
> +{
> + int i;
> + struct ptimer_info_listel *pi;
> +
> + mutex_lock(&industrialio_ptimer_board_lock);
> + for (i = 0; i < n; i++) {
> + pi = kzalloc(sizeof(*pi), GFP_KERNEL);
> + if (!pi) {
> + mutex_unlock(&industrialio_ptimer_board_lock);
> + return -ENOMEM;
> + }
> + strncpy(pi->info.name, info[i].name, IIO_PTIMER_NAME_SIZE);
> + list_add_tail(&pi->list, &industrialio_ptimer_board_info_list);
> + }
> + mutex_unlock(&industrialio_ptimer_board_lock);
> +
> + return 0;
> +}
> --- a/drivers/industrialio/industrialio-core.c 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/industrialio/industrialio-core.c 2008-07-23 15:07:21.000000000 +0100
> @@ -0,0 +1,787 @@
> +/* 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/rtc.h>
> +
> +#include <linux/industrialio.h>
> +#include <linux/industrialio_ptimer.h>
> +
> +MODULE_AUTHOR("Jonathan Cameron <[email protected]>");
> +MODULE_DESCRIPTION("Industrial I/O core");
> +MODULE_LICENSE("GPL");
> +
> +#define IIO_ID_PREFIX "industrialio"
> +#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
> +#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 used to maintain internal state about industrialio.
> + * This will be used to handle the character device accesses
> + * and redirect them to the relevant driver.
> + * Will reduce this to the included table if nothing else comes
> + * up that should go in here!
> + */
> +struct __iio_state {
> + /* All initially set to NULL in init */
> + struct iio_handler *fhs[256];
> +};
> +
> +static struct __iio_state iio_state;
> +static DEFINE_SPINLOCK(iio_state_lock);
> +
> +/* Used to escalate shared event.
> + * Currently this is only used with ring buffer events.
> + */
> +void __iio_change_event(struct iio_detected_event_list *ev,
> + int ev_code,
> + s64 timestamp)
> +{
> + ev->ev.id = ev_code;
> + ev->ev.timestamp = timestamp;
> +}
> +
> +/* Used both in the interrupt line put events and the ring buffer ones */
> + int
> +__iio_put_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;
> +
> + /* 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);
> + }
> +
> + return 0;
> +error_ret:
> + return ret;
> +
> +}
> +
> +int iio_put_event(struct iio_dev *dev_info,
> + int ev_line,
> + int ev_code,
> + s64 timestamp)
> +{
> + return __iio_put_event(&dev_info->event_interfaces[ev_line],
> + ev_code, timestamp, NULL);
> +}
> +EXPORT_SYMBOL(iio_put_event);
> +
> +/* 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;
> + }
> +
> + INIT_LIST_HEAD(&dev_info->interrupts[line_number]->ev_list.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 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);
> +
> +/* 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);
> +
> +/* 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;
> + s64 time_ns;
> +
> + if (list_empty(&int_info->ev_list.list))
> + return IRQ_NONE;
> +
> + time_ns = iio_get_time_ns();
> + /* detect single element list*/
> + if (int_info->ev_list.list.next->next == &int_info->ev_list.list) {
> + disable_irq_nosync(irq);
> + p = list_first_entry(&int_info->ev_list.list,
> + struct iio_event_handler_list,
> + list);
> + p->handler(dev_info, 1, time_ns, 1);
> + } else
> + list_for_each_entry(p, &int_info->ev_list.list, list) {
> + disable_irq_nosync(irq);
> + p->handler(dev_info, 1, time_ns, 0);
> + }
> + return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL(iio_interrupt_handler);
> +
> +int iio_add_event_to_list(struct iio_event_handler_list *list,
> + struct iio_event_handler_list *el)
> +{
> + if (el->refcount == 0)
> + list_add(&list->list, &el->list);
> + el->refcount++;
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_add_event_to_list);
> +
> +int iio_remove_event_from_list(struct iio_event_handler_list
> + *el)
> +{
> + el->refcount--;
> + if (el->refcount == 0)
> + list_del_init(&el->list);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(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 */
> +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 unlike input 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;
> +}
> +
> +
> +/* The main file ops structure. All open calls on the major number will
> + * be handled by this with fops for the actual minor number assigned by
> + * switching function above */
> +static const struct file_operations iio_fops = {
> + .owner = THIS_MODULE,
> + .open = iio_open_file,
> +};
> +
> +ssize_t iio_interrupt_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_interrupt_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_interrupt_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_interrupt_fileops = {
> + .read = iio_interrupt_read,
> + .release = iio_interrupt_release,
> + .open = iio_interrupt_open,
> + .owner = THIS_MODULE,
> +};
> +
> +
> +ssize_t iio_show_attr_minor(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + int len;
> +
> + struct iio_chrdev_minor_attr *_attr
> + = to_iio_chrdev_minor_attr(attr);
> + len = sprintf(buf, "%d\n", _attr->minor);
> +
> + return len;
> +}
> +
> +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_interrupt_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, "bob");
> + class_unregister(&iio_class);
> +}
> +
> +int iio_device_register_sysfs(struct iio_dev *dev_info)
> +{
> + int ret;
> +
> + dev_info->sysfs_dev = device_create(&iio_class,
> + dev_info->dev,
> + MKDEV(0, 0),
> + IIO_ID_FORMAT,
> + dev_info->id);
> +
> + if (IS_ERR(dev_info->sysfs_dev)) {
> + /* what would correct error here be?*/
> + ret = -EINVAL;
> + goto error_ret;
> + }
> + /* register attributes */
> + ret = sysfs_create_group(&dev_info->dev->kobj, dev_info->attrs);
> + if (ret) {
> + dev_err(dev_info->dev, "Failed to register sysfs hooks\n");
> + goto error_free_sysfs_device;
> + }
> +
> + return 0;
> +
> +error_free_sysfs_device:
> + device_unregister(dev_info->dev);
> +error_ret:
> + return ret;
> +}
> +
> +void iio_device_unregister_sysfs(struct iio_dev *dev_info)
> +{
> + sysfs_remove_group(&dev_info->dev->kobj, dev_info->attrs);
> + device_unregister(dev_info->sysfs_dev);
> +}
> +
> +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;
> +}
> +void iio_device_unregister_id(struct iio_dev *dev_info)
> +{
> + /* Can I use the save id? */
> + int id;
> +
> + if (likely(sscanf(dev_info->sysfs_dev->bus_id,
> + IIO_ID_FORMAT, &id) == 1)) {
> + spin_lock(&iio_idr_lock);
> + idr_remove(&iio_idr, id);
> + spin_unlock(&iio_idr_lock);
> + } else
> + dev_dbg(dev_info->dev->parent,
> + "indio_device_unregister() failed: bad class ID!\n");
> +}
> +
> +int iio_device_register_eventset(struct iio_dev *dev_info)
> +{
> + int ret, i, j;
> +
> + struct device_attribute *devattr;
> + struct iio_event_attr *indio_devattr;
> +
> + 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->dev);
> + if (ret) {
> + dev_err(dev_info->dev,
> + "Could not get chrdev interface\n");
> + goto error_free_setup_ev_ints;
> + }
> + }
> + dev_info->event_attrs->name = "event_sources";
> + ret = sysfs_create_group(&dev_info->dev->kobj, dev_info->event_attrs);
> + if (ret) {
> + dev_err(dev_info->dev,
> + "Failed to register sysfs hooks for events attributes");
> + goto error_free_setup_ev_ints;
> + }
> + /* May double initialize lists in case of shared handlers,
> + but other than a slight overhead that isn't a problem */
> + j = 0;
> + while (1) {
> + if (dev_info->event_attrs->attrs[j] == NULL)
> + break;
> + devattr = container_of(dev_info->event_attrs->attrs[j],
> + struct device_attribute, attr);
> + indio_devattr = to_iio_event_attr(devattr);
> + INIT_LIST_HEAD(&indio_devattr->listel->list);
> + j++;
> + }
> + return 0;
> +
> +error_free_setup_ev_ints:
> + for (j = 0; j < i; j++)
> + iio_free_ev_int(&dev_info->event_interfaces[j],
> + dev_info->dev);
> + kfree(dev_info->interrupts);
> +error_free_event_interfaces:
> + kfree(dev_info->event_interfaces);
> +error_ret:
> + return ret;
> +}
> +
> +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++)
> + iio_free_ev_int(&dev_info->event_interfaces[i],
> + dev_info->dev);
> + if (dev_info->event_attrs)
> + sysfs_remove_group(&dev_info->dev->kobj, dev_info->event_attrs);
> + kfree(dev_info->event_interfaces);
> +}
> +
> +int iio_get_ptimer(const char **name)
> +{
> + struct ptimer_info_listel *ptimer_i;
> +
> + *name = NULL;
> + mutex_lock(&industrialio_ptimer_board_lock);
> +
> + list_for_each_entry(ptimer_i, &industrialio_ptimer_board_info_list,
> + list)
> + if (ptimer_i->inuse == false) {
> + ptimer_i->inuse = true;
> + *name = ptimer_i->info.name;
> + break;
> + }
> + mutex_unlock(&industrialio_ptimer_board_lock);
> + if (*name == NULL)
> + return -EINVAL;
> +
> + return 0;
> +}
> +int iio_free_ptimer(const char *name)
> +{
> + struct ptimer_info_listel *ptimer_i;
> +
> + mutex_lock(&industrialio_ptimer_board_lock);
> + list_for_each_entry(ptimer_i, &industrialio_ptimer_board_info_list,
> + list)
> + if (ptimer_i->info.name == name) {
> + ptimer_i->inuse = false;
> + break;
> + }
> + mutex_unlock(&industrialio_ptimer_board_lock);
> +
> + return 0;
> +}
> +
> +int iio_device_register_ptimer(struct iio_dev *dev_info)
> +{
> + int ret = 0;
> +
> + if (dev_info->modes & INDIO_RING_POLLED) {
> + ret = iio_get_ptimer(&dev_info->ptimer_name);
> + if (ret)
> + goto error_ret;
> + ret = iio_ptimer_request_periodic_timer((char *)
> + (dev_info->ptimer_name),
> + dev_info);
> + if (ret)
> + goto error_release_ptimer;
> + }
> +
> + return ret;
> +
> +error_release_ptimer:
> + iio_free_ptimer(dev_info->ptimer_name);
> +error_ret:
> + return ret;
> +
> +}
> +
> +void iio_device_unregister_ptimer(struct iio_dev *dev_info)
> +{
> + if (dev_info->ptimer) {
> + iio_ptimer_unrequest_periodic_timer(dev_info);
> + iio_free_ptimer(dev_info->ptimer_name);
> + }
> +}
> +
> +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)
> + goto error_nothing;
> +
> +/* Create sysfs device */
> + ret = iio_device_register_sysfs(dev_info);
> + if (ret)
> + goto error_free_idr;
> +
> +/* Interrupt triggered events setup */
> + ret = iio_device_register_eventset(dev_info);
> + if (ret)
> + goto error_free_sysfs;
> +
> +/* Ring buffer init if relevant */
> + if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY)) {
> +
> + ret = iio_device_register_sw_ring(dev_info, 0);
> + if (ret)
> + goto error_free_eventset;
> + }
> +/* Register ptimer if relevant */
> + if (dev_info->modes & INDIO_RING_POLLED) {
> + ret = iio_device_register_ptimer(dev_info);
> + if (ret)
> + goto error_unregister_sw_ring;
> + }
> +
> + return 0;
> +
> +error_unregister_sw_ring:
> + iio_device_unregister_sw_ring(dev_info);
> +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_GPL(iio_device_register);
> +
> +void iio_device_unregister(struct iio_dev *dev_info)
> +{
> + if (dev_info->modes & INDIO_RING_POLLED)
> + iio_device_unregister_ptimer(dev_info);
> + if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY))
> + iio_device_unregister_sw_ring(dev_info);
> + iio_device_unregister_eventset(dev_info);
> + iio_device_unregister_sysfs(dev_info);
> + iio_device_unregister_id(dev_info);
> +
> +}
> +EXPORT_SYMBOL_GPL(iio_device_unregister);
> +
> +subsys_initcall(iio_init);
> +module_exit(iio_exit);
> --- a/drivers/industrialio/industrialio-rtc.c 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/industrialio/industrialio-rtc.c 2008-07-23 15:08:43.000000000 +0100
> @@ -0,0 +1,134 @@
> +/* 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.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/industrialio.h>
> +#include <linux/industrialio_ptimer.h>
> +/* This is a temporary stop gap until a more generic timer subsystem is in place
> + * within the kernel.
> + * See discussion (initial thoughts so far) on the rtc mailing list.
> + * Comments still welcomed though I may not do much about them!.
> + */
> +int iio_ptimer_irq_set_state(struct iio_dev *indio_dev, bool state)
> +{
> + return rtc_irq_set_state(indio_dev->ptimer->rtc,
> + &indio_dev->ptimer->task,
> + state);
> +}
> +EXPORT_SYMBOL(iio_ptimer_irq_set_state);
> +
> +int iio_ptimer_set_freq(struct iio_periodic *ptimer,
> + unsigned frequency)
> +{
> + int ret;
> +
> + ret = rtc_irq_set_freq(ptimer->rtc, &ptimer->task, frequency);
> + if (ret == 0)
> + ptimer->frequency = frequency;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(iio_ptimer_set_freq);
> +
> +static ssize_t iio_ptimer_show_samp_freq(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *dev_info = dev_get_drvdata(dev);
> + struct iio_periodic *ptimer = dev_info->ptimer;
> + return sprintf(buf, "%u\n", ptimer->frequency);
> +}
> +
> +static ssize_t iio_ptimer_store_samp_freq(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t len)
> +{
> + struct iio_dev *dev_info = dev_get_drvdata(dev);
> + struct iio_periodic *ptimer = dev_info->ptimer;
> + int ret;
> + unsigned long val;
> +
> + ret = strict_strtoul(buf, 10, &val);
> + if (ret)
> + goto error_ret;
> + ret = iio_ptimer_set_freq(ptimer, val);
> + if (ret)
> + goto error_ret;
> + return len;
> +
> +error_ret:
> + return ret;
> +}
> +
> +
> +IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
> + iio_ptimer_show_samp_freq,
> + iio_ptimer_store_samp_freq);
> +
> +int iio_ptimer_request_periodic_timer(char *name,
> + struct iio_dev *indio_dev)
> +{
> + int ret;
> +
> + indio_dev->ptimer = kmalloc(sizeof(struct iio_periodic), GFP_KERNEL);
> + indio_dev->ptimer->rtc = rtc_class_open(name);
> + if (indio_dev->ptimer == NULL) {
> + ret = -EINVAL;
> + goto error_free_ptimer;
> + }
> + indio_dev->ptimer->task.func = indio_dev->ring_poll_func;
> + indio_dev->ptimer->task.private_data = indio_dev;
> + ret = rtc_irq_register(indio_dev->ptimer->rtc,
> + &indio_dev->ptimer->task);
> + if (ret)
> + goto error_close_class;
> +
> + ret = sysfs_add_file_to_group(&indio_dev->dev->kobj,
> + &iio_dev_attr_sampling_frequency
> + .dev_attr.attr,
> + "ring_buffer");
> + if (ret)
> + goto error_unregister_irq;
> +
> + return 0;
> +
> +error_unregister_irq:
> + rtc_irq_unregister(indio_dev->ptimer->rtc, &indio_dev->ptimer->task);
> +
> +error_close_class:
> + rtc_class_close(indio_dev->ptimer->rtc);
> +error_free_ptimer:
> + kfree(indio_dev->ptimer);
> + indio_dev->ptimer = NULL;
> + return ret;
> +}
> +EXPORT_SYMBOL(iio_ptimer_request_periodic_timer);
> +
> +void iio_ptimer_unrequest_periodic_timer(struct iio_dev *indio_dev)
> +{
> +
> + sysfs_remove_file_from_group(&indio_dev->dev->kobj,
> + &iio_dev_attr_sampling_frequency
> + .dev_attr.attr,
> + "ring_buffer");
> +
> + if (indio_dev->ptimer->rtc) {
> + rtc_irq_set_state(indio_dev->ptimer->rtc,
> + &indio_dev->ptimer->task, 0);
> + rtc_irq_unregister(indio_dev->ptimer->rtc,
> + &indio_dev->ptimer->task);
> + flush_scheduled_work();
> + rtc_class_close(indio_dev->ptimer->rtc);
> + flush_scheduled_work();
> + }
> + kfree(indio_dev->ptimer);
> +}
> +EXPORT_SYMBOL(iio_ptimer_unrequest_periodic_timer);
> --- a/drivers/industrialio/industrialio-ring.c 1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/industrialio/industrialio-ring.c 2008-07-23 15:10:39.000000000 +0100
> @@ -0,0 +1,770 @@
> +/* 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.
> + *
> + * Handling of ring allocation / resizing.
> + *
> + *
> + * Things to look at here.
> + * - Better memory allocation techniques?
> + * - Alternative access techniques?
> + */
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/fs.h>
> +#include <linux/poll.h>
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/industrialio.h>
> +
> +/* Prevent resizing of the ring if it might break anything */
> +void iio_mark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring)
> +{
> + spin_lock(ring->use_lock);
> + ring->use_count++;
> + spin_unlock(ring->use_lock);
> +}
> +EXPORT_SYMBOL_GPL(iio_mark_sw_ring_buffer_in_use);
> +
> +void iio_unmark_sw_ring_buffer_in_use(struct iio_sw_ring_buffer *ring)
> +{
> + spin_lock(ring->use_lock);
> + ring->use_count--;
> + spin_unlock(ring->use_lock);
> +}
> +EXPORT_SYMBOL_GPL(iio_unmark_sw_ring_buffer_in_use);
> +
> +/* Mark that a resize is needed */
> +static void iio_mark_sw_ring_buffer_need_update(struct iio_sw_ring_buffer *ring)
> +{
> + spin_lock(ring->use_lock);
> + ring->update_needed = 1;
> + spin_unlock(ring->use_lock);
> +}
> +
> +/* Event handling for the ring - allows escallation of events */
> +int iio_put_ring_event(struct iio_ring_buffer *ring_buf,
> + int event_code,
> + s64 timestamp)
> +{
> + return __iio_put_event(&ring_buf->ev_int,
> + event_code,
> + timestamp,
> + &ring_buf->shared_ev_pointer);
> +}
> +EXPORT_SYMBOL(iio_put_ring_event);
> +
> +int iio_put_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
> + int event_code,
> + s64 timestamp)
> +{
> + if (ring_buf->shared_ev_pointer.ev_p)
> + __iio_change_event(ring_buf->shared_ev_pointer.ev_p,
> + event_code,
> + timestamp);
> + else
> + return iio_put_ring_event(ring_buf,
> + event_code,
> + timestamp);
> + return 0;
> +}
> +EXPORT_SYMBOL(iio_put_or_escallate_ring_event);
> +
> +
> +/* Ring buffer related functionality */
> +/* Store to ring is typically called in the bh of a data ready interrupt handler
> + * in the device driver */
> +/* Lock always held if their is a chance this may be called */
> +int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
> + unsigned char *data,
> + s64 timestamp)
> +{
> + bool init_read = true;
> + int ret;
> + int code;
> +
> + /* initial store */
> + if (unlikely(ring->write_p == 0)) {
> + ring->write_p = ring->data;
> + /* doesn't actually matter if this is out of the set */
> + ring->half_p = ring->data - ring->buf.length*ring->buf.size/2;
> + init_read = false;
> + }
> + memcpy(ring->write_p, data, ring->buf.size);
> + barrier();
> + ring->last_written_p = ring->write_p;
> + barrier();
> + ring->write_p += ring->buf.size;
> + /* End of ring, back to the beginning */
> + if (ring->write_p == ring->data + ring->buf.length*ring->buf.size) {
> + ring->write_p = ring->data;
> + ring->buf.loopcount++;
> + }
> + if (ring->read_p == 0)
> + ring->read_p = ring->data;
> + /* Buffer full - move the read pointer and create / escalate
> + * ring event */
> + else if (ring->write_p == ring->read_p) {
> + ring->read_p += ring->buf.size;
> + if (ring->read_p
> + == ring->data + ring->buf.length*ring->buf.size)
> + ring->read_p = ring->data;
> +
> + spin_lock(ring->buf.shared_ev_pointer.lock);
> + if (ring->buf.shared_ev_pointer.ev_p) {
> + /* Event escalation - probably quicker to let this
> + keep running than check if it is necessary */
> + code = IIO_EVENT_CODE_RING_100_FULL;
> + __iio_change_event(ring
> + ->buf.shared_ev_pointer.ev_p,
> + code,
> + timestamp);
> + } else {
> + code = IIO_EVENT_CODE_RING_100_FULL;
> + ret = __iio_put_event(&ring->buf.ev_int,
> + code,
> + timestamp,
> + &ring
> + ->buf.shared_ev_pointer);
> + if (ret) {
> + spin_unlock(ring->buf.shared_ev_pointer.lock);
> + goto error_ret;
> + }
> + }
> + spin_unlock(ring->buf.shared_ev_pointer.lock);
> + }
> + /* investigate if our event barrier has been passed */
> + /* There are definite 'issues' with this and chances of
> + * simultaneous read */
> + /* Also need to use loop count to ensure this only happens once */
> + ring->half_p += ring->buf.size;
> + if (ring->half_p == ring->data + ring->buf.length*ring->buf.size)
> + ring->half_p = ring->data;
> + if (ring->half_p == ring->read_p) {
> + spin_lock(ring->buf.shared_ev_pointer.lock);
> + code = IIO_EVENT_CODE_RING_50_FULL;
> + ret = __iio_put_event(&ring->buf.ev_int,
> + code,
> + timestamp,
> + &ring->buf.shared_ev_pointer);
> + spin_unlock(ring->buf.shared_ev_pointer.lock);
> +
> + if (ret)
> + goto error_ret;
> + }
> + return 0;
> +error_ret:
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_store_to_sw_ring);
> +
> +
> +/*doesn't currently read the timestamp */
> +/* For software ring buffers this function is needed to get the latest
> + * reading without preventing it from ending up in the ring buffer.
> +*/
> +int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
> + unsigned char *data)
> +{
> + int loopcount_copy;
> + unsigned char *last_written_p_copy;
> + iio_mark_sw_ring_buffer_in_use(ring);
> +again:
> + loopcount_copy = ring->buf.loopcount;
> + barrier();
> + last_written_p_copy = ring->last_written_p;
> + barrier(); /*unnessecary? */
> +
> + memcpy(data, last_written_p_copy, ring->buf.size);
> +
> + if (unlikely(loopcount_copy != ring->buf.loopcount)) {
> + if (unlikely(ring->last_written_p >= last_written_p_copy))
> + goto again;
> + }
> + iio_unmark_sw_ring_buffer_in_use(ring);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_read_last_from_sw_ring);
> +
> +/* Ring buffer access fileops */
> +int iio_ring_open(struct inode *inode, struct file *filp)
> +{
> + struct iio_sw_ring_buffer *ring = filp->private_data;
> +
> + iio_mark_sw_ring_buffer_in_use(ring);
> + try_module_get(ring->buf.access_minor_attr.dev_attr.attr.owner);
> +
> + return 0;
> +}
> +
> +int iio_ring_release(struct inode *inode, struct file *filp)
> +{
> + struct iio_sw_ring_buffer *ring = filp->private_data;
> +
> + module_put(ring->buf.access_minor_attr.dev_attr.attr.owner);
> + clear_bit(IIO_BUSY_BIT_POS, &ring->buf.access_handler.flags);
> + iio_unmark_sw_ring_buffer_in_use(ring);
> +
> + return 0;
> +}
> +
> +/* no point in ripping more than nearest number of whole records below count */
> +/* Depending on movement of pointers in the meantime this may return a lot
> + * less than count*/
> +/* Also, we aren't going to wait for enough data to be available */
> +
> +/* Can only occur currently when the ring buffer is marked
> + - from userspace call */
> +ssize_t iio_ring_rip(struct file *filp,
> + char *buf,
> + size_t count,
> + loff_t *f_ps)
> +{
> + unsigned char *initial_read_p, *initial_write_p,
> + *current_read_p, *end_read_p;
> +
> + struct iio_sw_ring_buffer *ring = filp->private_data;
> + unsigned char *data_cpy;
> + int ret;
> + int dead_offset;
> + int bytes_to_rip = 0;
> + int max_copied;
> + /* Round down to nearest datum boundary */
> + bytes_to_rip = (count - count % ring->buf.size);
> + /* Limit size to whole of ring buffer */
> + if (bytes_to_rip > ring->buf.size*ring->buf.length)
> + bytes_to_rip = ring->buf.size*ring->buf.length;
> + data_cpy = kmalloc(bytes_to_rip, GFP_KERNEL);
> + if (data_cpy == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> +
> + /* build local copy */
> + initial_read_p = ring->read_p;
> + if (unlikely(initial_read_p == 0)) {
> + /* No data here as yet */
> + ret = 0;
> + goto error_free_data_cpy;
> + }
> + initial_write_p = ring->write_p;
> +
> + /* Need a consistent pair */
> + while (initial_read_p != ring->read_p
> + || initial_write_p != ring->write_p) {
> + initial_read_p = ring->read_p;
> + initial_write_p = ring->write_p;
> + }
> + if (initial_write_p == initial_read_p) {
> + /* No new data available.*/
> + ret = 0;
> + goto error_free_data_cpy;
> + }
> +
> + if (initial_write_p > initial_read_p + bytes_to_rip) {
> + /* write_p is greater than necessary, all is easy */
> + max_copied = bytes_to_rip;
> + memcpy(data_cpy, initial_read_p, max_copied);
> + end_read_p = initial_read_p + max_copied;
> + } else if (initial_write_p > initial_read_p) {
> + /*not enough data to cpy */
> + max_copied = initial_write_p - initial_read_p;
> + memcpy(data_cpy, initial_read_p, max_copied);
> + end_read_p = initial_write_p;
> + } else { /* going through 'end' of ring buffer */
> + max_copied = ring->data
> + + ring->buf.length*ring->buf.size - initial_read_p;
> + memcpy(data_cpy, initial_read_p, max_copied);
> + if (initial_write_p > ring->data + bytes_to_rip - max_copied) {
> + /* enough data to finish */
> + memcpy(data_cpy + max_copied, ring->data,
> + bytes_to_rip - max_copied);
> + max_copied = bytes_to_rip;
> + end_read_p = ring->data + (bytes_to_rip - max_copied);
> + } else { /* not enough data */
> + memcpy(data_cpy + max_copied, ring->data,
> + initial_write_p - ring->data);
> + max_copied += initial_write_p - ring->data;
> + end_read_p = initial_write_p;
> + }
> + }
> + /* Now to verify which section was cleanly copied - i.e. how far
> + * read pointer has been pushed */
> + current_read_p = ring->read_p;
> +
> + if (initial_read_p <= current_read_p)
> + dead_offset = current_read_p - initial_read_p;
> + else
> + dead_offset = ring->buf.length*ring->buf.size
> + - (initial_read_p - current_read_p);
> +
> + /* possible issue if the initial write has been lapped or indeed
> + * the point we were reading to has been passed */
> + /* No valid data read.
> + * In this case the read pointer is already correct having been
> + * pushed further than we would look. */
> + if (max_copied - dead_offset < 0) {
> + ret = 0;
> + goto error_free_data_cpy;
> + }
> +
> + /* setup the next read position */
> + ring->read_p = end_read_p;
> +
> + if (copy_to_user(buf, data_cpy + dead_offset,
> + max_copied - dead_offset)) {
> + ret = -EFAULT;
> + goto error_free_data_cpy;
> + }
> + kfree(data_cpy);
> +
> + return max_copied - dead_offset;
> +
> +error_free_data_cpy:
> + kfree(data_cpy);
> +error_ret:
> + return 0;
> +}
> +
> +static const struct file_operations iio_ring_fileops = {
> + .read = iio_ring_rip,
> + .release = iio_ring_release,
> + .open = iio_ring_open,
> + .owner = THIS_MODULE,
> +};
> +
> +inline int __iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
> + int id,
> + struct module *owner,
> + struct device *dev)
> +{
> + int ret;
> +
> +/* Create and register the event character device */
> + buf->event_minor_name = kmalloc(20, GFP_KERNEL);
> + if (buf->event_minor_name == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> + sprintf(buf->event_minor_name, "ring_buffer%d_ev_minor", id);
> + ret = iio_setup_ev_int(&(buf->ev_int),
> + (const char *)(buf->event_minor_name),
> + owner,
> + dev);
> + if (ret)
> + goto error_free_event_minor_name;
> +
> + return 0;
> +
> +error_free_event_minor_name:
> + kfree(buf->event_minor_name);
> +error_ret:
> + return ret;
> +}
> +
> +inline void __iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
> + struct device *dev)
> +{
> + iio_free_ev_int(&(buf->ev_int), dev);
> + kfree(buf->event_minor_name);
> +}
> +
> +inline int
> +__iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
> + int id,
> + struct module *owner,
> + struct device *dev,
> + const struct file_operations *fops)
> +{
> + int ret;
> +/* Create and register the access character device */
> + buf->access_minor_name = kmalloc(20, GFP_KERNEL);
> + if (buf->access_minor_name == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> + sprintf(buf->access_minor_name, "ring_buffer%d_access_minor", id);
> +
> + ret = iio_allocate_chrdev(&buf->access_handler);
> + if (ret)
> + goto error_free_access_minor_name;
> + buf->access_handler.fops = fops;
> + buf->access_handler.flags = 0;
> +
> + __init_iio_chrdev_minor_attr(&buf->access_minor_attr,
> + (const char *)(buf->access_minor_name),
> + owner,
> + buf->access_handler.id);
> +
> + ret = sysfs_create_file(&dev->kobj,
> + &(buf->access_minor_attr.dev_attr.attr));
> + if (ret)
> + goto error_deallocate_chrdev;
> + return 0;
> +
> +error_deallocate_chrdev:
> + iio_deallocate_chrdev(&buf->access_handler);
> +error_free_access_minor_name:
> + kfree(buf->access_minor_name);
> +error_ret:
> + return ret;
> +}
> +
> +inline void __iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
> + struct device *dev)
> +{
> + sysfs_remove_file(&dev->kobj,
> + &buf->access_minor_attr.dev_attr.attr);
> + iio_deallocate_chrdev(&buf->access_handler);
> + kfree(buf->access_minor_name);
> +}
> +
> +int iio_request_hw_ring_buffer(int bytes_per_datum,
> + int length,
> + struct iio_hw_ring_buffer **ring,
> + int id,
> + struct module *owner,
> + struct device *dev,
> + const struct file_operations *fops,
> + void *private)
> +{
> + int ret;
> +
> + *ring = kmalloc(sizeof(struct iio_hw_ring_buffer),
> + GFP_KERNEL);
> +
> + if (*ring == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> + (*ring)->private = private;
> + INIT_IIO_RING_BUFFER(&((*ring)->buf), bytes_per_datum, length);
> + ret = __iio_request_ring_buffer_event_chrdev(&(*ring)->buf,
> + id,
> + owner,
> + dev);
> +
> + if (ret)
> + goto error_free_ring_data;
> + ret = __iio_request_ring_buffer_access_chrdev(&(*ring)->buf,
> + id,
> + owner,
> + dev,
> + fops);
> + if (ret)
> + goto error_free_ring_buffer_event_chrdev;
> + (*ring)->buf.ev_int.private = (*ring);
> + (*ring)->buf.access_handler.private = (*ring);
> +
> + return 0;
> +
> +error_free_ring_buffer_event_chrdev:
> + __iio_free_ring_buffer_event_chrdev(&(*ring)->buf, dev);
> +error_free_ring_data:
> + /* there isn't any!*/
> +error_ret:
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_request_hw_ring_buffer);
> +
> +void iio_free_hw_ring_buffer(struct iio_hw_ring_buffer *ring,
> + struct device *dev)
> +{
> + __iio_free_ring_buffer_access_chrdev(&(ring->buf), dev);
> + __iio_free_ring_buffer_event_chrdev(&(ring->buf), dev);
> + kfree(ring);
> +}
> +EXPORT_SYMBOL_GPL(iio_free_hw_ring_buffer);
> +
> +
> +/* Resize the ring if requested - run whenever ring buffer mode entered */
> +int __iio_request_update_sw_ring_buffer(int bytes_per_datum,
> + int length,
> + struct iio_sw_ring_buffer *ring,
> + int id,
> + struct module *owner,
> + struct device *dev)
> +{
> +/* Reference count the ring - if anyone is using it this will fail!*/
> + int ret = 0;
> +/* Need to sanity check if this is necessary? */
> + spin_lock(ring->use_lock);
> +
> + if (ring->use_count || !ring->update_needed) {
> + ret = -EAGAIN;
> + goto error_ret;
> + }
> + kfree(ring->data);
> + /* keeps clear of chr devs etc - so fine to use here - I THINK!*/
> + INIT_IIO_SW_RING_BUFFER(ring, bytes_per_datum, length);
> + if (ring->data == NULL)
> + ret = -ENOMEM;
> +
> +error_ret:
> + spin_unlock(ring->use_lock);
> + return ret;
> +}
> +
> +int iio_request_update_sw_ring_buffer(struct iio_dev *dev_info, int id)
> +{
> + return __iio_request_update_sw_ring_buffer(dev_info
> + ->ring_bytes_per_datum,
> + dev_info->ring_length,
> + dev_info->ring,
> + id,
> + dev_info->driver_module,
> + dev_info->dev);
> +}
> +EXPORT_SYMBOL_GPL(iio_request_update_sw_ring_buffer);
> +
> +/* Should only occur on init so no locking needed */
> +int iio_request_sw_ring_buffer(int bytes_per_datum,
> + int length,
> + struct iio_sw_ring_buffer **ring,
> + int id,
> + struct module *owner,
> + struct device *dev)
> +{
> + int ret;
> +
> + /* Actually do the ring buffer initialization */
> + *ring = kzalloc(sizeof(struct iio_sw_ring_buffer),
> + GFP_KERNEL);
> + if (*ring == NULL) {
> + ret = -ENOMEM;
> + goto error_ret;
> + }
> + /* Moved to an allocation on demand model.*/
> + iio_mark_sw_ring_buffer_need_update(*ring);
> + ret = __iio_request_ring_buffer_event_chrdev(&(*ring)->buf,
> + id,
> + owner,
> + dev);
> + if (ret)
> + goto error_free_ring_data;
> +
> + ret = __iio_request_ring_buffer_access_chrdev(&(*ring)->buf,
> + id,
> + owner,
> + dev,
> + &iio_ring_fileops);
> + if (ret)
> + goto error_free_ring_buffer_event_chrdev;
> +
> + /* Setup the private pointer so the fileoperations will work */
> + (*ring)->buf.ev_int.private = (*ring);
> + (*ring)->buf.access_handler.private = (*ring);
> +
> + return 0;
> +
> +error_free_ring_buffer_event_chrdev:
> + __iio_free_ring_buffer_event_chrdev(&(*ring)->buf, dev);
> +error_free_ring_data:
> + FREE_IIO_SW_RING_BUFFER(*ring);
> + kfree(*ring);
> +error_ret:
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_request_sw_ring_buffer);
> +
> +void iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
> + struct device *dev)
> +{
> + __iio_free_ring_buffer_access_chrdev(&(ring->buf), dev);
> + __iio_free_ring_buffer_event_chrdev(&(ring->buf), dev);
> + FREE_IIO_SW_RING_BUFFER(ring);
> + kfree(ring);
> +}
> +EXPORT_SYMBOL_GPL(iio_free_sw_ring_buffer);
> +
> +static ssize_t iio_read_ring_length(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + int len;
> + struct iio_dev *dev_info = dev_get_drvdata(dev);
> +
> + len = sprintf(buf, "%d\n", dev_info->ring_length);
> +
> + return len;
> +}
> +
> +static ssize_t iio_write_ring_length(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t len)
> +{
> + int ret;
> + long val;
> + struct iio_dev *dev_info = dev_get_drvdata(dev);
> + ret = strict_strtol(buf, 10, &val);
> + if (ret)
> + goto error_ret;
> + /* Ring length stored here and in ring? */
> + if (val != dev_info->ring_length) {
> + dev_info->ring_length = val;
> + iio_mark_sw_ring_buffer_need_update(dev_info->ring);
> + }
> +
> + return len;
> +error_ret:
> + return ret;
> +}
> +
> +static ssize_t iio_read_ring_bps(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + int len;
> + struct iio_dev *dev_info = dev_get_drvdata(dev);
> +
> + len = sprintf(buf, "%d\n", dev_info->ring_bytes_per_datum);
> +
> + return len;
> +}
> +
> +
> +DEVICE_ATTR(length, S_IRUGO | S_IWUSR,
> + iio_read_ring_length,
> + iio_write_ring_length);
> +/* The software ring buffers aren't currently capable of changing the
> + * storage accuracy so this is read only.
> + */
> +DEVICE_ATTR(bps, S_IRUGO,
> + iio_read_ring_bps,
> + NULL);
> +
> +ssize_t iio_store_ring_enable(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t len)
> +{
> + int ret;
> + bool requested_state, current_state;
> + struct iio_dev *dev_info = dev_get_drvdata(dev);
> +
> + mutex_lock(&dev_info->mlock);
> + requested_state = (buf[0] == '0') ? 0 : 1;
> + current_state = (dev_info->currentmode
> + & (INDIO_RING_DATA_RDY | INDIO_RING_POLLED))
> + ? 1: 0;
> + if (current_state == requested_state)
> + goto done;
> + if (requested_state) {
> + if (dev_info->ring_preenable) {
> + ret = dev_info->ring_preenable(dev_info);
> + if (ret)
> + goto error_ret;
> + }
> + ret = iio_request_update_sw_ring_buffer(dev_info, 0);
> + if (ret)
> + goto error_ret;
> + iio_mark_sw_ring_buffer_in_use(dev_info->ring);
> + if (dev_info->modes & INDIO_RING_DATA_RDY)
> + dev_info->currentmode = INDIO_RING_DATA_RDY;
> + else if (dev_info->modes & INDIO_RING_POLLED)
> + dev_info->currentmode = INDIO_RING_POLLED;
> + else { /* should never be reached */
> + ret = -EINVAL;
> + goto error_ret;
> + }
> +
> + if (dev_info->ring_postenable) {
> + ret = dev_info->ring_postenable(dev_info);
> + if (ret)
> + goto error_ret;
> + }
> + } else {
> + if (dev_info->ring_predisable) {
> + ret = dev_info->ring_predisable(dev_info);
> + if (ret)
> + goto error_ret;
> + }
> + iio_unmark_sw_ring_buffer_in_use(dev_info->ring);
> + dev_info->currentmode = INDIO_DIRECT_MODE;
> + if (dev_info->ring_postdisable) {
> + ret = dev_info->ring_postdisable(dev_info);
> + if (ret)
> + goto error_ret;
> + }
> + }
> +done:
> + mutex_unlock(&dev_info->mlock);
> + return len;
> +error_ret:
> + mutex_unlock(&dev_info->mlock);
> + return ret;
> +}
> +
> +static ssize_t iio_show_ring_enable(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> +
> + int len;
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +
> + if (indio_dev->currentmode & (INDIO_RING_DATA_RDY | INDIO_RING_POLLED))
> + len = sprintf(buf, "1\n");
> + else
> + len = sprintf(buf, "0\n");
> +
> + return len;
> +}
> +
> +DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR,
> + iio_show_ring_enable,
> + iio_store_ring_enable);
> +
> +static struct attribute *iio_ring_attributes[] = {
> + &dev_attr_length.attr,
> + &dev_attr_bps.attr,
> + &dev_attr_sw_ring_enable.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group iio_ring_attribute_group = {
> + .name = "ring_buffer",
> + .attrs = iio_ring_attributes,
> +};
> +
> +int iio_device_register_sw_ring(struct iio_dev *dev_info, int id)
> +{
> + int ret;
> +
> + ret = iio_request_sw_ring_buffer(dev_info->ring_bytes_per_datum,
> + dev_info->ring_length,
> + &dev_info->ring,
> + id,
> + dev_info->driver_module,
> + dev_info->dev);
> + if (ret < 0)
> + goto error_ret;
> +
> + ret = sysfs_create_group(&dev_info->dev->kobj,
> + &iio_ring_attribute_group);
> + if (ret)
> + goto error_free_ring;
> +
> + return 0;
> +
> +error_free_ring:
> + iio_free_sw_ring_buffer(dev_info->ring, dev_info->dev);
> +error_ret:
> + return ret;
> +}
> +
> +void iio_device_unregister_sw_ring(struct iio_dev *dev_info)
> +{
> + sysfs_remove_group(&dev_info->dev->kobj,
> + &iio_ring_attribute_group);
> + /* deallocate ring buffer related stuff */
> + if (dev_info->modes & (INDIO_RING_POLLED | INDIO_RING_DATA_RDY))
> + iio_free_sw_ring_buffer(dev_info->ring, dev_info->dev);
> +
> +}
Nice idea, I just don't get what is actually going on in
here. This needs more planning.
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
On Wed, Jul 23, 2008 at 08:19:18PM +0100, Ben Dooks wrote:
> On Wed, Jul 23, 2008 at 06:00:29PM +0100, Jonathan Cameron wrote:
> > Dear All,
> >
> > The need for an industrialio subsystem was discussed in
> > http://lkml.org/lkml/2008/5/20/135
>
> The name is really bad, this sounds like something for doing large
> scale industrial process control.
Well, it says "Industrial I/O". To me, this means it handles I/O devices
typically found in industrial applications.
>
> > Firstly thanks to all the people who have contributed to the discussion
> > of this in the past.
> >
> > In brief the intention is provide a kernel subsystem directed towards the
> > handling on sensors (and later related output devices) such as ADC's,
> > accelerometers and many others.
>
> We've already got an perfectly good hwmon framework, do we really need
> to do this again?
hwmon is designed for slow I/O. It won't handle an ADC that does a few
megasamples/sec.
Thanks,
Hans
Jonathan Cameron schreef:
:
> +
> +static void __exit iio_exit(void)
> +{
> + unregister_chrdev(IIO_MAJOR, "bob");
bob?! Maybe you meant "industrialio" ?
See you,
Eric
> hwmon is designed for slow I/O. It won't handle an ADC that does a few
> megasamples/sec.
At that rate I would assume you want a memory mapped ring buffer or
similar not a sysfs style interface ?
Henrique de Moraes Holschuh schreef:
> On Wed, 23 Jul 2008, Jonathan Cameron wrote:
>> The subsystem is now in a functional state with a small set of drivers:
>>
>> Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
>> - Uses a periodic timer to provide ring buffer mode.
>> - All reads form these devices are scan modes so direct single element access
>> is not provided.
>> - Monitor mode on max1363 is not yet supported (need to do a bit debugging of
>> the board I have so as to be able to test this).
>>
>> ST LIS3L02DQ - SPI accelerometer.
>> - Uses a datardy interrupt to driver a software ring buffer.
>> - Most functionality of this device is supported.
>>
>> VTI SCA3000 (tested with an e05)
>> - Hardware ring buffer.
>
> I'd like to see something done to have the common parts of interfaces of the
> same class (e.g. accelerometers) be standard. Like hwmon does with
> temp#_input, etc.
>
> Otherwise you made it easier to write drivers, but did nothing to help
> userspace to USE the drivers :-)
Hi,
I completely agree with Henrique. There already exist 3 accelerometer
drivers in the kernel (and I'm writing a fourth on). What we are
desperately in need of is a _user_ interface. So that a generic program
can pop up and say "Oh, there is an accelerometer on this computer, I'll
use it to detect free falls."
IMHO, I think the ADC should have a much more specific and specialised
(user and kernel) API corresponding to the accelerometers. Granted, you
probably had in mind only the accelerometers for industrial usage, but
it would be much better if the current accelerometer drivers had a
reason to be ported to this new subsystem.
In particular, what I think would be worthy would be:
* Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current
version of industrialio
* If the accelerometer is soldered on the computer, define once for all
to which _physical_ movement corresponds which axis (eg: a laptop on its
normal position going up has axis Z increasing).
* Free fall event. Either it's hardware detected, or the accelerometer
infrastructure will detect it in software.
* For each axis, what is the maximum and minimum bound and the unit -
Probably worthy for the whole ADC infrastructure
* Joystick emulation (calibrated so that when the computer is not
moving, all the values are at 0). All the current drivers have it.
That's it for my wish-list for accelerometers :-)
Also, a couple of specific comments on the patches:
* At a quick glance, it seems the VTI SCA3000 driver sends events at 50%
and 75% ring full, while the ST LIS3L02DQ driver sends events at 50% and
100%. Why this difference?
* The accelerometer infrastructure has axis offset and axis gain
attributes. However, it seems really specific to the ST accelerometer
(and still, I doubt it's useful for most users as those values are
factory-set to good values). Shouldn't they be moved to the ST LIS3L02DQ
driver instead of being part of the accelerometer infrastructure?
* It would be also very good to provide a small userspace program
showing how to read an industrialio device (à la evtest).
See you,
Eric
On Thu, Jul 24, 2008 at 09:41:25AM +0200, Hans J. Koch wrote:
> On Wed, Jul 23, 2008 at 08:19:18PM +0100, Ben Dooks wrote:
> > On Wed, Jul 23, 2008 at 06:00:29PM +0100, Jonathan Cameron wrote:
> > > Dear All,
> > >
> > > The need for an industrialio subsystem was discussed in
> > > http://lkml.org/lkml/2008/5/20/135
> >
> > The name is really bad, this sounds like something for doing large
> > scale industrial process control.
>
> Well, it says "Industrial I/O". To me, this means it handles I/O devices
> typically found in industrial applications.
Yes, industrial is generally process control of manufacturing
processes which in my view is making this sound like it is limiting
the field of operations.
All the applications we would currently need are things like
handheld PDA type devices which are hardly 'industrial' or small
consumer measurement systems.
> >
> > > Firstly thanks to all the people who have contributed to the discussion
> > > of this in the past.
> > >
> > > In brief the intention is provide a kernel subsystem directed towards the
> > > handling on sensors (and later related output devices) such as ADC's,
> > > accelerometers and many others.
> >
> > We've already got an perfectly good hwmon framework, do we really need
> > to do this again?
>
> hwmon is designed for slow I/O. It won't handle an ADC that does a few
> megasamples/sec.
>
> Thanks,
> Hans
--
Ben ([email protected], http://www.fluff.org/)
'a smiley only costs 4 bytes'
On Thu, Jul 24, 2008 at 11:44:36AM +0200, Eric Piel wrote:
> Henrique de Moraes Holschuh schreef:
>> On Wed, 23 Jul 2008, Jonathan Cameron wrote:
>>> The subsystem is now in a functional state with a small set of drivers:
>>>
>>> Max1363 (supports numerous Maxim i2c ADC's) (tested with max1363 and max1238 chips)
>>> - Uses a periodic timer to provide ring buffer mode.
>>> - All reads form these devices are scan modes so direct single element access
>>> is not provided.
>>> - Monitor mode on max1363 is not yet supported (need to do a bit debugging of
>>> the board I have so as to be able to test this).
>>>
>>> ST LIS3L02DQ - SPI accelerometer.
>>> - Uses a datardy interrupt to driver a software ring buffer.
>>> - Most functionality of this device is supported.
>>>
>>> VTI SCA3000 (tested with an e05)
>>> - Hardware ring buffer.
>>
>> I'd like to see something done to have the common parts of interfaces of the
>> same class (e.g. accelerometers) be standard. Like hwmon does with
>> temp#_input, etc.
>>
>> Otherwise you made it easier to write drivers, but did nothing to help
>> userspace to USE the drivers :-)
> Hi,
> I completely agree with Henrique. There already exist 3 accelerometer
> drivers in the kernel (and I'm writing a fourth on). What we are
> desperately in need of is a _user_ interface. So that a generic program
> can pop up and say "Oh, there is an accelerometer on this computer, I'll
> use it to detect free falls."
>
> IMHO, I think the ADC should have a much more specific and specialised
> (user and kernel) API corresponding to the accelerometers. Granted, you
> probably had in mind only the accelerometers for industrial usage, but
> it would be much better if the current accelerometer drivers had a
> reason to be ported to this new subsystem.
>
> In particular, what I think would be worthy would be:
> * Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current
> version of industrialio
> * If the accelerometer is soldered on the computer, define once for all
> to which _physical_ movement corresponds which axis (eg: a laptop on its
> normal position going up has axis Z increasing).
> * Free fall event. Either it's hardware detected, or the accelerometer
> infrastructure will detect it in software.
> * For each axis, what is the maximum and minimum bound and the unit -
> Probably worthy for the whole ADC infrastructure
> * Joystick emulation (calibrated so that when the computer is not
> moving, all the values are at 0). All the current drivers have it.
I think if we're trying to make something that will cover a number
of device classes, we need to be more like HID and have a system
that reports and possibly records the following:
1) What data is possible to be returned from each event, with the
units, magnitude and any scale applied by the device sending.
2) Each event should be able to report a number of items, so that
if the sensor reports X/Y/Z in one go, there is just a single
event containing those values.
3) A well defined set of information that can be read (like #1)
that can provide details of the device and how it relates to
the environment it is in.
--
Ben ([email protected], http://www.fluff.org/)
'a smiley only costs 4 bytes'
Anton Vorontsov wrote:
Hi Anton,
>
> Sorry, I didn't comment on the first version. So my comments for
> this version is below. Note, I only briefly looked in the code,
> thus the comments are really superficial.
>
> So... this subsystem is _huge_. And so it needs documentation. Yes,
> the code is well commented, much thanks for this. But the
> documentation is needed, for example, to answer these simple
> questions:
>
> - What userspace interface the subsystem provides (i.e. for use with
> userspace tools).
> - What kernel interface the subsystem provides (i.e. for use with
> in-kernel drivers, for example touchscreens).
> - What is iio_ring (as I see it, is one of basic concepts of this
> subsystem), I'd like to see description of its basic usage.
> - What is struct iio_event_data, what is event's "id"? How it is used
> across subsystem?
>
> There is a lot of questions more. Of course, half the questions I'll
> answer myself through reading the code... But you really need to
> describe basic ideas of 2431-LOC-subsystem.
>
> Something like Documentation/gpio.txt would work.
Agreed, sorry about that - docs are about half written and should probably have
finished them off before I posted the patch. I'll try and get a first version
cleaned up and posted today. Main issue at the moment is getting example code
into a state where it isn't too dependent on the exact board setup I'm testing
on.
> On the subsystem itself. I would recommend you to split the
> industrialio_{core,rtc,ring,ptimer_board_info} into separate modules
> (if possible) and into separate patches (for better reviewing).
>
> Few more comments below.
>
> [...]
>> +
>> +#define INIT_IIO_RING_BUFFER(ring_buf, _bytes_per_datum, _length) { \
>> + (ring_buf)->size = _bytes_per_datum; \
>> + (ring_buf)->length = _length; \
>> + (ring_buf)->loopcount = 0; \
>> + (ring_buf)->shared_ev_pointer.ev_p = 0; \
>> + (ring_buf)->shared_ev_pointer.lock = \
>> + __SPIN_LOCK_UNLOCKED((ring_buf) \
>> + ->shared_ev_pointer->loc); \
>> + }
>
> Is it possible to convert this into function?
Good point. No idea why I did it like that!
> [...]
>> + /* Event control attributes */
>> + struct attribute_group *event_attrs;
>> + /* The character device related elements */
>> + struct iio_event_interface *event_interfaces;
>> +
>> +/* Software Ring Buffer
>> + - for now assuming only makes sense to have a single ring */
>
> These comments are really hard to parse. Try to turn off syntax
> highlighting (if you use), and see the result. It is really
> unreadable.
>
> So, I'd suggest to use kernel doc syntax to describe the fields.
>
> For example, I very like how include/linux/mtd/nand.h is commented.
> It is in kernel doc, plus explains what fields are
> internal/driver-specific/e.t.c.
Thanks for the pointer, I'll do that.
> [...]
>> + IIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
>> +
>
> Why such a generic subsystem should be aware of X/Y/Z? ADC can measure
> strength of wind, for example. But in fact it measures voltage and/or
> current. Yes, it is convenient to tell user what exactly chip is
> supposed to measure, but it is chip (and thus driver) specific,
> subsystem should only provides means to expose that information, but
> it should not be aware of what exactly we're measuring. At least that is
> how I imagine the ADC subsystem.
I'd argue this is actually connected to improving the usability of the system.
I would envision that the set of definitions (probably broken out into specific
headers for different classes of device) and will grow considerably. If a
particular parameter is available in say 2 drivers, or looks like it will be,
then it will get moved to the main headers rather than being driver specific.
The basic advantages of doing it this way are concerned with user readability.
If we know the device is an accelerometer rather than a general ADC is would be
silly to have the user guessing which of the inputs are accelerations rather
than say temperature readings. Clearly there are issues with definitions of axes
on these devices but that will be something to clear up by clear documentation
(pick a standard and stick to it!).
Clearly there are cases, for example where an accelerometer is attached via a
generic ADC in which we don't have the inherent ability to identify the inputs
from the generic adc driver. Given this is a common setup, there may well be
some scope for custom drivers for any common sensor circuits or indeed providing
a way specifying this in a board config.
It will be interesting to see how this pans out.
What I also forgot to mention alongside the patches is that each driver should
be accompanied by a userspace header file providing conversion function
to real world units as appropriate. This is clearly required to interpret the
data coming out of the ring buffers (which will always be as raw as possible).
There is also the question of whether the outputs in sysfs of say acceleration
should be converted to SI units or left as raw readings. For now I'm inclined
to leave them raw as the calibration of such sensors within systems is fairly
complex and often done as an offline process. However I'm definitely open
to other opinions on this.
Thanks for taking a look and providing feedback.
--
Jonathan Cameron
Hi Ben,
Firstly thanks for taking a look at this.
Ben Dooks wrote:
>> +/* The actual event being pushed ot userspace */
>> +struct iio_event_data {
>> + int id;
>> + s64 timestamp;
>> +};
>
> So is this an header for an event? Where is the data in this, this
> is confusing... Also, should we have a framework to produce an
> key/pair data, so that an single event can export multiple values
> from one event...
>
> ie:
>
> struct event_header {
> unsigned int id;
> unsigned int nr_data; /* number of event_data after */
> s64 timestamp;
>
> struct event {
> struct event_header header;
> struct event_data data[0];
> };
At the current time at least the purpose of events in this subsystem is rather
different from those in say the input system. So far they have all
corresponded to things that don't actually have any data associated with them.
The list so far is:
Ring bufer 50% full, Ring buffer 75% full (due to the support for escalating
events, only the highest of these will ever be in the queue).
Motion detected, device dependent on whether this is for specific axis.
Thresholds breached.
Tap detection (common on accelerometers)
Anyhow I did originally start our with a similar layout to you have suggested
(based in input subsystem) but removed it for now as it is currently unecessary.
> the naming of these structures is rather long.
>
>> +/* Requires high resolution timers */
>> +/* TODO - provide alternative if not available? */
>> +static inline s64 iio_get_time_ns(void)
>> +{
>> + struct timespec ts;
>> + ktime_get_ts(&ts);
>> + return timespec_to_ns(&ts);
>> +}
>
> do we really need something that accurate? we should at-least have
> the option to remove the timestamp or choose something of lower
> accuracy?
Not necessarily, hence the TODO. I've been wondering about using an approach
similar to the 'scan' modes seen in the max1363 driver in which the time stamp
will simply be another 'input' recorded to the ring buffer as necessary. Within
the event structure, this is certainly something that needs to be configurable.
>> +
>> +#define INIT_IIO_SW_RING_BUFFER(ring, _bytes_per_datum, _length) { \
>> + INIT_IIO_RING_BUFFER(&(ring)->buf, \
>> + _bytes_per_datum, \
>> + _length); \
>> + (ring)->read_p = 0; \
>> + (ring)->write_p = 0; \
>> + (ring)->last_written_p = 0; \
>> + (ring)->data = kmalloc(_length*(ring)->buf.size, \
>> + GFP_KERNEL); \
>> + (ring)->use_count = 0; \
>> + (ring)->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock); \
>> + }
>
> these should be inlined functions.
Oops, I'll clean them up. Thanks for pointing that out.
>> +
>> +int iio_request_sw_ring_buffer(int bytes_per_datum,
>> + int length,
>> + struct iio_sw_ring_buffer **ring,
>> + int id,
>> + struct module *owner,
>> + struct device *dev);
>> +
>
> how about tying this to a driver, where you already know the owner
> and the dev?
Good point.
>> +
>> +struct iio_work_cont {
>> + struct work_struct ws;
>> + struct work_struct ws_nocheck;
>> + int address;
>> + int mask;
>> + void *st;
>> +};
>> +#define INIT_IIO_WORK_CONT(cont, _checkfunc, _nocheckfunc, _add, _mask, _st)\
>> + do { \
>> + INIT_WORK(&(cont)->ws, _checkfunc); \
>> + INIT_WORK(&(cont)->ws_nocheck, _nocheckfunc); \
>> + (cont)->address = _add; \
>> + (cont)->mask = _mask; \
>> + (cont)->st = _st; \
>> + } while (0)
>
> more nasty macros.
Yes, definitely need to get rid of most of them.
[ring buffer code]
> Nice idea, I just don't get what is actually going on in
> here. This needs more planning.
I definitely should have put together some more explanatory documents explaining
the aims and approach taken for the ring buffer handling. I'll get some initial
stuff writen and posted here shortly.
Thanks for taking a look and your suggestions,
--
Jonathan Cameron
Eric Piel wrote:
> Jonathan Cameron schreef:
> :
>> +
>> +static void __exit iio_exit(void)
>> +{
>> + unregister_chrdev(IIO_MAJOR, "bob");
> bob?! Maybe you meant "industrialio" ?
>
> See you,
> Eric
>
Good spot ;)
Jonathan
>> I'd like to see something done to have the common parts of interfaces of the
>> same class (e.g. accelerometers) be standard. Like hwmon does with
>> temp#_input, etc.
>>
>> Otherwise you made it easier to write drivers, but did nothing to help
>> userspace to USE the drivers :-)
> Hi,
> I completely agree with Henrique. There already exist 3 accelerometer
> drivers in the kernel (and I'm writing a fourth on). What we are
> desperately in need of is a _user_ interface. So that a generic program
> can pop up and say "Oh, there is an accelerometer on this computer, I'll
> use it to detect free falls."
Agreed. (though on the whole I don't like testing that particular
functionality ;)
>
> IMHO, I think the ADC should have a much more specific and specialised
> (user and kernel) API corresponding to the accelerometers. Granted, you
> probably had in mind only the accelerometers for industrial usage, but
> it would be much better if the current accelerometer drivers had a
> reason to be ported to this new subsystem.>
> In particular, what I think would be worthy would be:
> * Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current
> version of industrialio
> * If the accelerometer is soldered on the computer, define once for all
> to which _physical_ movement corresponds which axis (eg: a laptop on its
> normal position going up has axis Z increasing).
Agreed, though this is more documentation (and strict enforcement on drivers)
> * Free fall event. Either it's hardware detected, or the accelerometer
> infrastructure will detect it in software.
Hadn't thought of doing that in software - should be relatively straight
forward, though would involve a fairly large overhead if the intention
is to detect it fast enough to park hardware etc. (similar to that for
the ring buffer I guess). I'll look into getting that done for the next
version (maybe driver specific for now).
> * For each axis, what is the maximum and minimum bound and the unit -
> Probably worthy for the whole ADC infrastructure
Agreed - only laziness has prevented me adding the sysfs stuff to output
that sort of thing.
> * Joystick emulation (calibrated so that when the computer is not
> moving, all the values are at 0). All the current drivers have it.
Ok, this is a matter of writing an input subsystem element to do this.
It was discussed in the original discussion on lkml and is definitely
on the todo list (I like playing with that sort of thing as well :)
> That's it for my wish-list for accelerometers :-)
>
>
> Also, a couple of specific comments on the patches:
> * At a quick glance, it seems the VTI SCA3000 driver sends events at 50%
> and 75% ring full, while the ST LIS3L02DQ driver sends events at 50% and
> 100%. Why this difference?
I wrote the lis3l02dq driver first, then discovered the VTI only gives hardware
interrupts on those values. Probably not something that is going to be easy
to standardize in general. Can easily add more levels if only to ensure a
consistentish set are available.
> * The accelerometer infrastructure has axis offset and axis gain
> attributes. However, it seems really specific to the ST accelerometer
> (and still, I doubt it's useful for most users as those values are
> factory-set to good values).
I'd seriously debate that! For starters that lis3l02dq is reading slightly
over 10g static on my desk. For the sort of stuff I do with these, the
factory calibration is typically shockingly inaccurate and subject to issues
like temperature of environment etc.
> Shouldn't they be moved to the ST LIS3L02DQ
> driver instead of being part of the accelerometer infrastructure?
Based on the other accels I've used (quite a few different ones) these
are pretty standard parameters - and I wish they were there on the VTI chip
as it's calibration is far from perfect. They are to be found in about half
of all the accelerometers I've come across.
> * It would be also very good to provide a small userspace program
> showing how to read an industrialio device (à la evtest).
One is on it's way (oops, should have cleaned that up before posting these
patches)
At the moment the big missing element of the subsystem is an easy way of
querying what is there. (proc interface similar to that for the input subsystem)
The lack of this makes writing remotely generic tests pretty hideous. Currently
I have separate ones for each of the drivers which is just plain silly.
I guess for now posting a commented one of those would be a good start and
I'll do so after lunch.
Thanks for taking a look and particularly the accelerometer wish list. I'll see
what I can do!
--
Jonathan Cameron
Dear Ben,
>> In particular, what I think would be worthy would be:
>> * Up to 3 pre-defined axes (X, Y, Z) - that's provided by your current
>> version of industrialio
>> * If the accelerometer is soldered on the computer, define once for all
>> to which _physical_ movement corresponds which axis (eg: a laptop on its
>> normal position going up has axis Z increasing).
>> * Free fall event. Either it's hardware detected, or the accelerometer
>> infrastructure will detect it in software.
>> * For each axis, what is the maximum and minimum bound and the unit -
>> Probably worthy for the whole ADC infrastructure
>> * Joystick emulation (calibrated so that when the computer is not
>> moving, all the values are at 0). All the current drivers have it.
>
> I think if we're trying to make something that will cover a number
> of device classes, we need to be more like HID and have a system
> that reports and possibly records the following:
Ok, this is the key disagreement at the moment on the approach to be
taken here. I'd again argue that the purpose of events in this subsys
are very different from what you describing. Events tend to indicate
that something has happened (eg. the Free fall even mentioned above),
not that the current value of something is x. It may be that we
will have devices that do indeed act closer to you conventional
user interface device and provide (x moved to 103 etc). If we
do then this may all need rethinking.
> 1) What data is possible to be returned from each event, with the
> units, magnitude and any scale applied by the device sending.
>
> 2) Each event should be able to report a number of items, so that
> if the sensor reports X/Y/Z in one go, there is just a single
> event containing those values.
Agreed.
> 3) A well defined set of information that can be read (like #1)
> that can provide details of the device and how it relates to
> the environment it is in.
Indeed, this is crucial and probably forms the next element of the
code to be written. At the moment I'm inclined to go with an approach
similar to the input subsystem with it's proc interface that provides
a nice machine readable description of device functionality.
Anyone have any other suggestions on how this can be sensibly achieved?
Thanks,
--
Jonathan Cameron
Alan Cox wrote:
>> hwmon is designed for slow I/O. It won't handle an ADC that does a few
>> megasamples/sec.
>
> At that rate I would assume you want a memory mapped ring buffer or
> similar not a sysfs style interface ?
Agreed, this isn't going to do megsamps per second either (at least not
without a fair bit faster processor / memory than available in embedded
architectures). If you want to go at those rates you'll need something
with a hardware ring buffer or an intermediate chip to provide said
buffering whilst not trying to run a general purpose os at the same time.
I am considering adding a 'burst' mode which would use up any available
i2c / spi hardware buffers to get somewhere nearer to those sort of speeds.
One of the most important elements of this subsystem is indeed a ring
buffer architecture, currently not mmapped. I'm still trying to work out
how to make that work without blocking the ring filling on interrupts.
Currently I can only seem to get around this by doing copies of ring
to mmapped locations and having some dirty bit type marking of regions
that don't contain valid data. I guess suitable userspace libraries
could hide this mess though.
For the few ksps range the current chrdev interface is adequate if not
ideal.
The ring achitecture is one of the areas that probably needs most work.
Thanks,
--
Jonathan Cameron
Ben Dooks wrote:
> On Wed, Jul 23, 2008 at 06:00:29PM +0100, Jonathan Cameron wrote:
>> Dear All,
>>
>> The need for an industrialio subsystem was discussed in
>> http://lkml.org/lkml/2008/5/20/135
>
> The name is really bad, this sounds like something for doing large
> scale industrial process control.
To a certain extent I agree, it's a pretty hideous name and I'm happy to change
it if someone can come up with a better one whilst maintaining the flexibility
to handle devices that do a range of different sensing and output tasks.
>> Firstly thanks to all the people who have contributed to the discussion
>> of this in the past.
>>
>> In brief the intention is provide a kernel subsystem directed towards the
>> handling on sensors (and later related output devices) such as ADC's,
>> accelerometers and many others.
>
> We've already got an perfectly good hwmon framework, do we really need
> to do this again?
On this, see the original discussion (where using that was one of the options)
discussed. Basically it comes down to a different set of requirements with
the ability to handle events from the device, ring buffering and higher (non
cached) update rates. Whilst we could have bludgeoned the functionality into
that framework, it was decided that it was better to start afresh.
Thanks,
--
Jonathan Cameron
Jonathan Cameron schreef:
:
>> * If the accelerometer is soldered on the computer, define once for all
>> to which _physical_ movement corresponds which axis (eg: a laptop on its
>> normal position going up has axis Z increasing).
> Agreed, though this is more documentation (and strict enforcement on drivers)
Indeed, it's more about defining conventions. A one page document saying
to userspace developers how they can expect any accelerometer driver
will behave is desperately needed. The more conventions, the more all
the drivers will behave similarly, and the easier it will be for
userspace programs to be written :-)
>> * Free fall event. Either it's hardware detected, or the accelerometer
>> infrastructure will detect it in software.
> Hadn't thought of doing that in software - should be relatively straight
> forward, though would involve a fairly large overhead if the intention
> is to detect it fast enough to park hardware etc. (similar to that for
> the ring buffer I guess). I'll look into getting that done for the next
> version (maybe driver specific for now).
Yes, on a second though, this is a low priority point. If userspace is
able to know if the hardware has or not freefall detection, it should be
possible to just implement the software detection in the userspace
daemon. People might come up with lots of "clever" algorithms for that,
so it might be better to not do too much in the kernel ;-)
>
> At the moment the big missing element of the subsystem is an easy way of
> querying what is there. (proc interface similar to that for the input subsystem)
You mean /sys/class/input/, right? Indeed, something inspired by the
input subsystem should work well.
See you,
Eric
hat done for the next
>> version (maybe driver specific for now).
> Yes, on a second though, this is a low priority point. If userspace is
> able to know if the hardware has or not freefall detection, it should be
> possible to just implement the software detection in the userspace
> daemon. People might come up with lots of "clever" algorithms for that,
> so it might be better to not do too much in the kernel ;-)
Good point; will leave that one for now.
>
>>
>> At the moment the big missing element of the subsystem is an easy way of
>> querying what is there. (proc interface similar to that for the input
>> subsystem)
> You mean /sys/class/input/, right? Indeed, something inspired by the
> input subsystem should work well.
No, I meant /proc/bus/input/devices which gives machine readable description
of all devices registered with the input subsystem and their capabilities.
Makes it easier to make userspace code reasonably generic.
--
Jonathan
On Thu, Jul 24, 2008 at 01:45:31PM +0100, Jonathan Cameron wrote:
> >>
> >> At the moment the big missing element of the subsystem is an easy way of
> >> querying what is there. (proc interface similar to that for the input
> >> subsystem)
> > You mean /sys/class/input/, right? Indeed, something inspired by the
> > input subsystem should work well.
> No, I meant /proc/bus/input/devices which gives machine readable description
> of all devices registered with the input subsystem and their capabilities.
>
/proc/bus/input is a legacy interface (pre-sysfs). Newer subsystems
should try to stay in /sys/.
--
Dmitry
Dmitry Torokhov wrote:
> On Thu, Jul 24, 2008 at 01:45:31PM +0100, Jonathan Cameron wrote:
>>>> At the moment the big missing element of the subsystem is an easy way of
>>>> querying what is there. (proc interface similar to that for the input
>>>> subsystem)
>>> You mean /sys/class/input/, right? Indeed, something inspired by the
>>> input subsystem should work well.
>> No, I meant /proc/bus/input/devices which gives machine readable description
>> of all devices registered with the input subsystem and their capabilities.
>>
>
> /proc/bus/input is a legacy interface (pre-sysfs). Newer subsystems
> should try to stay in /sys/.
Ah, I'd assumed it was there to provide a centralized way of discovering
what was there. I guess it's just a matter of scanning your way through
the contents of /sys/class/input (and equivalent iio) then to find out
what is present and what is supported by each device.
Messier in a sense but guess it will work.
Ah well,
Jonathan
On Thu, Jul 24, 2008 at 11:01:44AM +0100, Ben Dooks wrote:
> On Thu, Jul 24, 2008 at 09:41:25AM +0200, Hans J. Koch wrote:
> > On Wed, Jul 23, 2008 at 08:19:18PM +0100, Ben Dooks wrote:
> > > On Wed, Jul 23, 2008 at 06:00:29PM +0100, Jonathan Cameron wrote:
> > > > Dear All,
> > > >
> > > > The need for an industrialio subsystem was discussed in
> > > > http://lkml.org/lkml/2008/5/20/135
> > >
> > > The name is really bad, this sounds like something for doing large
> > > scale industrial process control.
> >
> > Well, it says "Industrial I/O". To me, this means it handles I/O devices
> > typically found in industrial applications.
>
> Yes, industrial is generally process control of manufacturing
> processes which in my view is making this sound like it is limiting
> the field of operations.
OK, I agree.
>
> All the applications we would currently need are things like
> handheld PDA type devices which are hardly 'industrial' or small
> consumer measurement systems.
Well, though the _use_ of such devices might not be "industrial",
_technically_ they are very similar to embedded systems found in
automation or other industrial equipment.
Many of these devices (all that have mmappable memory) can be handled
with a UIO driver, but for the rest (mostly stuff connected to serial
busses), it's important to have a subsystem in the kernel. I really
don't care too much about its name. BTW, before UIO was first published,
its internal name was "Industrial I/O" ;-)
Thanks,
Hans
>>> Well, it says "Industrial I/O". To me, this means it handles I/O devices
>>> typically found in industrial applications.
>>>
>> Yes, industrial is generally process control of manufacturing
>> processes which in my view is making this sound like it is limiting
>> the field of operations.
>>
> OK, I agree.
>
Agreed, though don't yet have a better idea.
In fact my use cases are all really embedded systems anyway.
>> All the applications we would currently need are things like
>> handheld PDA type devices which are hardly 'industrial' or small
>> consumer measurement systems.
>>
>
> Well, though the _use_ of such devices might not be "industrial",
> _technically_ they are very similar to embedded systems found in
> automation or other industrial equipment.
>
> Many of these devices (all that have mmappable memory) can be handled
> with a UIO driver, but for the rest (mostly stuff connected to serial
> busses), it's important to have a subsystem in the kernel. I really
> don't care too much about its name. BTW, before UIO was first published,
> its internal name was "Industrial I/O" ;-)
>
lol. We could try and make it the standard working name for all new
subsystems ;)
--
Jonathan Cameron
From: Jonathan Cameron <[email protected]>
A very early cut of some documentation for the industrialio subsystem. Also
includes a small demo app for listening to a ring buffer event chrdev and
reading from the access chrdev as appropriate.
--- a/Documentation/industrialio/overview.txt 1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/industrialio/overview.txt 2008-07-24 18:37:56.000000000 +0100
@@ -0,0 +1,117 @@
+Overview of the industrialio subsystem.
+
+Main Components
+
+Core
+
+industrialio-core.c contains the main registration code for devices using the
+subsytem. The key function is
+
+int iio_device_register(struct iio_dev *dev_info);
+
+This takes a structure containing a number of user specified variables which
+control how the device driver interacts with the subsystem components.
+
+This is a cut down version containing only those elements intended for direct
+access by drivers.
+
+struct iio_dev {
+/* device specific data */
+ void *dev_data;
+
+/* Modes the drivers supports */
+ int modes;
+
+/* Current mode */
+ int currentmode;
+
+/* The device for with which we are dealing */
+ struct device *dev;
+
+/* General attributes */
+ const struct attribute_group *attrs;
+
+/* Used to specify ownership of interrupt etc that may be created by iio */
+ struct module *driver_module;
+/* How many hardware interrupt lines are there */
+ int num_interrupt_lines;
+
+/* Event control attributes */
+ struct attribute_group *event_attrs;
+
+
+/* Software Ring Buffer (discussed in iio_ring.txt
+ - for now assuming only makes sense to have a single ring */
+ int ring_dimension;
+ int ring_bytes_per_datum;
+ int ring_length;
+
+/* enabling / disabling related functions.
+ * post / pre refer to relative to the change of current_mode. */
+ int (*ring_preenable)(struct iio_dev *);
+ int (*ring_postenable)(struct iio_dev *);
+ int (*ring_predisable)(struct iio_dev *);
+ int (*ring_postdisable)(struct iio_dev *);
+
+ void (*ring_poll_func)(void *private_data);
+
+ /* Device state lock.
+ * Used to prevent simultaneous changes to device state.
+ * In here rather than modules as some ring buffer changes must occur
+ * with this locked.*/
+ struct mutex mlock;
+
+};
+
+@dev_data - driver specific data.
+
+@modes - currently limited to combinations of INDIO_DIRECT_MODE,
+INDIO_RING_POLLED, INDIO_RING_DATA_RDY and INDIO_RING_HARDWARE_BUFFER
+
+All devices should probably support INDIO_DIRECT_MODE which means
+that sensor values may be read directly from files in sysfs.
+Typically this may be via single element files (x, y, z for accelerometers)
+or scan files (max1363 for example).
+
+INDIO_RING_POLLED currently uses periodic real time clocks to generate
+interrupts which are then used to poll the device. See iio_ring for more details
+
+INDIO_RING_DATA_RDY is for devices that supply a data ready interrupt when new
+data becomes available (eg lis3l02dq)
+
+INDIO_RING_HARDWARE_BUFFER is for devices with hardware ring buffers
+(eg. sca3000)
+
+@attrs general attribute group for both direct access attributes for reading
+from sensors and for sensor specific parameters of use to userspace (conversion
+factors etc).
+
+@driver_module - corresponds to OWNER within the driver. This is to ensure
+any interrupts etc requested are registered to the relevant module rather than
+iio.
+
+@num_interrupt_lines - does what it says on the tin. Most devices only have one
+but I have seen ones with separate lines for data ready signals from motion
+detection etc.
+
+@event_attrs - sysfs attributes which control whether particular events (read
+interrupts from the point of view of the sensor) are enabled or not. If they
+are up to 10 events will be queued on the related chrdev for reading by
+userspace code.
+
+Ring parameters and functions are covered in iio_ring.txt.
+
+@mlock - device configuration lock. Note it is the responsibility of drivers
+to get this lock if they wish to change parameters which may effect ring buffer
+capture (changing scan modes for example.)
+
+
+What happens when a iio_dev is registered.
+
+1) Unique id obtained.
+2) Sysfs direct read and configuration elements registered
+3) Device event (sensor interrupts) registered.
+4) If software ring to be used, setup the ring but don't actually allocate.
+ This occurs on first enabled / when reenabled after parameter change.
+4) If polled ring get a periodic timer.
+
--- a/Documentation/industrialio/iio_ring.txt 1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/industrialio/iio_ring.txt 2008-07-24 18:45:04.000000000 +0100
@@ -0,0 +1,91 @@
+Industrialio subsystem ring buffers
+
+The industrial io subsystem supports both hardware (eg. sca3000) and software
+(eg. max1363 and lis3l02dq) ring buffers.
+
+For both types a chrdev is used to provide events to userspace. These merely
+contain an id and a timestamp.
+
+This is used to provide notifications of the ring having reached a certain level
+(50%, 75%, 100%).
+
+Direct access to the contents of the ring buffer is available via a second
+dev. The data output is pretty much raw device readings, so a userspace function
+is needed to convert these into appropriate SI units. This function should be
+provided in a device specific header if appropriate.
+
+The hardware ring buffer simply implements the above functionality by pull data
+directly from the device on demand (sca3000).
+
+
+The software ring buffer is somewhat more complex.
+
+The design considerations for this are:
+
+1) Writing should be as latency free as possible (preferably lock free)
+2) As few readings as possible should be missed (ideally none - but as
+ we aren't dealing with a realtime OS some will occasionally be lost).
+3) Reading does not lock the buffer but instead takes a copy then validate
+ what data is 'clean' approach.
+4) The filling of the buffer should be either driver by the device (datardy)
+ or if that is not possible via a periodic time source.
+5) All latencies should be as small as possible(wishful thinking ;)
+
+The code in industrialio-ring.c meets most of these requirements and hopefuly
+does not have any critical failure cases.
+
+A number of pointers into a static array are maintained.
+
+write_p - the next location to write to.
+read_p - the oldest location from which we may read (start point for copying)
+last_written_p - the newest location from which we may read (used to provide
+ direct access whilst the ring buffer is in use, without adding
+ to the communications with the sensor.
+
+half_p - Kept half the length of the buffer behind the write pointer and used
+ in conjunction with read_p to trigger an event when the buffer is half
+ full.
+
+The events are designed to escalate until we reach the point of buffer 100%
+full. Thus a single event has it's code changed when it becomes outdated.
+
+
+The other interesting bit is reading data from the ring. Currently this is via
+normal file reads rather than mmaping the ring buffer.
+
+1) A copy of the ring buffer between read_p and write_p is made. This is done
+without locking the buffer in anyway so the data is not guaranteed to have not
+been changed by subsequent writes.
+
+2) The value of read_p after the copy is used to provide a worst case location
+for where we have clean data from. There is an obvious nasty case of the read
+pointer having wrapped all the way round the buffer. For now we assume the
+capture rate is slow enough that this will not have happened.
+
+Only the valid data is then sent to userspace.
+
+
+What the iio_dev ring parameters are
+
+@ring_bytes_per_datum Number of bytes per 'reading', this includes timestamp
+
+@ring_length Number of readings in the ring
+
+The four functions are concerned with behaviour around the point where the
+device mode actually switches.
+@ring_preenable - usually things like disabling unwanted (sensor side)
+ interrupts.
+
+@ring_postenable - usually actually enabling the data ready generation if
+ appropriate.
+
+@ring_predisable - usually disabling data ready generation
+@ring_postdisable - restoring anything disable before the the ring came into
+ use.
+
+@ring_poll_func - For perioidic timer based rings, this function is called on
+ each timer interrupt. Reads from the device and pushes the
+ data into the ring. Also tends to grab a timestamp at the
+ point likely to be as close as possible to when the data
+ was acquired. Sensor specific offsets can compensate for
+ some fixed lags (particularly at low bus speeds).
--- a/Documentation/industrialio/TestRingMax1363.c 1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/industrialio/TestRingMax1363.c 2008-07-24 18:48:56.000000000 +0100
@@ -0,0 +1,106 @@
+/* Industrialio test ring buffer with a max1238
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ * Little tool to poke character devices associated with ring buffer and see
+ * what events are coming out.
+ *
+ * Todo: Make this more adapatable - e.g. allow specification of which
+ * ring to poke.
+ *
+ * Clearly this needs a lot of work if it going to be a coherent / general
+ * piece of example code.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <linux/types.h>
+
+
+struct iio_event_data {
+ int id;
+ __s64 timestamp;
+};
+int main(int argc, char **argv)
+{
+ FILE *sysfsfp, *fp_ev;
+ int fp;
+ char data[20000];
+ size_t read_size;
+ int i, j, k;
+ int minor, minor_ev;
+ char name[100];
+ char name2[100];
+ char command[100];
+ char temp[100];
+ int pos;
+ struct iio_event_data dat;
+ int device_no;
+
+ if (argc == 1)
+ return -1;
+ device_no = atoi(argv[1]);
+ pos = sprintf(temp, "/sys/class/industrialio/industrialio%d/device/",
+ device_no);
+ sprintf(temp + pos, "ring_buffer0_access_minor");
+ sysfsfp = fopen(temp, "r");
+ if (sysfsfp == NULL) {
+ printf("failed to open minor stuff \n");
+ return -1;
+ }
+
+ fscanf(sysfsfp, "%d\n", &minor);
+ sprintf(name, "/dev/indring%d", minor);
+
+ fclose(sysfsfp);
+ sprintf(temp + pos, "ring_buffer0_ev_minor");
+ sysfsfp = fopen(temp, "r");
+ if (sysfsfp == NULL) {
+ printf("failed to open minor stuff \n");
+ return -1;
+ }
+
+ fscanf(sysfsfp, "%d\n", &minor_ev);
+ fclose(sysfsfp);
+ sprintf(name2, "/dev/indringev%d", minor_ev);
+
+ fp = open(name, O_RDONLY | O_NONBLOCK);
+ if (fp == -1) {
+ sprintf(command, "mknod %s c 244 %d; mknod %s c 244 %d ",
+ name, minor, name2, minor_ev);
+ system(command);
+ fp = open(name, O_RDONLY | O_NONBLOCK);
+ if (fp == -1) {
+ printf("Unable to open %s\n", name);
+ return -1;
+ }
+ }
+ fp_ev = fopen(name2, "rb");
+ if (fp_ev == NULL)
+ printf("bug opening %s\n", name2);
+ /* Wait for events 10 times */
+ for (j = 0; j < 10; j++) {
+ read_size = fread(&dat, 1, sizeof(struct iio_event_data),
+ fp_ev);
+ printf("event code received: %d\n", dat.id);
+ read_size = read(fp, (char *)(data), 100*(12+8));
+ if (read_size == -EAGAIN)
+ printf("nothing available \n");
+
+ /* print a small amount of data */
+ for (i = 0; i < 10; i++) {
+ for (k = 0; k < 12; k++)
+ printf("%d ",
+ ((int)((data[i*32 + (k)*2 + 0]
+ & 0x0F) << 8)
+ + ((int)((data[i*32 + (k)*2 + 1])))));
+ printf(" %lld\n", *(__s64 *)(&data[i*32 + 12*2]));
+ }
+ }
+ return 0;
+}
On Wednesday 2008-07-23 19:00, Jonathan Cameron wrote:
>Dear All,
>
>The need for an industrialio subsystem was discussed in
>http://lkml.org/lkml/2008/5/20/135
>
>Firstly thanks to all the people who have contributed to the discussion
>of this in the past.
>
>In brief the intention is provide a kernel subsystem directed towards the
>handling on sensors (and later related output devices) such as ADC's,
>accelerometers and many others.
Could this be done using UIO instead?
Jan Engelhardt wrote:
> On Wednesday 2008-07-23 19:00, Jonathan Cameron wrote:
>
>
>> Dear All,
>>
>> The need for an industrialio subsystem was discussed in
>> http://lkml.org/lkml/2008/5/20/135
>>
>> Firstly thanks to all the people who have contributed to the discussion
>> of this in the past.
>>
>> In brief the intention is provide a kernel subsystem directed towards the
>> handling on sensors (and later related output devices) such as ADC's,
>> accelerometers and many others.
>>
>
> Could this be done using UIO instead?
>
No, this lot is directed towards devices that use serial buses such as
i2c and SPI whereas
UIO is, from my admittedly cursory knowledge, intended for devices which
can have
their control registers memory mapped into user space.
Looks like I need to be a bit more specific in my descriptions. It's
terrible but as all the
sensors etc I work with are connected like this it never occurred to me
to make it
explicit in the description.
--
Jonathan
On Fri, Jul 25, 2008 at 12:12:36PM +0100, Jonathan Cameron wrote:
> Jan Engelhardt wrote:
> > On Wednesday 2008-07-23 19:00, Jonathan Cameron wrote:
> >
> >
> >> Dear All,
> >>
> >> The need for an industrialio subsystem was discussed in
> >> http://lkml.org/lkml/2008/5/20/135
> >>
> >> Firstly thanks to all the people who have contributed to the discussion
> >> of this in the past.
> >>
> >> In brief the intention is provide a kernel subsystem directed towards the
> >> handling on sensors (and later related output devices) such as ADC's,
> >> accelerometers and many others.
> >>
> >
> > Could this be done using UIO instead?
> >
> No, this lot is directed towards devices that use serial buses such as
> i2c and SPI
[...]
There is spidev driver to do SPI in userspace. But no, it does not mean
that we can always use UIO. For example, we can't use UIO when we want to
process IRQs (think touchscreen pen-down irq), or better, we anyway will
need to write UIO-kernel helper for this.
So, sometimes it's just more convenient to do things in kernel.
--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2