summaryrefslogtreecommitdiff
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/iio_core.h4
-rw-r--r--drivers/iio/industrialio-buffer.c127
-rw-r--r--drivers/iio/industrialio-core.c1
3 files changed, 130 insertions, 2 deletions
diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 8f4a9b264962..61e318431de9 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -68,12 +68,15 @@ __poll_t iio_buffer_poll_wrapper(struct file *filp,
struct poll_table_struct *wait);
ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps);
+ssize_t iio_buffer_write_wrapper(struct file *filp, const char __user *buf,
+ size_t n, loff_t *f_ps);
int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr (&iio_buffer_poll_wrapper)
#define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper)
+#define iio_buffer_write_outer_addr (&iio_buffer_write_wrapper)
void iio_disable_all_buffers(struct iio_dev *indio_dev);
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
@@ -83,6 +86,7 @@ void iio_device_detach_buffers(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr NULL
#define iio_buffer_read_outer_addr NULL
+#define iio_buffer_write_outer_addr NULL
static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 4209e933ab80..b884d78657cb 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -120,6 +120,9 @@ static ssize_t iio_buffer_read(struct file *filp, char __user *buf,
if (!rb || !rb->access->read)
return -EINVAL;
+ if (rb->direction != IIO_BUFFER_DIRECTION_IN)
+ return -EPERM;
+
datum_size = rb->bytes_per_datum;
/*
@@ -161,6 +164,65 @@ static ssize_t iio_buffer_read(struct file *filp, char __user *buf,
return ret;
}
+static size_t iio_buffer_space_available(struct iio_buffer *buf)
+{
+ if (buf->access->space_available)
+ return buf->access->space_available(buf);
+
+ return SIZE_MAX;
+}
+
+static ssize_t iio_buffer_write(struct file *filp, const char __user *buf,
+ size_t n, loff_t *f_ps)
+{
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_buffer *rb = ib->buffer;
+ struct iio_dev *indio_dev = ib->indio_dev;
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
+ int ret;
+ size_t written;
+
+ if (!indio_dev->info)
+ return -ENODEV;
+
+ if (!rb || !rb->access->write)
+ return -EINVAL;
+
+ if (rb->direction != IIO_BUFFER_DIRECTION_OUT)
+ return -EPERM;
+
+ written = 0;
+ add_wait_queue(&rb->pollq, &wait);
+ do {
+ if (indio_dev->info == NULL)
+ return -ENODEV;
+
+ if (!iio_buffer_space_available(rb)) {
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ wait_woken(&wait, TASK_INTERRUPTIBLE,
+ MAX_SCHEDULE_TIMEOUT);
+ continue;
+ }
+
+ ret = rb->access->write(rb, n - written, buf + written);
+ if (ret == 0 && (filp->f_flags & O_NONBLOCK))
+ ret = -EAGAIN;
+
+ if (ret > 0) {
+ written += ret;
+ if (written != n && !(filp->f_flags & O_NONBLOCK))
+ continue;
+ }
+ } while (ret == 0);
+ remove_wait_queue(&rb->pollq, &wait);
+
+ return ret < 0 ? ret : n;
+}
+
/**
* iio_buffer_poll() - poll the buffer to find out if it has data
* @filp: File structure pointer for device access
@@ -181,8 +243,18 @@ static __poll_t iio_buffer_poll(struct file *filp,
return 0;
poll_wait(filp, &rb->pollq, wait);
- if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
- return EPOLLIN | EPOLLRDNORM;
+
+ switch (rb->direction) {
+ case IIO_BUFFER_DIRECTION_IN:
+ if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
+ return EPOLLIN | EPOLLRDNORM;
+ break;
+ case IIO_BUFFER_DIRECTION_OUT:
+ if (iio_buffer_space_available(rb))
+ return EPOLLOUT | EPOLLWRNORM;
+ break;
+ }
+
return 0;
}
@@ -199,6 +271,19 @@ ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf,
return iio_buffer_read(filp, buf, n, f_ps);
}
+ssize_t iio_buffer_write_wrapper(struct file *filp, const char __user *buf,
+ size_t n, loff_t *f_ps)
+{
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_buffer *rb = ib->buffer;
+
+ /* check if buffer was opened through new API */
+ if (test_bit(IIO_BUSY_BIT_POS, &rb->flags))
+ return -EBUSY;
+
+ return iio_buffer_write(filp, buf, n, f_ps);
+}
+
__poll_t iio_buffer_poll_wrapper(struct file *filp,
struct poll_table_struct *wait)
{
@@ -231,6 +316,15 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev)
}
}
+int iio_pop_from_buffer(struct iio_buffer *buffer, void *data)
+{
+ if (!buffer || !buffer->access || !buffer->access->remove_from)
+ return -EINVAL;
+
+ return buffer->access->remove_from(buffer, data);
+}
+EXPORT_SYMBOL_GPL(iio_pop_from_buffer);
+
void iio_buffer_init(struct iio_buffer *buffer)
{
INIT_LIST_HEAD(&buffer->demux_list);
@@ -1156,6 +1250,10 @@ int iio_update_buffers(struct iio_dev *indio_dev,
if (insert_buffer == remove_buffer)
return 0;
+ if (insert_buffer &&
+ (insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT))
+ return -EINVAL;
+
mutex_lock(&iio_dev_opaque->info_exist_lock);
mutex_lock(&indio_dev->mlock);
@@ -1277,6 +1375,22 @@ static ssize_t iio_dma_show_data_available(struct device *dev,
return sysfs_emit(buf, "%zu\n", iio_buffer_data_available(buffer));
}
+static ssize_t direction_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
+
+ switch (buffer->direction) {
+ case IIO_BUFFER_DIRECTION_IN:
+ return sprintf(buf, "in\n");
+ case IIO_BUFFER_DIRECTION_OUT:
+ return sprintf(buf, "out\n");
+ default:
+ return -EINVAL;
+ }
+}
+
static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length,
iio_buffer_write_length);
static struct device_attribute dev_attr_length_ro = __ATTR(length,
@@ -1289,12 +1403,20 @@ static struct device_attribute dev_attr_watermark_ro = __ATTR(watermark,
S_IRUGO, iio_buffer_show_watermark, NULL);
static DEVICE_ATTR(data_available, S_IRUGO,
iio_dma_show_data_available, NULL);
+static DEVICE_ATTR_RO(direction);
+/*
+ * When adding new attributes here, put the at the end, at least until
+ * the code that handles the length/length_ro & watermark/watermark_ro
+ * assignments gets cleaned up. Otherwise these can create some weird
+ * duplicate attributes errors under some setups.
+ */
static struct attribute *iio_buffer_attrs[] = {
&dev_attr_length.attr,
&dev_attr_enable.attr,
&dev_attr_watermark.attr,
&dev_attr_data_available.attr,
+ &dev_attr_direction.attr,
};
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
@@ -1397,6 +1519,7 @@ static const struct file_operations iio_buffer_chrdev_fileops = {
.owner = THIS_MODULE,
.llseek = noop_llseek,
.read = iio_buffer_read,
+ .write = iio_buffer_write,
.poll = iio_buffer_poll,
.release = iio_buffer_chrdev_release,
};
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 2dbb37e09b8c..537a08549a69 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1822,6 +1822,7 @@ static const struct file_operations iio_buffer_fileops = {
.owner = THIS_MODULE,
.llseek = noop_llseek,
.read = iio_buffer_read_outer_addr,
+ .write = iio_buffer_write_outer_addr,
.poll = iio_buffer_poll_addr,
.unlocked_ioctl = iio_ioctl,
.compat_ioctl = compat_ptr_ioctl,