Sorry, forgot to include the headers in the original patch. Obviously these
are very rough and ready at the moment and if nothing else their overall
layout isn't particularly logical.
> Dear All,
>
> This email is mainly to give people an idea of current progress towards
> a new
> subsystem as discussed in the thread starting with
> http://lkml.org/lkml/2008/5/20/135
>
> Sorry for the mass list bombardment, but clearly some elements of this
> discussion
> will end up in various different territories.
>
> Some elements of a prototype subsystem are in place. It draws very heavily
> on parts of the input and hwmon subsystems diverging only where necessary.
>
> The only test driver currently integrated is for an ST LIS3L02DQ
> accelerometer
> which has more than a few quirks to make it tricky to handle (and some what
> sketchy documentation.) More chips will follow over next week or so but
> hopefully the driver for this chip gives enough of an idea of how I envision
> the system working to encourage discussion / advice.
>
> Note that I haven't dealt with anywhere near all the possible locking issues
> etc and am well aware that this needs to be done. Other cleanups that will
> need to be done include working out the layout in sysfs to make it more
> intuitive. Also sorry for the somewhat rough and ready nature of the
> attached
> patch (against 2.6.26-rc4)
>
> Ring buffer design is a large part of the attached patch. I'm not sure if
> I am going about this the right way. Basically, we need ring buffers with
> almost no write latency but can waste plenty of time reading from them
> (in general case - we do however want reading the last available value to be
> fast). What is there works, but probably has at least a few nasty corner
> cases that I haven't prevented.
>
> Interfaces (these are per device) - at somepoint a procfs interface similar
> to that used in the input subsystem would make device querying
> simpler.
>
> Sysfs - Parameter Control - gain / offsets etc
> State control, turn interrupts on and off etc.
> Interrupt control parameters (threshold etc)
> Ring buffer parameters as relevant (currently fixed)
> Individual input reading (acceleration values here)
> Minor numbers for various chrdevs associated with this device.
>
> chrdev- 3 types of chrdev at the moment
> Ring buffer events
> Ring buffer access (currently ripping data off the buffer only)
> Interrupt events - for lis3l02dq these are only threshold breaks
>
> Functionality yet to be implemented.
> Polled based capture (use a peroidic timer if available)
>
> Hardware ring buffering for devices that support it (two level ring
> buffer -
> hard and soft may be appropriate)
>
> A chrdev for polling of whole device (with timestamps etc).
>
> Composite interrupt handling (some devices allow logical combinations
> of different interrupt signals to be used as the trigger condition).
>
> Documenation ;)
>
> Cleaner solution to data alignment in the ring buffer (currently I'm
> cheating
> and manually doing it)
>
> Lots lots more....
>
> Anyhow, all comments welcome. Can anyone think of a better name?
> (I'm not keen on industrialio. It's too long if nothing else!
> It will do as a working title for now)
>
> Thanks,
>
> --
>
> Jonathan Cameron
>
--- a/include/linux/industrialio.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/industrialio.h 2008-06-26 12:10:31.000000000 +0100
@@ -0,0 +1,275 @@
+/* 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 */
+/* Initial test drivers to implement
+ SCA3000 VTI Accelerometers (hardware ring buffers)
+ MAX1363 ADC (polled only for ring buffer )
+
+ Static device specific elements (conversion factors etc) should be exported via sysfs
+
+ Finish writing ring buffer character interface
+ Write general event character interfaces
+ Another type of chardev to allow direct reading (typically in response to data ready events)
+
+
+ Opinions sought on:
+ Shared interrupt lines. Worth dealing with? (very time consuming to check
+ whether some devices caused an interrupt or not - in some states anyway)
+ Limiting length of event lists. Could get silly numbers of them otherwise.
+*/
+
+
+/* Event interface flags */
+#define INDUSTRIALIO_BUSY_BIT_POS 1
+
+
+/* Could maintain a list of these for rapid clean up purposes,
+ but it doesn't exactly take long to scan the array */
+struct industrialio_handler {
+ const struct file_operations *fops;
+ int id;
+ unsigned long flags;
+ void *private;
+};
+
+
+/* The actual event being pushed ot userspace */
+struct industrialio_event_data {
+ int id;
+ s64 timestamp;
+};
+
+/* FIXME -WORK ON NAMING*/
+struct industrialio_detected_event_list {
+ struct list_head list;
+ struct industrialio_event_data ev;
+ /* Part of shared event handling - (typicaly ring buffers) */
+ struct industrialio_shared_ev_pointer *shared_pointer;
+};
+
+
+/* Requires high resolution timers */
+static inline s64 industrialio_get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
+
+struct industrialio_dev;
+/* Each device has one of these per interrupt */
+struct industrialio_event_handler_list {
+ struct list_head list;
+ int (*handler)(struct industrialio_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 */
+/* FIXME CONFUSING NAMING */
+int industrialio_add_event_to_list(struct industrialio_event_handler_list *list,
+ struct industrialio_event_handler_list *el);
+
+int industrialio_remove_event_from_list(struct industrialio_event_handler_list *el);
+
+
+
+/* This means that interrupts can be turned off when no events are being generated,
+ and also provides the interrupt handler the means to identify the incoming event */
+//int industrialio_register_event_list_to_interrupt(int interrupt, industrialio_event_list *list);
+
+/* Want this to be as transparrent as possible from the point of view of the driver! */
+
+/* JIC23: This is my first serious attempt at a lock free ring buffer for this sort of
+ situation so all suggestions on this code particularly welcome! */
+
+
+
+
+struct industrialio_ring_buffer;
+#define INIT_INDUSTRIALIO_RING_BUFFER(ring, _dim, _bytes, _length) { \
+ (ring)->size = _dim*_bytes; \
+ (ring)->skip = (ring)->size + sizeof(s64); \
+ (ring)->length = _length; \
+ (ring)->dimension = _dim; \
+ (ring)->bytes = _bytes; \
+ (ring)->read_p = 0; \
+ (ring)->write_p = 0; \
+ (ring)->last_written_p = 0; \
+ (ring)->loopcount = 0; \
+ (ring)->data \
+ = (unsigned char*) \
+ (kmalloc(_length*(ring)->skip, \
+ GFP_KERNEL)); \
+ (ring)->shared_ev_pointer.ev_p =0; \
+ (ring)->shared_ev_pointer.lock = \
+ __SPIN_LOCK_UNLOCKED((ring)->shared_ev_pointer->loc); \
+}
+
+#define FREE_INDUSTRIALIO_RING_BUFFER(ring) \
+ kfree((ring)->data)
+
+int industrialio_store_to_ring(struct industrialio_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 industrialio_read_last_from_ring(struct industrialio_ring_buffer *ring,
+ unsigned char* data);
+/* Dump the ring */
+
+int
+industrialio_request_ring_buffer(int dimension,
+ int bytes_per_reading,
+ int length,
+ struct industrialio_ring_buffer **ring,
+ int id,
+ struct module *owner,
+ struct device *dev );
+
+
+void industrialio_free_ring_buffer(struct industrialio_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 industrialio_event_interface {
+ struct industrialio_handler handler;
+ wait_queue_head_t wait;
+ struct industrialio_detected_event_list det_events;
+ int max_events;
+ int current_events;
+ /* Integer id, used to differentiate this one form any others */
+ int id;
+ struct industrialio_chrdev_minor_attr attr;
+ struct module *owner;
+ void *private;
+};
+
+
+struct industrialio_shared_ev_pointer {
+ struct industrialio_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 industrialio_ring_buffer
+{
+ unsigned char* data;
+ int length;
+ int dimension;
+ int bytes;
+ int size;
+ int skip;
+ 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 loopcount;
+ /* accessing the ring buffer */
+ char* access_minor_name;
+ struct industrialio_chrdev_minor_attr access_minor_attr;
+ struct industrialio_handler access_handler;
+ /* events triggered by the ring buffer */
+ char* event_minor_name;
+ struct industrialio_event_interface ev_int;
+ /* a fully shared output event */
+ struct industrialio_shared_ev_pointer shared_ev_pointer;
+};
+/* Seperate registration functions were leading to very messy driver init */
+/* Vast majority of this is set by the industrialio subsystem.
+ * FIXME: Add a macro to set only the relevant stuff within a chip driver
+ */
+struct industrialio_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 */
+ /* FIXME: GETTING MESSY! */
+ struct module *driver_module;
+ int num_interrupt_lines; /* Driver Set */
+
+ struct industrialio_interrupt **interrupts;
+
+
+ /* Event control attributes */
+ const struct attribute_group *event_attrs;
+ /* The character device related elements */
+ struct industrialio_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_reading;
+ int ring_length;
+ struct industrialio_ring_buffer *ring;
+ struct attribute_group *ring_attrs_group;
+ struct industrialio_ring_attr *ring_attrs;
+};
+
+int industrialio_device_register(struct industrialio_dev *dev_info);
+
+void industrialio_device_unregister(struct industrialio_dev *dev_info);
+
+/* Wrapper class used to allow easy specification of different line numbers */
+struct industrialio_interrupt {
+ struct industrialio_dev *dev_info;
+ int line_number;
+ int irq;
+ struct industrialio_event_handler_list ev_list;
+};
+
+irqreturn_t industrialio_interrupt_handler(int irq, void *_int_info);
+
+int industrialio_register_interrupt_line(unsigned int irq,
+ struct industrialio_dev *dev_info,
+ int line_number,
+ unsigned long type,
+ const char *name);
+
+void industrialio_unregister_interrupt_line(struct industrialio_dev *dev_info,
+ int line_number);
+
+
+/* Used to try inserting an event into the list for userspace reading via
+ * chrdev */
+int industrialio_put_event(struct industrialio_dev *dev_info,
+ int ev_line,
+ int ev_code,
+ s64 timestamp);
+#endif /* _INDUSTRIAL_IO_H_ */
--- a/include/linux/industrialio_sysfs.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/industrialio_sysfs.h 2008-06-26 16:44:50.000000000 +0100
@@ -0,0 +1,207 @@
+/* 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 industrialio_event_attr {
+ struct device_attribute dev_attr;
+ int mask;
+ struct industrialio_event_handler_list *listel;
+};
+
+
+#define to_industrialio_event_attr(_dev_attr) \
+ container_of(_dev_attr, struct industrialio_event_attr, dev_attr)
+
+
+struct industrialio_chrdev_minor_attr {
+ struct device_attribute dev_attr;
+ int minor;
+};
+
+#define to_industrialio_chrdev_minor_attr(_dev_attr) \
+ container_of(_dev_attr, struct industrialio_chrdev_minor_attr, dev_attr);
+
+struct industrialio_dev_attr {
+ struct device_attribute dev_attr;
+ int address;
+};
+
+
+#define to_industrialio_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct industrialio_dev_attr, dev_attr)
+
+/* Some attributes will be hard coded (device dependant) and not require an
+ address, in these cases pass a negative */
+#define INDUSTRIALIO_ATTR(_name, _mode, _show, _store, _addr) \
+ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
+ .address = _addr }
+
+#define INDUSTRIALIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \
+ struct industrialio_dev_attr industrialio_dev_attr_##_name \
+ = INDUSTRIALIO_ATTR(_name, _mode, _show, _store, _addr)
+
+/* This may get broken down into separate files later */
+
+/* For devices with internal clocks - and possibly poling later */
+
+#define INDUSTRIALIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store) \
+ INDUSTRIALIO_DEVICE_ATTR(sampling_frequency, _mode, \
+ _show, _store, 0)
+
+#define INDUSTRIALIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show)\
+ INDUSTRIALIO_DEVICE_ATTR(available_sampling_frequency, \
+ S_IRUGO, _show, NULL, 0)
+
+/* Accelerometer types of attribute */
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(x_offset, _mode, _show, _store, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(y_offset, _mode, _show, _store, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(z_offset, _mode, _show, _store, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(x_gain, _mode, _show, _store, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(y_gain, _mode, _show, _store, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(z_gain, _mode, _show, _store, _addr)
+
+
+/* The actual device readings are always going to be read only */
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_X(_show, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(x, S_IRUGO, _show, NULL, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_Y(_show, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(y, S_IRUGO, _show, NULL, _addr)
+
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_Z(_show, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(z, S_IRUGO, _show, NULL, _addr)
+
+/* Thresholds are somewhat chip dependent - may need quite a few defs here */
+#define INDUSTRIALIO_DEV_ATTR_ACCEL_THRESH(_mode, _show, _store, _addr) \
+ INDUSTRIALIO_DEVICE_ATTR(thresh, _mode, _show, _store, _addr)
+
+
+
+/* Events that the device may generate */
+/* How to do this. Is it valid to have sysfs elements which can be neither
+ read nor written? */
+/* GOING TO NEED a usage count */
+#define INDUSTRIALIO_EVENT_SH(_name, _handler) \
+ static struct industrialio_event_handler_list \
+ industrialio_event_##_name = { \
+ .handler=_handler, \
+ .refcount = 0, \
+ };
+#define INDUSTRIALIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask) \
+ static struct industrialio_event_attr \
+ industrialio_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 INDUSTRIALIO_EVENT_ATTR(_name, _show, _store, _mask, _handler) \
+ static struct industrialio_event_handler_list \
+ industrialio_event_##_name = { \
+ .handler=_handler, \
+ }; \
+ static struct \
+ industrialio_event_attr \
+ industrialio_event_attr_##_name \
+ = { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR, _show, _store), \
+ .mask = _mask, \
+ .listel = &industrialio_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 INDUSTRIALIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_CODE_DATA_RDY 100
+
+/* Threshold pass events */
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_X_HIGH(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(x_high, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_CODE_ACCEL_X_HIGH 1
+
+/* Shared handler version */
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_X_HIGH_SH(_evlist, _show, _store, _mask)\
+ INDUSTRIALIO_EVENT_ATTR_SH(x_high, _evlist, _show, _store, _mask)
+
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Y_HIGH(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(y_high, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(_evlist, _show, _store, _mask)\
+ INDUSTRIALIO_EVENT_ATTR_SH(y_high, _evlist, _show, _store, _mask)
+
+#define INDUSTRIALIO_EVENT_CODE_ACCEL_Y_HIGH 2
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Z_HIGH(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(z_high, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(_evlist, _show, _store, _mask)\
+ INDUSTRIALIO_EVENT_ATTR_SH(z_high, _evlist, _show, _store, _mask)
+
+#define INDUSTRIALIO_EVENT_CODE_ACCEL_Z_HIGH 3
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_X_LOW(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(x_low, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_X_LOW_SH(_evlist, _show, _store, _mask)\
+ INDUSTRIALIO_EVENT_ATTR_SH(x_low, _evlist, _show, _store, _mask)
+
+#define INDUSTRIALIO_EVENT_CODE_ACCEL_X_LOW 4
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Y_LOW(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(y_low, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Y_LOW_SH(_evlist,_show, _store, _mask)\
+ INDUSTRIALIO_EVENT_ATTR_SH(y_low, _evlist, _show, _store, _mask)
+
+#define INDUSTRIALIO_EVENT_CODE_ACCEL_Y_LOW 5
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Z_LOW(_show, _store, _mask, _handler) \
+ INDUSTRIALIO_EVENT_ATTR(z_low, _show, _store, _mask, _handler)
+
+#define INDUSTRIALIO_EVENT_ATTR_ACCEL_Z_LOW_SH(_evlist, _show, _store, _mask)\
+ INDUSTRIALIO_EVENT_ATTR_SH(z_low, _evlist, _show, _store, _mask)
+
+#define INDUSTRIALIO_EVENT_CODE_ACCEL_Z_LOW 6
+
+
+#define INDUSTRIALIO_EVENT_CODE_RING_50_FULL 100
+#define INDUSTRIALIO_EVENT_CODE_RING_100_FULL 101
+/* HOW TO HANDLE COMPOSITE EVENTS? */
+
+
+
+
+/* function that takes a list of these and puts them in an events directory? */
+
+#endif /* _INDUSTRIAL_IO_SYSFS_H_ */
--- a/include/linux/spi/lis3l02dq.h 1970-01-01 01:00:00.000000000 +0100
+++ b/include/linux/spi/lis3l02dq.h 2008-05-27 20:18:00.000000000 +0100
@@ -0,0 +1,6 @@
+
+
+struct LIS3L02DQ_platform_data {
+ unsigned data_ready_gpio;
+};
+
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general