summaryrefslogtreecommitdiffstats
path: root/drivers/staging/iio/industrialio-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/iio/industrialio-core.c')
-rw-r--r--drivers/staging/iio/industrialio-core.c961
1 files changed, 438 insertions, 523 deletions
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index 19819e7..326e967 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -21,21 +21,17 @@
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/slab.h>
+#include <linux/anon_inodes.h>
#include "iio.h"
-#include "trigger_consumer.h"
+#include "iio_core.h"
+#include "iio_core_trigger.h"
+#include "chrdev.h"
+#include "sysfs.h"
-#define IIO_ID_PREFIX "device"
-#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
-
-/* IDR to assign each registered device a unique id*/
+/* IDA to assign each registered device a unique id*/
static DEFINE_IDA(iio_ida);
-/* IDR to allocate character device minor numbers */
-static DEFINE_IDA(iio_chrdev_ida);
-/* Lock used to protect both of the above */
-static DEFINE_SPINLOCK(iio_ida_lock);
-dev_t iio_devt;
-EXPORT_SYMBOL(iio_devt);
+static dev_t iio_devt;
#define IIO_DEV_MAX 256
struct bus_type iio_bus_type = {
@@ -43,14 +39,22 @@ struct bus_type iio_bus_type = {
};
EXPORT_SYMBOL(iio_bus_type);
-static const char * const iio_chan_type_name_spec_shared[] = {
- [IIO_IN] = "in",
- [IIO_OUT] = "out",
+static const char * const iio_data_type_name[] = {
+ [IIO_RAW] = "raw",
+ [IIO_PROCESSED] = "input",
+};
+
+static const char * const iio_direction[] = {
+ [0] = "in",
+ [1] = "out",
+};
+
+static const char * const iio_chan_type_name_spec[] = {
+ [IIO_VOLTAGE] = "voltage",
[IIO_CURRENT] = "current",
[IIO_POWER] = "power",
[IIO_ACCEL] = "accel",
- [IIO_IN_DIFF] = "in-in",
- [IIO_GYRO] = "gyro",
+ [IIO_ANGL_VEL] = "anglvel",
[IIO_MAGN] = "magn",
[IIO_LIGHT] = "illuminance",
[IIO_INTENSITY] = "intensity",
@@ -60,21 +64,15 @@ static const char * const iio_chan_type_name_spec_shared[] = {
[IIO_ROT] = "rot",
[IIO_ANGL] = "angl",
[IIO_TIMESTAMP] = "timestamp",
+ [IIO_CAPACITANCE] = "capacitance",
};
-static const char * const iio_chan_type_name_spec_complex[] = {
- [IIO_IN_DIFF] = "in%d-in%d",
-};
-
-static const char * const iio_modifier_names_light[] = {
- [IIO_MOD_LIGHT_BOTH] = "both",
- [IIO_MOD_LIGHT_IR] = "ir",
-};
-
-static const char * const iio_modifier_names_axial[] = {
+static const char * const iio_modifier_names[] = {
[IIO_MOD_X] = "x",
[IIO_MOD_Y] = "y",
[IIO_MOD_Z] = "z",
+ [IIO_MOD_LIGHT_BOTH] = "both",
+ [IIO_MOD_LIGHT_IR] = "ir",
};
/* relies on pairs of these shared then separate */
@@ -85,21 +83,51 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
[IIO_CHAN_INFO_PEAK_SHARED/2] = "peak_raw",
[IIO_CHAN_INFO_PEAK_SCALE_SHARED/2] = "peak_scale",
+ [IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED/2]
+ = "quadrature_correction_raw",
+ [IIO_CHAN_INFO_AVERAGE_RAW_SHARED/2] = "mean_raw",
};
-int iio_push_event(struct iio_dev *dev_info,
- int ev_line,
- int ev_code,
- s64 timestamp)
+/**
+ * struct iio_detected_event_list - list element for events that have occurred
+ * @list: linked list header
+ * @ev: the event itself
+ */
+struct iio_detected_event_list {
+ struct list_head list;
+ struct iio_event_data ev;
+};
+
+/**
+ * struct iio_event_interface - chrdev interface for an event line
+ * @dev: device assocated with event interface
+ * @wait: wait queue to allow blocking reads of events
+ * @event_list_lock: mutex to protect the list of detected events
+ * @det_events: list of detected events
+ * @max_events: maximum number of events before new ones are dropped
+ * @current_events: number of events in detected list
+ * @flags: file operations related flags including busy flag.
+ */
+struct iio_event_interface {
+ wait_queue_head_t wait;
+ struct mutex event_list_lock;
+ struct list_head det_events;
+ int max_events;
+ int current_events;
+ struct list_head dev_attr_list;
+ unsigned long flags;
+ struct attribute_group group;
+};
+
+int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
{
- struct iio_event_interface *ev_int
- = &dev_info->event_interfaces[ev_line];
+ struct iio_event_interface *ev_int = indio_dev->event_interface;
struct iio_detected_event_list *ev;
int ret = 0;
/* Does anyone care? */
mutex_lock(&ev_int->event_list_lock);
- if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) {
+ if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
if (ev_int->current_events == ev_int->max_events) {
mutex_unlock(&ev_int->event_list_lock);
return 0;
@@ -125,7 +153,6 @@ error_ret:
}
EXPORT_SYMBOL(iio_push_event);
-
/* This turns up an awful lot */
ssize_t iio_read_const_attr(struct device *dev,
struct device_attribute *attr,
@@ -135,7 +162,6 @@ ssize_t iio_read_const_attr(struct device *dev,
}
EXPORT_SYMBOL(iio_read_const_attr);
-
static ssize_t iio_event_chrdev_read(struct file *filep,
char __user *buf,
size_t count,
@@ -187,12 +213,11 @@ error_ret:
static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
{
- struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev);
- struct iio_event_interface *ev_int = hand->private;
+ struct iio_event_interface *ev_int = filep->private_data;
struct iio_detected_event_list *el, *t;
mutex_lock(&ev_int->event_list_lock);
- clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags);
+ clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
/*
* In order to maintain a clean state for reopening,
* clear out any awaiting events. The mask will prevent
@@ -202,23 +227,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
list_del(&el->list);
kfree(el);
}
- mutex_unlock(&ev_int->event_list_lock);
-
- return 0;
-}
-
-static int iio_event_chrdev_open(struct inode *inode, struct file *filep)
-{
- struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev);
- struct iio_event_interface *ev_int = hand->private;
-
- mutex_lock(&ev_int->event_list_lock);
- if (test_and_set_bit(IIO_BUSY_BIT_POS, &hand->flags)) {
- fops_put(filep->f_op);
- mutex_unlock(&ev_int->event_list_lock);
- return -EBUSY;
- }
- filep->private_data = hand->private;
+ ev_int->current_events = 0;
mutex_unlock(&ev_int->event_list_lock);
return 0;
@@ -227,124 +236,25 @@ static int iio_event_chrdev_open(struct inode *inode, struct file *filep)
static const struct file_operations iio_event_chrdev_fileops = {
.read = iio_event_chrdev_read,
.release = iio_event_chrdev_release,
- .open = iio_event_chrdev_open,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
-static void iio_event_dev_release(struct device *dev)
+static int iio_event_getfd(struct iio_dev *indio_dev)
{
- struct iio_event_interface *ev_int
- = container_of(dev, struct iio_event_interface, dev);
- cdev_del(&ev_int->handler.chrdev);
- iio_device_free_chrdev_minor(MINOR(dev->devt));
-};
-
-static struct device_type iio_event_type = {
- .release = iio_event_dev_release,
-};
-
-int iio_device_get_chrdev_minor(void)
-{
- int ret, val;
-
-ida_again:
- if (unlikely(ida_pre_get(&iio_chrdev_ida, GFP_KERNEL) == 0))
- return -ENOMEM;
- spin_lock(&iio_ida_lock);
- ret = ida_get_new(&iio_chrdev_ida, &val);
- spin_unlock(&iio_ida_lock);
- if (unlikely(ret == -EAGAIN))
- goto ida_again;
- else if (unlikely(ret))
- return ret;
- if (val > IIO_DEV_MAX)
- return -ENOMEM;
- return val;
-}
-
-void iio_device_free_chrdev_minor(int val)
-{
- spin_lock(&iio_ida_lock);
- ida_remove(&iio_chrdev_ida, val);
- spin_unlock(&iio_ida_lock);
-}
+ if (indio_dev->event_interface == NULL)
+ return -ENODEV;
-static int iio_setup_ev_int(struct iio_event_interface *ev_int,
- const char *dev_name,
- int index,
- struct module *owner,
- struct device *dev)
-{
- int ret, minor;
-
- ev_int->dev.bus = &iio_bus_type;
- ev_int->dev.parent = dev;
- ev_int->dev.type = &iio_event_type;
- device_initialize(&ev_int->dev);
-
- minor = iio_device_get_chrdev_minor();
- if (minor < 0) {
- ret = minor;
- goto error_device_put;
+ mutex_lock(&indio_dev->event_interface->event_list_lock);
+ if (test_and_set_bit(IIO_BUSY_BIT_POS,
+ &indio_dev->event_interface->flags)) {
+ mutex_unlock(&indio_dev->event_interface->event_list_lock);
+ return -EBUSY;
}
- ev_int->dev.devt = MKDEV(MAJOR(iio_devt), minor);
- dev_set_name(&ev_int->dev, "%s:event%d", dev_name, index);
-
- ret = device_add(&ev_int->dev);
- if (ret)
- goto error_free_minor;
-
- cdev_init(&ev_int->handler.chrdev, &iio_event_chrdev_fileops);
- ev_int->handler.chrdev.owner = owner;
-
- 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);
- init_waitqueue_head(&ev_int->wait);
- ev_int->handler.private = ev_int;
- ev_int->handler.flags = 0;
-
- ret = cdev_add(&ev_int->handler.chrdev, ev_int->dev.devt, 1);
- if (ret)
- goto error_unreg_device;
-
- return 0;
-
-error_unreg_device:
- device_unregister(&ev_int->dev);
-error_free_minor:
- iio_device_free_chrdev_minor(minor);
-error_device_put:
- put_device(&ev_int->dev);
-
- return ret;
-}
-
-static void iio_free_ev_int(struct iio_event_interface *ev_int)
-{
- device_unregister(&ev_int->dev);
- put_device(&ev_int->dev);
-}
-
-static int __init iio_dev_init(void)
-{
- int err;
-
- err = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
- if (err < 0)
- printk(KERN_ERR "%s: failed to allocate char dev region\n",
- __FILE__);
-
- return err;
-}
-
-static void __exit iio_dev_exit(void)
-{
- if (iio_devt)
- unregister_chrdev_region(iio_devt, IIO_DEV_MAX);
+ mutex_unlock(&indio_dev->event_interface->event_list_lock);
+ return anon_inode_getfd("iio:event",
+ &iio_event_chrdev_fileops,
+ indio_dev->event_interface, O_RDONLY);
}
static int __init iio_init(void)
@@ -360,9 +270,12 @@ static int __init iio_init(void)
goto error_nothing;
}
- ret = iio_dev_init();
- if (ret < 0)
+ ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to allocate char dev region\n",
+ __FILE__);
goto error_unregister_bus_type;
+ }
return 0;
@@ -374,7 +287,8 @@ error_nothing:
static void __exit iio_exit(void)
{
- iio_dev_exit();
+ if (iio_devt)
+ unregister_chrdev_region(iio_devt, IIO_DEV_MAX);
bus_unregister(&iio_bus_type);
}
@@ -476,54 +390,7 @@ static ssize_t iio_write_channel_info(struct device *dev,
return len;
}
-static int __iio_build_postfix(struct iio_chan_spec const *chan,
- bool generic,
- const char *postfix,
- char **result)
-{
- char *all_post;
- /* 3 options - generic, extend_name, modified - if generic, extend_name
- * and modified cannot apply.*/
-
- if (generic || (!chan->modified && !chan->extend_name)) {
- all_post = kasprintf(GFP_KERNEL, "%s", postfix);
- } else if (chan->modified) {
- const char *intermediate;
- switch (chan->type) {
- case IIO_INTENSITY:
- intermediate
- = iio_modifier_names_light[chan->channel2];
- break;
- case IIO_ACCEL:
- case IIO_GYRO:
- case IIO_MAGN:
- case IIO_INCLI:
- case IIO_ROT:
- case IIO_ANGL:
- intermediate
- = iio_modifier_names_axial[chan->channel2];
- break;
- default:
- return -EINVAL;
- }
- if (chan->extend_name)
- all_post = kasprintf(GFP_KERNEL, "%s_%s_%s",
- intermediate,
- chan->extend_name,
- postfix);
- else
- all_post = kasprintf(GFP_KERNEL, "%s_%s",
- intermediate,
- postfix);
- } else
- all_post = kasprintf(GFP_KERNEL, "%s_%s", chan->extend_name,
- postfix);
- if (all_post == NULL)
- return -ENOMEM;
- *result = all_post;
- return 0;
-}
-
+static
int __iio_device_attr_init(struct device_attribute *dev_attr,
const char *postfix,
struct iio_chan_spec const *chan,
@@ -539,28 +406,77 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
int ret;
char *name_format, *full_postfix;
sysfs_attr_init(&dev_attr->attr);
- ret = __iio_build_postfix(chan, generic, postfix, &full_postfix);
- if (ret)
- goto error_ret;
- /* Special case for types that uses both channel numbers in naming */
- if (chan->type == IIO_IN_DIFF && !generic)
- name_format
- = kasprintf(GFP_KERNEL, "%s_%s",
- iio_chan_type_name_spec_complex[chan->type],
- full_postfix);
- else if (generic || !chan->indexed)
- name_format
- = kasprintf(GFP_KERNEL, "%s_%s",
- iio_chan_type_name_spec_shared[chan->type],
- full_postfix);
- else
- name_format
- = kasprintf(GFP_KERNEL, "%s%d_%s",
- iio_chan_type_name_spec_shared[chan->type],
- chan->channel,
- full_postfix);
+ /* Build up postfix of <extend_name>_<modifier>_postfix */
+ if (chan->modified) {
+ if (chan->extend_name)
+ full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_modifier_names[chan
+ ->channel2],
+ chan->extend_name,
+ postfix);
+ else
+ full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_modifier_names[chan
+ ->channel2],
+ postfix);
+ } else {
+ if (chan->extend_name == NULL)
+ full_postfix = kstrdup(postfix, GFP_KERNEL);
+ else
+ full_postfix = kasprintf(GFP_KERNEL,
+ "%s_%s",
+ chan->extend_name,
+ postfix);
+ }
+ if (full_postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ if (chan->differential) { /* Differential can not have modifier */
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ else if (chan->indexed)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ iio_chan_type_name_spec[chan->type],
+ chan->channel2,
+ full_postfix);
+ else {
+ WARN_ON("Differential channels must be indexed\n");
+ ret = -EINVAL;
+ goto error_free_full_postfix;
+ }
+ } else { /* Single ended */
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ else if (chan->indexed)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ full_postfix);
+ else
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ }
if (name_format == NULL) {
ret = -ENOMEM;
goto error_free_full_postfix;
@@ -596,13 +512,12 @@ error_ret:
return ret;
}
-void __iio_device_attr_deinit(struct device_attribute *dev_attr)
+static void __iio_device_attr_deinit(struct device_attribute *dev_attr)
{
kfree(dev_attr->attr.name);
}
int __iio_add_chan_devattr(const char *postfix,
- const char *group,
struct iio_chan_spec const *chan,
ssize_t (*readfunc)(struct device *dev,
struct device_attribute *attr,
@@ -611,7 +526,7 @@ int __iio_add_chan_devattr(const char *postfix,
struct device_attribute *attr,
const char *buf,
size_t len),
- int mask,
+ u64 mask,
bool generic,
struct device *dev,
struct list_head *attr_list)
@@ -640,12 +555,6 @@ int __iio_add_chan_devattr(const char *postfix,
ret = -EBUSY;
goto error_device_attr_deinit;
}
-
- ret = sysfs_add_file_to_group(&dev->kobj,
- &iio_attr->dev_attr.attr, group);
- if (ret < 0)
- goto error_device_attr_deinit;
-
list_add(&iio_attr->l, attr_list);
return 0;
@@ -658,59 +567,52 @@ error_ret:
return ret;
}
-static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
+static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
- int ret, i;
-
+ int ret, i, attrcount = 0;
if (chan->channel < 0)
return 0;
- if (chan->processed_val)
- ret = __iio_add_chan_devattr("input", NULL, chan,
- &iio_read_channel_info,
- NULL,
- 0,
- 0,
- &dev_info->dev,
- &dev_info->channel_attr_list);
- else
- ret = __iio_add_chan_devattr("raw", NULL, chan,
- &iio_read_channel_info,
- (chan->type == IIO_OUT ?
- &iio_write_channel_info : NULL),
- 0,
- 0,
- &dev_info->dev,
- &dev_info->channel_attr_list);
+
+ ret = __iio_add_chan_devattr(iio_data_type_name[chan->processed_val],
+ chan,
+ &iio_read_channel_info,
+ (chan->output ?
+ &iio_write_channel_info : NULL),
+ 0,
+ 0,
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
if (ret)
goto error_ret;
+ attrcount++;
for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) {
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
- NULL, chan,
+ chan,
&iio_read_channel_info,
&iio_write_channel_info,
(1 << i),
!(i%2),
- &dev_info->dev,
- &dev_info->channel_attr_list);
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
if (ret == -EBUSY && (i%2 == 0)) {
ret = 0;
continue;
}
if (ret < 0)
goto error_ret;
+ attrcount++;
}
+ ret = attrcount;
error_ret:
return ret;
}
-static void iio_device_remove_and_free_read_attr(struct iio_dev *dev_info,
+static void iio_device_remove_and_free_read_attr(struct iio_dev *indio_dev,
struct iio_dev_attr *p)
{
- sysfs_remove_file_from_group(&dev_info->dev.kobj,
- &p->dev_attr.attr, NULL);
kfree(p->dev_attr.attr.name);
kfree(p);
}
@@ -725,107 +627,91 @@ static ssize_t iio_show_dev_name(struct device *dev,
static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
-static int iio_device_register_sysfs(struct iio_dev *dev_info)
+static int iio_device_register_sysfs(struct iio_dev *indio_dev)
{
- int i, ret = 0;
+ int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
struct iio_dev_attr *p, *n;
+ struct attribute **attr;
- if (dev_info->info->attrs) {
- ret = sysfs_create_group(&dev_info->dev.kobj,
- dev_info->info->attrs);
- if (ret) {
- dev_err(dev_info->dev.parent,
- "Failed to register sysfs hooks\n");
- goto error_ret;
- }
+ /* First count elements in any existing group */
+ if (indio_dev->info->attrs) {
+ attr = indio_dev->info->attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
}
-
+ attrcount = attrcount_orig;
/*
* New channel registration method - relies on the fact a group does
* not need to be initialized if it is name is NULL.
*/
- INIT_LIST_HEAD(&dev_info->channel_attr_list);
- if (dev_info->channels)
- for (i = 0; i < dev_info->num_channels; i++) {
- ret = iio_device_add_channel_sysfs(dev_info,
- &dev_info
+ INIT_LIST_HEAD(&indio_dev->channel_attr_list);
+ if (indio_dev->channels)
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = iio_device_add_channel_sysfs(indio_dev,
+ &indio_dev
->channels[i]);
if (ret < 0)
goto error_clear_attrs;
+ attrcount += ret;
}
- if (dev_info->name) {
- ret = sysfs_add_file_to_group(&dev_info->dev.kobj,
- &dev_attr_name.attr,
- NULL);
- if (ret)
- goto error_clear_attrs;
+
+ if (indio_dev->name)
+ attrcount++;
+
+ indio_dev->chan_attr_group.attrs
+ = kzalloc(sizeof(indio_dev->chan_attr_group.attrs[0])*
+ (attrcount + 1),
+ GFP_KERNEL);
+ if (indio_dev->chan_attr_group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_attrs;
}
+ /* Copy across original attributes */
+ if (indio_dev->info->attrs)
+ memcpy(indio_dev->chan_attr_group.attrs,
+ indio_dev->info->attrs->attrs,
+ sizeof(indio_dev->chan_attr_group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p, &indio_dev->channel_attr_list, l)
+ indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
+ if (indio_dev->name)
+ indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+
+ indio_dev->groups[indio_dev->groupcounter++] =
+ &indio_dev->chan_attr_group;
+
return 0;
error_clear_attrs:
list_for_each_entry_safe(p, n,
- &dev_info->channel_attr_list, l) {
+ &indio_dev->channel_attr_list, l) {
list_del(&p->l);
- iio_device_remove_and_free_read_attr(dev_info, p);
+ iio_device_remove_and_free_read_attr(indio_dev, p);
}
- if (dev_info->info->attrs)
- sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs);
-error_ret:
- return ret;
+ return ret;
}
-static void iio_device_unregister_sysfs(struct iio_dev *dev_info)
+static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
{
struct iio_dev_attr *p, *n;
- if (dev_info->name)
- sysfs_remove_file_from_group(&dev_info->dev.kobj,
- &dev_attr_name.attr,
- NULL);
- list_for_each_entry_safe(p, n, &dev_info->channel_attr_list, l) {
+
+ list_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) {
list_del(&p->l);
- iio_device_remove_and_free_read_attr(dev_info, p);
+ iio_device_remove_and_free_read_attr(indio_dev, p);
}
-
- if (dev_info->info->attrs)
- sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs);
-}
-
-/* Return a negative errno on failure */
-int iio_get_new_ida_val(struct ida *this_ida)
-{
- int ret;
- int val;
-
-ida_again:
- if (unlikely(ida_pre_get(this_ida, GFP_KERNEL) == 0))
- return -ENOMEM;
-
- spin_lock(&iio_ida_lock);
- ret = ida_get_new(this_ida, &val);
- spin_unlock(&iio_ida_lock);
- if (unlikely(ret == -EAGAIN))
- goto ida_again;
- else if (unlikely(ret))
- return ret;
-
- return val;
+ kfree(indio_dev->chan_attr_group.attrs);
}
-EXPORT_SYMBOL(iio_get_new_ida_val);
-
-void iio_free_ida_val(struct ida *this_ida, int id)
-{
- spin_lock(&iio_ida_lock);
- ida_remove(this_ida, id);
- spin_unlock(&iio_ida_lock);
-}
-EXPORT_SYMBOL(iio_free_ida_val);
static const char * const iio_ev_type_text[] = {
[IIO_EV_TYPE_THRESH] = "thresh",
[IIO_EV_TYPE_MAG] = "mag",
- [IIO_EV_TYPE_ROC] = "roc"
+ [IIO_EV_TYPE_ROC] = "roc",
+ [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
+ [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
};
static const char * const iio_ev_dir_text[] = {
@@ -907,230 +793,214 @@ static ssize_t iio_ev_value_store(struct device *dev,
return len;
}
-static int iio_device_add_event_sysfs(struct iio_dev *dev_info,
+static int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
-
- int ret = 0, i, mask;
+ int ret = 0, i, attrcount = 0;
+ u64 mask = 0;
char *postfix;
if (!chan->event_mask)
return 0;
for_each_set_bit(i, &chan->event_mask, sizeof(chan->event_mask)*8) {
postfix = kasprintf(GFP_KERNEL, "%s_%s_en",
- iio_ev_type_text[i/IIO_EV_TYPE_MAX],
- iio_ev_dir_text[i%IIO_EV_TYPE_MAX]);
+ iio_ev_type_text[i/IIO_EV_DIR_MAX],
+ iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
if (postfix == NULL) {
ret = -ENOMEM;
goto error_ret;
}
- switch (chan->type) {
- /* Switch this to a table at some point */
- case IIO_IN:
- mask = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
- i/IIO_EV_TYPE_MAX,
- i%IIO_EV_TYPE_MAX);
- break;
- case IIO_ACCEL:
+ if (chan->modified)
mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel,
- i/IIO_EV_TYPE_MAX,
- i%IIO_EV_TYPE_MAX);
- break;
- case IIO_IN_DIFF:
- mask = IIO_MOD_EVENT_CODE(chan->type, chan->channel,
- chan->channel2,
- i/IIO_EV_TYPE_MAX,
- i%IIO_EV_TYPE_MAX);
- break;
- default:
- printk(KERN_INFO "currently unhandled type of event\n");
- }
+ i/IIO_EV_DIR_MAX,
+ i%IIO_EV_DIR_MAX);
+ else if (chan->differential)
+ mask = IIO_EVENT_CODE(chan->type,
+ 0, 0,
+ i%IIO_EV_DIR_MAX,
+ i/IIO_EV_DIR_MAX,
+ 0,
+ chan->channel,
+ chan->channel2);
+ else
+ mask = IIO_UNMOD_EVENT_CODE(chan->type,
+ chan->channel,
+ i/IIO_EV_DIR_MAX,
+ i%IIO_EV_DIR_MAX);
+
ret = __iio_add_chan_devattr(postfix,
- NULL,
chan,
&iio_ev_state_show,
iio_ev_state_store,
mask,
- /*HACK. - limits us to one
- event interface - fix by
- extending the bitmask - but
- how far*/
0,
- &dev_info->event_interfaces[0].dev,
- &dev_info->event_interfaces[0].
+ &indio_dev->dev,
+ &indio_dev->event_interface->
dev_attr_list);
kfree(postfix);
if (ret)
goto error_ret;
-
+ attrcount++;
postfix = kasprintf(GFP_KERNEL, "%s_%s_value",
- iio_ev_type_text[i/IIO_EV_TYPE_MAX],
- iio_ev_dir_text[i%IIO_EV_TYPE_MAX]);
+ iio_ev_type_text[i/IIO_EV_DIR_MAX],
+ iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
if (postfix == NULL) {
ret = -ENOMEM;
goto error_ret;
}
- ret = __iio_add_chan_devattr(postfix, NULL, chan,
+ ret = __iio_add_chan_devattr(postfix, chan,
iio_ev_value_show,
iio_ev_value_store,
mask,
0,
- &dev_info->event_interfaces[0]
- .dev,
- &dev_info->event_interfaces[0]
- .dev_attr_list);
+ &indio_dev->dev,
+ &indio_dev->event_interface->
+ dev_attr_list);
kfree(postfix);
if (ret)
goto error_ret;
-
+ attrcount++;
}
-
+ ret = attrcount;
error_ret:
return ret;
}
-static inline void __iio_remove_all_event_sysfs(struct iio_dev *dev_info,
- const char *groupname,
- int num)
+static inline void __iio_remove_event_config_attrs(struct iio_dev *indio_dev)
{
struct iio_dev_attr *p, *n;
list_for_each_entry_safe(p, n,
- &dev_info->event_interfaces[num].
+ &indio_dev->event_interface->
dev_attr_list, l) {
- sysfs_remove_file_from_group(&dev_info
- ->event_interfaces[num].dev.kobj,
- &p->dev_attr.attr,
- groupname);
kfree(p->dev_attr.attr.name);
kfree(p);
}
}
-static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
+static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
{
- int j;
- int ret;
- INIT_LIST_HEAD(&dev_info->event_interfaces[0].dev_attr_list);
+ int j, ret, attrcount = 0;
+
+ INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list);
/* Dynically created from the channels array */
- if (dev_info->channels) {
- for (j = 0; j < dev_info->num_channels; j++) {
- ret = iio_device_add_event_sysfs(dev_info,
- &dev_info
- ->channels[j]);
- if (ret)
- goto error_clear_attrs;
- }
+ for (j = 0; j < indio_dev->num_channels; j++) {
+ ret = iio_device_add_event_sysfs(indio_dev,
+ &indio_dev->channels[j]);
+ if (ret < 0)
+ goto error_clear_attrs;
+ attrcount += ret;
}
- return 0;
+ return attrcount;
error_clear_attrs:
- __iio_remove_all_event_sysfs(dev_info, NULL, i);
+ __iio_remove_event_config_attrs(indio_dev);
return ret;
}
-static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info,
- int i)
+static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev)
{
- __iio_remove_all_event_sysfs(dev_info, NULL, i);
- return 0;
+ int j;
+
+ for (j = 0; j < indio_dev->num_channels; j++)
+ if (indio_dev->channels[j].event_mask != 0)
+ return true;
+ return false;
+}
+
+static void iio_setup_ev_int(struct iio_event_interface *ev_int)
+{
+ 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);
+ init_waitqueue_head(&ev_int->wait);
}
-static int iio_device_register_eventset(struct iio_dev *dev_info)
+static const char *iio_event_group_name = "events";
+static int iio_device_register_eventset(struct iio_dev *indio_dev)
{
- int ret = 0, i, j;
+ struct iio_dev_attr *p;
+ int ret = 0, attrcount_orig = 0, attrcount, attrn;
+ struct attribute **attr;
- if (dev_info->info->num_interrupt_lines == 0)
+ if (!(indio_dev->info->event_attrs ||
+ iio_check_for_dynamic_events(indio_dev)))
return 0;
- dev_info->event_interfaces =
- kzalloc(sizeof(struct iio_event_interface)
- *dev_info->info->num_interrupt_lines,
- GFP_KERNEL);
- if (dev_info->event_interfaces == NULL) {
+ indio_dev->event_interface =
+ kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL);
+ if (indio_dev->event_interface == NULL) {
ret = -ENOMEM;
goto error_ret;
}
- for (i = 0; i < dev_info->info->num_interrupt_lines; i++) {
- ret = iio_setup_ev_int(&dev_info->event_interfaces[i],
- dev_name(&dev_info->dev),
- i,
- dev_info->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_set_drvdata(&dev_info->event_interfaces[i].dev,
- (void *)dev_info);
-
- if (dev_info->info->event_attrs != NULL)
- ret = sysfs_create_group(&dev_info
- ->event_interfaces[i]
- .dev.kobj,
- &dev_info->info
- ->event_attrs[i]);
-
- if (ret) {
- dev_err(&dev_info->dev,
- "Failed to register sysfs for event attrs");
- goto error_remove_sysfs_interfaces;
- }
+ iio_setup_ev_int(indio_dev->event_interface);
+ if (indio_dev->info->event_attrs != NULL) {
+ attr = indio_dev->info->event_attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
+ }
+ attrcount = attrcount_orig;
+ if (indio_dev->channels) {
+ ret = __iio_add_event_config_attrs(indio_dev);
+ if (ret < 0)
+ goto error_free_setup_event_lines;
+ attrcount += ret;
}
- for (i = 0; i < dev_info->info->num_interrupt_lines; i++) {
- ret = __iio_add_event_config_attrs(dev_info, i);
- if (ret)
- goto error_unregister_config_attrs;
+ indio_dev->event_interface->group.name = iio_event_group_name;
+ indio_dev->event_interface->group.attrs =
+ kzalloc(sizeof(indio_dev->event_interface->group.attrs[0])
+ *(attrcount + 1),
+ GFP_KERNEL);
+ if (indio_dev->event_interface->group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_free_setup_event_lines;
}
+ if (indio_dev->info->event_attrs)
+ memcpy(indio_dev->event_interface->group.attrs,
+ indio_dev->info->event_attrs->attrs,
+ sizeof(indio_dev->event_interface->group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p,
+ &indio_dev->event_interface->dev_attr_list,
+ l)
+ indio_dev->event_interface->group.attrs[attrn++] =
+ &p->dev_attr.attr;
+ indio_dev->groups[indio_dev->groupcounter++] =
+ &indio_dev->event_interface->group;
return 0;
-error_unregister_config_attrs:
- for (j = 0; j < i; j++)
- __iio_remove_event_config_attrs(dev_info, i);
- i = dev_info->info->num_interrupt_lines - 1;
-error_remove_sysfs_interfaces:
- for (j = 0; j < i; j++)
- if (dev_info->info->event_attrs != NULL)
- sysfs_remove_group(&dev_info
- ->event_interfaces[j].dev.kobj,
- &dev_info->info->event_attrs[j]);
-error_free_setup_ev_ints:
- for (j = 0; j < i; j++)
- iio_free_ev_int(&dev_info->event_interfaces[j]);
- kfree(dev_info->event_interfaces);
+error_free_setup_event_lines:
+ __iio_remove_event_config_attrs(indio_dev);
+ kfree(indio_dev->event_interface);
error_ret:
return ret;
}
-static void iio_device_unregister_eventset(struct iio_dev *dev_info)
+static void iio_device_unregister_eventset(struct iio_dev *indio_dev)
{
- int i;
-
- if (dev_info->info->num_interrupt_lines == 0)
+ if (indio_dev->event_interface == NULL)
return;
- for (i = 0; i < dev_info->info->num_interrupt_lines; i++) {
- __iio_remove_event_config_attrs(dev_info, i);
- if (dev_info->info->event_attrs != NULL)
- sysfs_remove_group(&dev_info
- ->event_interfaces[i].dev.kobj,
- &dev_info->info->event_attrs[i]);
- }
-
- for (i = 0; i < dev_info->info->num_interrupt_lines; i++)
- iio_free_ev_int(&dev_info->event_interfaces[i]);
- kfree(dev_info->event_interfaces);
+ __iio_remove_event_config_attrs(indio_dev);
+ kfree(indio_dev->event_interface->group.attrs);
+ kfree(indio_dev->event_interface);
}
static void iio_dev_release(struct device *device)
{
- iio_put();
- kfree(to_iio_dev(device));
+ struct iio_dev *indio_dev = container_of(device, struct iio_dev, dev);
+ cdev_del(&indio_dev->chrdev);
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ iio_device_unregister_trigger_consumer(indio_dev);
+ iio_device_unregister_eventset(indio_dev);
+ iio_device_unregister_sysfs(indio_dev);
}
static struct device_type iio_dev_type = {
@@ -1154,12 +1024,21 @@ struct iio_dev *iio_allocate_device(int sizeof_priv)
dev = kzalloc(alloc_size, GFP_KERNEL);
if (dev) {
+ dev->dev.groups = dev->groups;
dev->dev.type = &iio_dev_type;
dev->dev.bus = &iio_bus_type;
device_initialize(&dev->dev);
dev_set_drvdata(&dev->dev, (void *)dev);
mutex_init(&dev->mlock);
- iio_get();
+
+ dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
+ if (dev->id < 0) {
+ /* cannot use a dev_err as the name isn't available */
+ printk(KERN_ERR "Failed to get id\n");
+ kfree(dev);
+ return NULL;
+ }
+ dev_set_name(&dev->dev, "iio:device%d", dev->id);
}
return dev;
@@ -1168,75 +1047,111 @@ EXPORT_SYMBOL(iio_allocate_device);
void iio_free_device(struct iio_dev *dev)
{
- if (dev)
- iio_put_device(dev);
+ if (dev) {
+ ida_simple_remove(&iio_ida, dev->id);
+ kfree(dev);
+ }
}
EXPORT_SYMBOL(iio_free_device);
-int iio_device_register(struct iio_dev *dev_info)
+/**
+ * iio_chrdev_open() - chrdev file open for buffer access and ioctls
+ **/
+static int iio_chrdev_open(struct inode *inode, struct file *filp)
{
- int ret;
+ struct iio_dev *indio_dev = container_of(inode->i_cdev,
+ struct iio_dev, chrdev);
+ filp->private_data = indio_dev;
- dev_info->id = iio_get_new_ida_val(&iio_ida);
- if (dev_info->id < 0) {
- ret = dev_info->id;
- dev_err(&dev_info->dev, "Failed to get id\n");
- goto error_ret;
+ return iio_chrdev_buffer_open(indio_dev);
+}
+
+/**
+ * iio_chrdev_release() - chrdev file close buffer access and ioctls
+ **/
+static int iio_chrdev_release(struct inode *inode, struct file *filp)
+{
+ iio_chrdev_buffer_release(container_of(inode->i_cdev,
+ struct iio_dev, chrdev));
+ return 0;
+}
+
+/* Somewhat of a cross file organization violation - ioctls here are actually
+ * event related */
+static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct iio_dev *indio_dev = filp->private_data;
+ int __user *ip = (int __user *)arg;
+ int fd;
+
+ if (cmd == IIO_GET_EVENT_FD_IOCTL) {
+ fd = iio_event_getfd(indio_dev);
+ if (copy_to_user(ip, &fd, sizeof(fd)))
+ return -EFAULT;
+ return 0;
}
- dev_set_name(&dev_info->dev, "device%d", dev_info->id);
+ return -EINVAL;
+}
- ret = device_add(&dev_info->dev);
- if (ret)
- goto error_free_ida;
- ret = iio_device_register_sysfs(dev_info);
+static const struct file_operations iio_buffer_fileops = {
+ .read = iio_buffer_read_first_n_outer_addr,
+ .release = iio_chrdev_release,
+ .open = iio_chrdev_open,
+ .poll = iio_buffer_poll_addr,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = iio_ioctl,
+ .compat_ioctl = iio_ioctl,
+};
+
+int iio_device_register(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ /* configure elements for the chrdev */
+ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
+
+ ret = iio_device_register_sysfs(indio_dev);
if (ret) {
- dev_err(dev_info->dev.parent,
+ dev_err(indio_dev->dev.parent,
"Failed to register sysfs interfaces\n");
- goto error_del_device;
+ goto error_ret;
}
- ret = iio_device_register_eventset(dev_info);
+ ret = iio_device_register_eventset(indio_dev);
if (ret) {
- dev_err(dev_info->dev.parent,
+ dev_err(indio_dev->dev.parent,
"Failed to register event set\n");
goto error_free_sysfs;
}
- if (dev_info->modes & INDIO_RING_TRIGGERED)
- iio_device_register_trigger_consumer(dev_info);
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ iio_device_register_trigger_consumer(indio_dev);
+ ret = device_add(&indio_dev->dev);
+ if (ret < 0)
+ goto error_unreg_eventset;
+ cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
+ indio_dev->chrdev.owner = indio_dev->info->driver_module;
+ ret = cdev_add(&indio_dev->chrdev, indio_dev->dev.devt, 1);
+ if (ret < 0)
+ goto error_del_device;
return 0;
-error_free_sysfs:
- iio_device_unregister_sysfs(dev_info);
error_del_device:
- device_del(&dev_info->dev);
-error_free_ida:
- iio_free_ida_val(&iio_ida, dev_info->id);
+ device_del(&indio_dev->dev);
+error_unreg_eventset:
+ iio_device_unregister_eventset(indio_dev);
+error_free_sysfs:
+ iio_device_unregister_sysfs(indio_dev);
error_ret:
return ret;
}
EXPORT_SYMBOL(iio_device_register);
-void iio_device_unregister(struct iio_dev *dev_info)
+void iio_device_unregister(struct iio_dev *indio_dev)
{
- if (dev_info->modes & INDIO_RING_TRIGGERED)
- iio_device_unregister_trigger_consumer(dev_info);
- iio_device_unregister_eventset(dev_info);
- iio_device_unregister_sysfs(dev_info);
- iio_free_ida_val(&iio_ida, dev_info->id);
- device_unregister(&dev_info->dev);
+ device_unregister(&indio_dev->dev);
}
EXPORT_SYMBOL(iio_device_unregister);
-
-void iio_put(void)
-{
- module_put(THIS_MODULE);
-}
-
-void iio_get(void)
-{
- __module_get(THIS_MODULE);
-}
-
subsys_initcall(iio_init);
module_exit(iio_exit);
OpenPOWER on IntegriCloud