#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 = {
};
EXPORT_SYMBOL(iio_bus_type);
+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_shared[] = {
- [IIO_IN] = "in",
- [IIO_OUT] = "out",
+ [IIO_VOLTAGE] = "voltage",
[IIO_CURRENT] = "current",
[IIO_POWER] = "power",
[IIO_ACCEL] = "accel",
- [IIO_IN_DIFF] = "in-in",
+ [IIO_VOLTAGE_DIFF] = "voltage-voltage",
[IIO_GYRO] = "gyro",
[IIO_MAGN] = "magn",
[IIO_LIGHT] = "illuminance",
[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",
+ [IIO_VOLTAGE_DIFF] = "voltage%d-voltage%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 */
[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",
+};
+
+/**
+ * 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 *dev_info,
- int ev_line,
- int ev_code,
- s64 timestamp)
+int iio_push_event(struct iio_dev *dev_info, u64 ev_code, s64 timestamp)
{
- struct iio_event_interface *ev_int
- = &dev_info->event_interfaces[ev_line];
+ struct iio_event_interface *ev_int = dev_info->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;
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
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;
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);
-}
-
-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;
+ if (indio_dev->event_interface == NULL)
+ return -ENODEV;
- 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)
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;
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);
}
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,
+ iio_modifier_names[chan->channel2],
chan->extend_name,
postfix);
else
all_post = kasprintf(GFP_KERNEL, "%s_%s",
- intermediate,
+ iio_modifier_names[chan->channel2],
postfix);
} else
all_post = kasprintf(GFP_KERNEL, "%s_%s", chan->extend_name,
return 0;
}
+static
int __iio_device_attr_init(struct device_attribute *dev_attr,
const char *postfix,
struct iio_chan_spec const *chan,
goto error_ret;
/* Special case for types that uses both channel numbers in naming */
- if (chan->type == IIO_IN_DIFF && !generic)
+ if (chan->type == IIO_VOLTAGE_DIFF && !generic)
name_format
- = kasprintf(GFP_KERNEL, "%s_%s",
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction[chan->output],
iio_chan_type_name_spec_complex[chan->type],
full_postfix);
else if (generic || !chan->indexed)
name_format
- = kasprintf(GFP_KERNEL, "%s_%s",
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction[chan->output],
iio_chan_type_name_spec_shared[chan->type],
full_postfix);
else
name_format
- = kasprintf(GFP_KERNEL, "%s%d_%s",
+ = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
+ iio_direction[chan->output],
iio_chan_type_name_spec_shared[chan->type],
chan->channel,
full_postfix);
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,
struct device_attribute *attr,
const char *buf,
size_t len),
- int mask,
+ u64 mask,
bool generic,
struct device *dev,
struct list_head *attr_list)
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;
static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
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,
+ &dev_info->dev,
+ &dev_info->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),
}
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,
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);
}
static int iio_device_register_sysfs(struct iio_dev *dev_info)
{
- int i, ret = 0;
+ int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
struct iio_dev_attr *p, *n;
+ struct attribute **attr;
+ /* First count elements in any existing group */
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;
- }
+ attr = dev_info->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.
->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 (dev_info->name)
+ attrcount++;
+
+ dev_info->chan_attr_group.attrs
+ = kzalloc(sizeof(dev_info->chan_attr_group.attrs[0])*
+ (attrcount + 1),
+ GFP_KERNEL);
+ if (dev_info->chan_attr_group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_attrs;
}
+ /* Copy across original attributes */
+ if (dev_info->info->attrs)
+ memcpy(dev_info->chan_attr_group.attrs,
+ dev_info->info->attrs->attrs,
+ sizeof(dev_info->chan_attr_group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p, &dev_info->channel_attr_list, l)
+ dev_info->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
+ if (dev_info->name)
+ dev_info->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+
+ dev_info->groups[dev_info->groupcounter++] =
+ &dev_info->chan_attr_group;
+
return 0;
error_clear_attrs:
list_del(&p->l);
iio_device_remove_and_free_read_attr(dev_info, 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)
{
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_del(&p->l);
iio_device_remove_and_free_read_attr(dev_info, 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(dev_info->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[] = {
static int iio_device_add_event_sysfs(struct iio_dev *dev_info,
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,
+ i/IIO_EV_DIR_MAX,
+ i%IIO_EV_DIR_MAX);
+ else if (chan->type == IIO_VOLTAGE_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
+ 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].
+ &dev_info->dev,
+ &dev_info->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);
+ &dev_info->dev,
+ &dev_info->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 *dev_info)
{
struct iio_dev_attr *p, *n;
list_for_each_entry_safe(p, n,
- &dev_info->event_interfaces[num].
+ &dev_info->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 *dev_info)
{
- int j;
- int ret;
- INIT_LIST_HEAD(&dev_info->event_interfaces[0].dev_attr_list);
+ int j, ret, attrcount = 0;
+
+ INIT_LIST_HEAD(&dev_info->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 < dev_info->num_channels; j++) {
+ ret = iio_device_add_event_sysfs(dev_info,
+ &dev_info->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(dev_info);
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 *dev_info)
{
- __iio_remove_all_event_sysfs(dev_info, NULL, i);
- return 0;
+ int j;
+
+ for (j = 0; j < dev_info->num_channels; j++)
+ if (dev_info->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 const char *iio_event_group_name = "events";
static int iio_device_register_eventset(struct iio_dev *dev_info)
{
- 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 (!(dev_info->info->event_attrs ||
+ iio_check_for_dynamic_events(dev_info)))
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) {
+ dev_info->event_interface =
+ kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL);
+ if (dev_info->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(dev_info->event_interface);
+ if (dev_info->info->event_attrs != NULL) {
+ attr = dev_info->info->event_attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
+ }
+ attrcount = attrcount_orig;
+ if (dev_info->channels) {
+ ret = __iio_add_event_config_attrs(dev_info);
+ 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;
+ dev_info->event_interface->group.name = iio_event_group_name;
+ dev_info->event_interface->group.attrs =
+ kzalloc(sizeof(dev_info->event_interface->group.attrs[0])
+ *(attrcount + 1),
+ GFP_KERNEL);
+ if (dev_info->event_interface->group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_free_setup_event_lines;
}
+ if (dev_info->info->event_attrs)
+ memcpy(dev_info->event_interface->group.attrs,
+ dev_info->info->event_attrs->attrs,
+ sizeof(dev_info->event_interface->group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p,
+ &dev_info->event_interface->dev_attr_list,
+ l)
+ dev_info->event_interface->group.attrs[attrn++] =
+ &p->dev_attr.attr;
+
+ dev_info->groups[dev_info->groupcounter++] =
+ &dev_info->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(dev_info);
+ kfree(dev_info->event_interface);
error_ret:
return ret;
static void iio_device_unregister_eventset(struct iio_dev *dev_info)
{
- int i;
-
- if (dev_info->info->num_interrupt_lines == 0)
+ if (dev_info->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(dev_info);
+ kfree(dev_info->event_interface->group.attrs);
+ kfree(dev_info->event_interface);
}
static void iio_dev_release(struct device *device)
{
- iio_put();
- kfree(to_iio_dev(device));
+ struct iio_dev *dev_info = container_of(device, struct iio_dev, dev);
+ cdev_del(&dev_info->chrdev);
+ 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);
+ ida_simple_remove(&iio_ida, dev_info->id);
+ kfree(dev_info);
}
static struct device_type iio_dev_type = {
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();
}
return dev;
void iio_free_device(struct iio_dev *dev)
{
if (dev)
- iio_put_device(dev);
+ kfree(dev);
}
EXPORT_SYMBOL(iio_free_device);
+/**
+ * iio_chrdev_open() - chrdev file open for ring buffer access and ioctls
+ **/
+static int iio_chrdev_open(struct inode *inode, struct file *filp)
+{
+ struct iio_dev *dev_info = container_of(inode->i_cdev,
+ struct iio_dev, chrdev);
+ filp->private_data = dev_info;
+ iio_chrdev_ring_open(dev_info);
+ return 0;
+}
+
+/**
+ * iio_chrdev_release() - chrdev file close ring buffer access and ioctls
+ **/
+static int iio_chrdev_release(struct inode *inode, struct file *filp)
+{
+ iio_chrdev_ring_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;
+ }
+ return -EINVAL;
+}
+
+static const struct file_operations iio_ring_fileops = {
+ .read = iio_ring_read_first_n_outer_addr,
+ .release = iio_chrdev_release,
+ .open = iio_chrdev_open,
+ .poll = iio_ring_poll_addr,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = iio_ioctl,
+ .compat_ioctl = iio_ioctl,
+};
+
int iio_device_register(struct iio_dev *dev_info)
{
int ret;
- dev_info->id = iio_get_new_ida_val(&iio_ida);
+ dev_info->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
if (dev_info->id < 0) {
ret = dev_info->id;
dev_err(&dev_info->dev, "Failed to get id\n");
goto error_ret;
}
- dev_set_name(&dev_info->dev, "device%d", dev_info->id);
+ dev_set_name(&dev_info->dev, "iio:device%d", dev_info->id);
+
+ /* configure elements for the chrdev */
+ dev_info->dev.devt = MKDEV(MAJOR(iio_devt), dev_info->id);
- ret = device_add(&dev_info->dev);
- if (ret)
- goto error_free_ida;
ret = iio_device_register_sysfs(dev_info);
if (ret) {
dev_err(dev_info->dev.parent,
"Failed to register sysfs interfaces\n");
- goto error_del_device;
+ goto error_free_ida;
}
ret = iio_device_register_eventset(dev_info);
if (ret) {
if (dev_info->modes & INDIO_RING_TRIGGERED)
iio_device_register_trigger_consumer(dev_info);
+ ret = device_add(&dev_info->dev);
+ if (ret < 0)
+ goto error_unreg_eventset;
+ cdev_init(&dev_info->chrdev, &iio_ring_fileops);
+ dev_info->chrdev.owner = dev_info->info->driver_module;
+ ret = cdev_add(&dev_info->chrdev, dev_info->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_unreg_eventset:
+ iio_device_unregister_eventset(dev_info);
+error_free_sysfs:
+ iio_device_unregister_sysfs(dev_info);
error_free_ida:
- iio_free_ida_val(&iio_ida, dev_info->id);
+ ida_simple_remove(&iio_ida, dev_info->id);
error_ret:
return ret;
}
void iio_device_unregister(struct iio_dev *dev_info)
{
- 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);
}
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);