mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
![Greg Kroah-Hartman](/assets/img/avatar_default.png)
This mostly consists of patches that didn't quite make the last cycle. Lots of interesting things under review currently. Core: - Add devm_iio_device_register/unregister. I took some convincing on whether there would be many devices that really were simple enough to need no explicit actions on removal. Turns out there are some. - Move some stray docs to above the relevant implemenation. - Drop a redundant repeated check on the fact the trigger has actually changed when there is a userspace attempt change it. Drivers: New drivers - Freescale MPL3115A2 Pressure / temperature sensor New functionality - hid_sensors: add sensitivity support. DT bindings - tsl2563 - hmc5843 Cleanups - Drop unused scan_type from viperboard adc driver. - devm_iio_device_register used in viperboard, ad5421, ad5755, adis16130, adxrs450, vcnl4000, adis16220, ad7816, lpc32xx, adt7316, adis16060, isl29018 and ad2s1200. Note that this was proposed in a number of other drivers and this revealed a number of missorderings in remove functions. Also for now I have blocked this on any device that any hardware suspend suport on the basis that we probably want to power down devices if they have no driver support loaded. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJSk7pfAAoJEFSFNJnE9BaINEAP/2LKNkK/65BI8DOb3DQcRQWw yQAjYd43c7OWTW6clx3DRZiESI1qR7B1LeIV3P+jF1kvOS5qdE1WfYI9pQCLW+JL yRQgqLJknuHnrs+Bzw5CrGRcPDl5Zk31megcs0CcOt76LbQsaQ+w/blq9EiuVioW s2HfopsbVPQBe/IRA5ajLRG5gQYR4FIEIdzifzmv3Zydzhg9yFy5sk6vpSnzN6zM bh1Q8OooMDCqrCsKwzG1z5ZT4ki6oEILVQsiCblI9kcbw+GyKb95d63E0ZdlH72o ymowmRGujB+gS1zBh+1zLVjuaUytLHBUxAcQIFYwpOtbqaAYo68J5HFEyXWh5QpH aeMWnN4Cd54h83SsBofF05aFPGGK3iM6rioDpPJaLjBzJgFuRvIV87O3t0j1/3/Q VfXKYboGH4EVHsbARoUaQ4UP/0WlXoDQwX5XfBbsSlL9/U58JmrKAx6+HvmSyvcG 5HKzOsVQNKbZMrSlBlvFzPXtRdJwRuv9TwWLLUatQKZAt66rdBgWcycOvlQv1m/9 Neo/2b22vqoAN1AfyW32Hz7rQCnKxVczbTdex9I5ikNJmIPUfbZ6TAE/660HSVE2 J70ZA6SHGU9Edhm0fkJNBhRanO3my4vQFUwcl457JY9O/BeC0xBhU1ZjvkFGysG0 D/9TEiSaLoURkhxeeN0M =KZou -----END PGP SIGNATURE----- Merge tag 'iio-for-3.14a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First set of new features, drivers and cleanups for IIO in the 3.14 cycle. This mostly consists of patches that didn't quite make the last cycle. Lots of interesting things under review currently. Core: - Add devm_iio_device_register/unregister. I took some convincing on whether there would be many devices that really were simple enough to need no explicit actions on removal. Turns out there are some. - Move some stray docs to above the relevant implemenation. - Drop a redundant repeated check on the fact the trigger has actually changed when there is a userspace attempt change it. Drivers: New drivers - Freescale MPL3115A2 Pressure / temperature sensor New functionality - hid_sensors: add sensitivity support. DT bindings - tsl2563 - hmc5843 Cleanups - Drop unused scan_type from viperboard adc driver. - devm_iio_device_register used in viperboard, ad5421, ad5755, adis16130, adxrs450, vcnl4000, adis16220, ad7816, lpc32xx, adt7316, adis16060, isl29018 and ad2s1200. Note that this was proposed in a number of other drivers and this revealed a number of missorderings in remove functions. Also for now I have blocked this on any device that any hardware suspend suport on the basis that we probably want to power down devices if they have no driver support loaded.
628 lines
16 KiB
C
628 lines
16 KiB
C
/* Industrial I/O event handling
|
|
*
|
|
* 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/anon_inodes.h>
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kfifo.h>
|
|
#include <linux/module.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/iio/iio.h>
|
|
#include "iio_core.h"
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/events.h>
|
|
|
|
/**
|
|
* struct iio_event_interface - chrdev interface for an event line
|
|
* @wait: wait queue to allow blocking reads of events
|
|
* @det_events: list of detected events
|
|
* @dev_attr_list: list of event interface sysfs attribute
|
|
* @flags: file operations related flags including busy flag.
|
|
* @group: event interface sysfs attribute group
|
|
*/
|
|
struct iio_event_interface {
|
|
wait_queue_head_t wait;
|
|
DECLARE_KFIFO(det_events, struct iio_event_data, 16);
|
|
|
|
struct list_head dev_attr_list;
|
|
unsigned long flags;
|
|
struct attribute_group group;
|
|
};
|
|
|
|
/**
|
|
* iio_push_event() - try to add event to the list for userspace reading
|
|
* @indio_dev: IIO device structure
|
|
* @ev_code: What event
|
|
* @timestamp: When the event occurred
|
|
**/
|
|
int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
|
{
|
|
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
|
struct iio_event_data ev;
|
|
unsigned long flags;
|
|
int copied;
|
|
|
|
/* Does anyone care? */
|
|
spin_lock_irqsave(&ev_int->wait.lock, flags);
|
|
if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
|
|
|
|
ev.id = ev_code;
|
|
ev.timestamp = timestamp;
|
|
|
|
copied = kfifo_put(&ev_int->det_events, ev);
|
|
if (copied != 0)
|
|
wake_up_locked_poll(&ev_int->wait, POLLIN);
|
|
}
|
|
spin_unlock_irqrestore(&ev_int->wait.lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(iio_push_event);
|
|
|
|
/**
|
|
* iio_event_poll() - poll the event queue to find out if it has data
|
|
*/
|
|
static unsigned int iio_event_poll(struct file *filep,
|
|
struct poll_table_struct *wait)
|
|
{
|
|
struct iio_dev *indio_dev = filep->private_data;
|
|
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
|
unsigned int events = 0;
|
|
|
|
if (!indio_dev->info)
|
|
return -ENODEV;
|
|
|
|
poll_wait(filep, &ev_int->wait, wait);
|
|
|
|
spin_lock_irq(&ev_int->wait.lock);
|
|
if (!kfifo_is_empty(&ev_int->det_events))
|
|
events = POLLIN | POLLRDNORM;
|
|
spin_unlock_irq(&ev_int->wait.lock);
|
|
|
|
return events;
|
|
}
|
|
|
|
static ssize_t iio_event_chrdev_read(struct file *filep,
|
|
char __user *buf,
|
|
size_t count,
|
|
loff_t *f_ps)
|
|
{
|
|
struct iio_dev *indio_dev = filep->private_data;
|
|
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
|
unsigned int copied;
|
|
int ret;
|
|
|
|
if (!indio_dev->info)
|
|
return -ENODEV;
|
|
|
|
if (count < sizeof(struct iio_event_data))
|
|
return -EINVAL;
|
|
|
|
spin_lock_irq(&ev_int->wait.lock);
|
|
if (kfifo_is_empty(&ev_int->det_events)) {
|
|
if (filep->f_flags & O_NONBLOCK) {
|
|
ret = -EAGAIN;
|
|
goto error_unlock;
|
|
}
|
|
/* Blocking on device; waiting for something to be there */
|
|
ret = wait_event_interruptible_locked_irq(ev_int->wait,
|
|
!kfifo_is_empty(&ev_int->det_events) ||
|
|
indio_dev->info == NULL);
|
|
if (ret)
|
|
goto error_unlock;
|
|
if (indio_dev->info == NULL) {
|
|
ret = -ENODEV;
|
|
goto error_unlock;
|
|
}
|
|
/* Single access device so no one else can get the data */
|
|
}
|
|
|
|
ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
|
|
|
|
error_unlock:
|
|
spin_unlock_irq(&ev_int->wait.lock);
|
|
|
|
return ret ? ret : copied;
|
|
}
|
|
|
|
static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
|
|
{
|
|
struct iio_dev *indio_dev = filep->private_data;
|
|
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
|
|
|
spin_lock_irq(&ev_int->wait.lock);
|
|
__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
|
|
* any new __iio_push_event calls running.
|
|
*/
|
|
kfifo_reset_out(&ev_int->det_events);
|
|
spin_unlock_irq(&ev_int->wait.lock);
|
|
|
|
iio_device_put(indio_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations iio_event_chrdev_fileops = {
|
|
.read = iio_event_chrdev_read,
|
|
.poll = iio_event_poll,
|
|
.release = iio_event_chrdev_release,
|
|
.owner = THIS_MODULE,
|
|
.llseek = noop_llseek,
|
|
};
|
|
|
|
int iio_event_getfd(struct iio_dev *indio_dev)
|
|
{
|
|
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
|
int fd;
|
|
|
|
if (ev_int == NULL)
|
|
return -ENODEV;
|
|
|
|
spin_lock_irq(&ev_int->wait.lock);
|
|
if (__test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
|
|
spin_unlock_irq(&ev_int->wait.lock);
|
|
return -EBUSY;
|
|
}
|
|
spin_unlock_irq(&ev_int->wait.lock);
|
|
iio_device_get(indio_dev);
|
|
|
|
fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,
|
|
indio_dev, O_RDONLY | O_CLOEXEC);
|
|
if (fd < 0) {
|
|
spin_lock_irq(&ev_int->wait.lock);
|
|
__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
|
spin_unlock_irq(&ev_int->wait.lock);
|
|
iio_device_put(indio_dev);
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
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_THRESH_ADAPTIVE] = "thresh_adaptive",
|
|
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
|
|
};
|
|
|
|
static const char * const iio_ev_dir_text[] = {
|
|
[IIO_EV_DIR_EITHER] = "either",
|
|
[IIO_EV_DIR_RISING] = "rising",
|
|
[IIO_EV_DIR_FALLING] = "falling"
|
|
};
|
|
|
|
static const char * const iio_ev_info_text[] = {
|
|
[IIO_EV_INFO_ENABLE] = "en",
|
|
[IIO_EV_INFO_VALUE] = "value",
|
|
[IIO_EV_INFO_HYSTERESIS] = "hysteresis",
|
|
};
|
|
|
|
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
|
|
{
|
|
return attr->c->event_spec[attr->address & 0xffff].dir;
|
|
}
|
|
|
|
static enum iio_event_type iio_ev_attr_type(struct iio_dev_attr *attr)
|
|
{
|
|
return attr->c->event_spec[attr->address & 0xffff].type;
|
|
}
|
|
|
|
static enum iio_event_info iio_ev_attr_info(struct iio_dev_attr *attr)
|
|
{
|
|
return (attr->address >> 16) & 0xffff;
|
|
}
|
|
|
|
static ssize_t iio_ev_state_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf,
|
|
size_t len)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
int ret;
|
|
bool val;
|
|
|
|
ret = strtobool(buf, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (indio_dev->info->write_event_config)
|
|
ret = indio_dev->info->write_event_config(indio_dev,
|
|
this_attr->address, val);
|
|
else
|
|
ret = indio_dev->info->write_event_config_new(indio_dev,
|
|
this_attr->c, iio_ev_attr_type(this_attr),
|
|
iio_ev_attr_dir(this_attr), val);
|
|
|
|
return (ret < 0) ? ret : len;
|
|
}
|
|
|
|
static ssize_t iio_ev_state_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
int val;
|
|
|
|
if (indio_dev->info->read_event_config)
|
|
val = indio_dev->info->read_event_config(indio_dev,
|
|
this_attr->address);
|
|
else
|
|
val = indio_dev->info->read_event_config_new(indio_dev,
|
|
this_attr->c, iio_ev_attr_type(this_attr),
|
|
iio_ev_attr_dir(this_attr));
|
|
if (val < 0)
|
|
return val;
|
|
else
|
|
return sprintf(buf, "%d\n", val);
|
|
}
|
|
|
|
static ssize_t iio_ev_value_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
int val, val2;
|
|
int ret;
|
|
|
|
if (indio_dev->info->read_event_value) {
|
|
ret = indio_dev->info->read_event_value(indio_dev,
|
|
this_attr->address, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
return sprintf(buf, "%d\n", val);
|
|
} else {
|
|
ret = indio_dev->info->read_event_value_new(indio_dev,
|
|
this_attr->c, iio_ev_attr_type(this_attr),
|
|
iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr),
|
|
&val, &val2);
|
|
if (ret < 0)
|
|
return ret;
|
|
return iio_format_value(buf, ret, val, val2);
|
|
}
|
|
}
|
|
|
|
static ssize_t iio_ev_value_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf,
|
|
size_t len)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
int val, val2;
|
|
int ret;
|
|
|
|
if (!indio_dev->info->write_event_value &&
|
|
!indio_dev->info->write_event_value_new)
|
|
return -EINVAL;
|
|
|
|
if (indio_dev->info->write_event_value) {
|
|
ret = kstrtoint(buf, 10, &val);
|
|
if (ret)
|
|
return ret;
|
|
ret = indio_dev->info->write_event_value(indio_dev,
|
|
this_attr->address, val);
|
|
} else {
|
|
ret = iio_str_to_fixpoint(buf, 100000, &val, &val2);
|
|
if (ret)
|
|
return ret;
|
|
ret = indio_dev->info->write_event_value_new(indio_dev,
|
|
this_attr->c, iio_ev_attr_type(this_attr),
|
|
iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr),
|
|
val, val2);
|
|
}
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return len;
|
|
}
|
|
|
|
static int iio_device_add_event(struct iio_dev *indio_dev,
|
|
const struct iio_chan_spec *chan, unsigned int spec_index,
|
|
enum iio_event_type type, enum iio_event_direction dir,
|
|
enum iio_shared_by shared_by, const unsigned long *mask)
|
|
{
|
|
ssize_t (*show)(struct device *, struct device_attribute *, char *);
|
|
ssize_t (*store)(struct device *, struct device_attribute *,
|
|
const char *, size_t);
|
|
unsigned int attrcount = 0;
|
|
unsigned int i;
|
|
char *postfix;
|
|
int ret;
|
|
|
|
for_each_set_bit(i, mask, sizeof(*mask)) {
|
|
postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
|
|
iio_ev_type_text[type], iio_ev_dir_text[dir],
|
|
iio_ev_info_text[i]);
|
|
if (postfix == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (i == IIO_EV_INFO_ENABLE) {
|
|
show = iio_ev_state_show;
|
|
store = iio_ev_state_store;
|
|
} else {
|
|
show = iio_ev_value_show;
|
|
store = iio_ev_value_store;
|
|
}
|
|
|
|
ret = __iio_add_chan_devattr(postfix, chan, show, store,
|
|
(i << 16) | spec_index, shared_by, &indio_dev->dev,
|
|
&indio_dev->event_interface->dev_attr_list);
|
|
kfree(postfix);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
attrcount++;
|
|
}
|
|
|
|
return attrcount;
|
|
}
|
|
|
|
static int iio_device_add_event_sysfs_new(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan)
|
|
{
|
|
int ret = 0, i, attrcount = 0;
|
|
enum iio_event_direction dir;
|
|
enum iio_event_type type;
|
|
|
|
for (i = 0; i < chan->num_event_specs; i++) {
|
|
type = chan->event_spec[i].type;
|
|
dir = chan->event_spec[i].dir;
|
|
|
|
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
|
IIO_SEPARATE, &chan->event_spec[i].mask_separate);
|
|
if (ret < 0)
|
|
goto error_ret;
|
|
attrcount += ret;
|
|
|
|
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
|
IIO_SHARED_BY_TYPE,
|
|
&chan->event_spec[i].mask_shared_by_type);
|
|
if (ret < 0)
|
|
goto error_ret;
|
|
attrcount += ret;
|
|
|
|
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
|
IIO_SHARED_BY_DIR,
|
|
&chan->event_spec[i].mask_shared_by_dir);
|
|
if (ret < 0)
|
|
goto error_ret;
|
|
attrcount += ret;
|
|
|
|
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
|
IIO_SHARED_BY_ALL,
|
|
&chan->event_spec[i].mask_shared_by_all);
|
|
if (ret < 0)
|
|
goto error_ret;
|
|
attrcount += ret;
|
|
}
|
|
ret = attrcount;
|
|
error_ret:
|
|
return ret;
|
|
}
|
|
|
|
static int iio_device_add_event_sysfs_old(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan)
|
|
{
|
|
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_DIR_MAX],
|
|
iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
|
|
if (postfix == NULL) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
if (chan->modified)
|
|
mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel2,
|
|
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,
|
|
chan,
|
|
&iio_ev_state_show,
|
|
iio_ev_state_store,
|
|
mask,
|
|
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_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, chan,
|
|
iio_ev_value_show,
|
|
iio_ev_value_store,
|
|
mask,
|
|
0,
|
|
&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 int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan)
|
|
{
|
|
if (chan->event_mask)
|
|
return iio_device_add_event_sysfs_old(indio_dev, chan);
|
|
else
|
|
return iio_device_add_event_sysfs_new(indio_dev, chan);
|
|
}
|
|
|
|
static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
|
|
{
|
|
int j, ret, attrcount = 0;
|
|
|
|
/* Dynically created from the channels array */
|
|
for (j = 0; j < indio_dev->num_channels; j++) {
|
|
ret = iio_device_add_event_sysfs(indio_dev,
|
|
&indio_dev->channels[j]);
|
|
if (ret < 0)
|
|
return ret;
|
|
attrcount += ret;
|
|
}
|
|
return attrcount;
|
|
}
|
|
|
|
static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < indio_dev->num_channels; j++) {
|
|
if (indio_dev->channels[j].event_mask != 0)
|
|
return true;
|
|
if (indio_dev->channels[j].num_event_specs != 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void iio_setup_ev_int(struct iio_event_interface *ev_int)
|
|
{
|
|
INIT_KFIFO(ev_int->det_events);
|
|
init_waitqueue_head(&ev_int->wait);
|
|
}
|
|
|
|
static const char *iio_event_group_name = "events";
|
|
int iio_device_register_eventset(struct iio_dev *indio_dev)
|
|
{
|
|
struct iio_dev_attr *p;
|
|
int ret = 0, attrcount_orig = 0, attrcount, attrn;
|
|
struct attribute **attr;
|
|
|
|
if (!(indio_dev->info->event_attrs ||
|
|
iio_check_for_dynamic_events(indio_dev)))
|
|
return 0;
|
|
|
|
indio_dev->event_interface =
|
|
kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL);
|
|
if (indio_dev->event_interface == NULL) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list);
|
|
|
|
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;
|
|
}
|
|
|
|
indio_dev->event_interface->group.name = iio_event_group_name;
|
|
indio_dev->event_interface->group.attrs = kcalloc(attrcount + 1,
|
|
sizeof(indio_dev->event_interface->group.attrs[0]),
|
|
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_free_setup_event_lines:
|
|
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
|
|
kfree(indio_dev->event_interface);
|
|
error_ret:
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* iio_device_wakeup_eventset - Wakes up the event waitqueue
|
|
* @indio_dev: The IIO device
|
|
*
|
|
* Wakes up the event waitqueue used for poll() and blocking read().
|
|
* Should usually be called when the device is unregistered.
|
|
*/
|
|
void iio_device_wakeup_eventset(struct iio_dev *indio_dev)
|
|
{
|
|
if (indio_dev->event_interface == NULL)
|
|
return;
|
|
wake_up(&indio_dev->event_interface->wait);
|
|
}
|
|
|
|
void iio_device_unregister_eventset(struct iio_dev *indio_dev)
|
|
{
|
|
if (indio_dev->event_interface == NULL)
|
|
return;
|
|
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
|
|
kfree(indio_dev->event_interface->group.attrs);
|
|
kfree(indio_dev->event_interface);
|
|
}
|