Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: buffer: add ioctl() to support opening extra buffers for IIO device

With this change, an ioctl() call is added to open a character device for a
buffer. The ioctl() number is 'i' 0x91, which follows the
IIO_GET_EVENT_FD_IOCTL ioctl.

The ioctl() will return an FD for the requested buffer index. The indexes
are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
variable).

Since there doesn't seem to be a sane way to return the FD for buffer0 to
be the same FD for the /dev/iio:deviceX, this ioctl() will return another
FD for buffer0 (or the first buffer). This duplicate FD will be able to
access the same buffer object (for buffer0) as accessing directly the
/dev/iio:deviceX chardev.

Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
index for each buffer (and the count) can be deduced from the
'/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
bufferY folders).

Used following C code to test this:
-------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h"
#include <errno.h>

#define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)

int main(int argc, char *argv[])
{
int fd;
int fd1;
int ret;

if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
return -1;
}

fprintf(stderr, "Using FD %d\n", fd);

fd1 = atoi(argv[1]);

ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
if (ret < 0) {
fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
close(fd);
return -1;
}

fprintf(stderr, "Got FD %d\n", fd1);

close(fd1);
close(fd);

return 0;
}
-------------------------------------------------------------------

Results are:
-------------------------------------------------------------------
# ./test 0
Using FD 3
Got FD 4

# ./test 1
Using FD 3
Got FD 4

# ./test 2
Using FD 3
Got FD 4

# ./test 3
Using FD 3
Got FD 4

# ls /sys/bus/iio/devices/iio\:device0
buffer buffer0 buffer1 buffer2 buffer3 dev
in_voltage_sampling_frequency in_voltage_scale
in_voltage_scale_available
name of_node power scan_elements subsystem uevent
-------------------------------------------------------------------

iio:device0 has some fake kfifo buffers attached to an IIO device.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
Link: https://lore.kernel.org/r/20210215104043.91251-21-alexandru.ardelean@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Alexandru Ardelean and committed by
Jonathan Cameron
f73f7f4d ee708e6b

+162 -11
+6 -6
drivers/iio/iio_core.h
··· 64 64 #ifdef CONFIG_IIO_BUFFER 65 65 struct poll_table_struct; 66 66 67 - __poll_t iio_buffer_poll(struct file *filp, 68 - struct poll_table_struct *wait); 69 - ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, 70 - size_t n, loff_t *f_ps); 67 + __poll_t iio_buffer_poll_wrapper(struct file *filp, 68 + struct poll_table_struct *wait); 69 + ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, 70 + size_t n, loff_t *f_ps); 71 71 72 72 int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev); 73 73 void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); 74 74 75 - #define iio_buffer_poll_addr (&iio_buffer_poll) 76 - #define iio_buffer_read_outer_addr (&iio_buffer_read_outer) 75 + #define iio_buffer_poll_addr (&iio_buffer_poll_wrapper) 76 + #define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper) 77 77 78 78 void iio_disable_all_buffers(struct iio_dev *indio_dev); 79 79 void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
+139 -5
drivers/iio/industrialio-buffer.c
··· 9 9 * - Better memory allocation techniques? 10 10 * - Alternative access techniques? 11 11 */ 12 + #include <linux/anon_inodes.h> 12 13 #include <linux/kernel.h> 13 14 #include <linux/export.h> 14 15 #include <linux/device.h> 16 + #include <linux/file.h> 15 17 #include <linux/fs.h> 16 18 #include <linux/cdev.h> 17 19 #include <linux/slab.h> ··· 91 89 } 92 90 93 91 /** 94 - * iio_buffer_read_outer() - chrdev read for buffer access 92 + * iio_buffer_read() - chrdev read for buffer access 95 93 * @filp: File structure pointer for the char device 96 94 * @buf: Destination buffer for iio buffer read 97 95 * @n: First n bytes to read ··· 103 101 * Return: negative values corresponding to error codes or ret != 0 104 102 * for ending the reading activity 105 103 **/ 106 - ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, 107 - size_t n, loff_t *f_ps) 104 + static ssize_t iio_buffer_read(struct file *filp, char __user *buf, 105 + size_t n, loff_t *f_ps) 108 106 { 109 107 struct iio_dev_buffer_pair *ib = filp->private_data; 110 108 struct iio_buffer *rb = ib->buffer; ··· 170 168 * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading 171 169 * or 0 for other cases 172 170 */ 173 - __poll_t iio_buffer_poll(struct file *filp, 174 - struct poll_table_struct *wait) 171 + static __poll_t iio_buffer_poll(struct file *filp, 172 + struct poll_table_struct *wait) 175 173 { 176 174 struct iio_dev_buffer_pair *ib = filp->private_data; 177 175 struct iio_buffer *rb = ib->buffer; ··· 184 182 if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) 185 183 return EPOLLIN | EPOLLRDNORM; 186 184 return 0; 185 + } 186 + 187 + ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, 188 + size_t n, loff_t *f_ps) 189 + { 190 + struct iio_dev_buffer_pair *ib = filp->private_data; 191 + struct iio_buffer *rb = ib->buffer; 192 + 193 + /* check if buffer was opened through new API */ 194 + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) 195 + return -EBUSY; 196 + 197 + return iio_buffer_read(filp, buf, n, f_ps); 198 + } 199 + 200 + __poll_t iio_buffer_poll_wrapper(struct file *filp, 201 + struct poll_table_struct *wait) 202 + { 203 + struct iio_dev_buffer_pair *ib = filp->private_data; 204 + struct iio_buffer *rb = ib->buffer; 205 + 206 + /* check if buffer was opened through new API */ 207 + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) 208 + return 0; 209 + 210 + return iio_buffer_poll(filp, wait); 187 211 } 188 212 189 213 /** ··· 1372 1344 kfree(iio_dev_opaque->legacy_scan_el_group.attrs); 1373 1345 } 1374 1346 1347 + static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep) 1348 + { 1349 + struct iio_dev_buffer_pair *ib = filep->private_data; 1350 + struct iio_dev *indio_dev = ib->indio_dev; 1351 + struct iio_buffer *buffer = ib->buffer; 1352 + 1353 + wake_up(&buffer->pollq); 1354 + 1355 + kfree(ib); 1356 + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags); 1357 + iio_device_put(indio_dev); 1358 + 1359 + return 0; 1360 + } 1361 + 1362 + static const struct file_operations iio_buffer_chrdev_fileops = { 1363 + .owner = THIS_MODULE, 1364 + .llseek = noop_llseek, 1365 + .read = iio_buffer_read, 1366 + .poll = iio_buffer_poll, 1367 + .release = iio_buffer_chrdev_release, 1368 + }; 1369 + 1370 + static long iio_device_buffer_getfd(struct iio_dev *indio_dev, unsigned long arg) 1371 + { 1372 + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 1373 + int __user *ival = (int __user *)arg; 1374 + struct iio_dev_buffer_pair *ib; 1375 + struct iio_buffer *buffer; 1376 + int fd, idx, ret; 1377 + 1378 + if (copy_from_user(&idx, ival, sizeof(idx))) 1379 + return -EFAULT; 1380 + 1381 + if (idx >= iio_dev_opaque->attached_buffers_cnt) 1382 + return -ENODEV; 1383 + 1384 + iio_device_get(indio_dev); 1385 + 1386 + buffer = iio_dev_opaque->attached_buffers[idx]; 1387 + 1388 + if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) { 1389 + ret = -EBUSY; 1390 + goto error_iio_dev_put; 1391 + } 1392 + 1393 + ib = kzalloc(sizeof(*ib), GFP_KERNEL); 1394 + if (!ib) { 1395 + ret = -ENOMEM; 1396 + goto error_clear_busy_bit; 1397 + } 1398 + 1399 + ib->indio_dev = indio_dev; 1400 + ib->buffer = buffer; 1401 + 1402 + fd = anon_inode_getfd("iio:buffer", &iio_buffer_chrdev_fileops, 1403 + ib, O_RDWR | O_CLOEXEC); 1404 + if (fd < 0) { 1405 + ret = fd; 1406 + goto error_free_ib; 1407 + } 1408 + 1409 + if (copy_to_user(ival, &fd, sizeof(fd))) { 1410 + put_unused_fd(fd); 1411 + ret = -EFAULT; 1412 + goto error_free_ib; 1413 + } 1414 + 1415 + return fd; 1416 + 1417 + error_free_ib: 1418 + kfree(ib); 1419 + error_clear_busy_bit: 1420 + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags); 1421 + error_iio_dev_put: 1422 + iio_device_put(indio_dev); 1423 + return ret; 1424 + } 1425 + 1426 + static long iio_device_buffer_ioctl(struct iio_dev *indio_dev, struct file *filp, 1427 + unsigned int cmd, unsigned long arg) 1428 + { 1429 + switch (cmd) { 1430 + case IIO_BUFFER_GET_FD_IOCTL: 1431 + return iio_device_buffer_getfd(indio_dev, arg); 1432 + default: 1433 + return IIO_IOCTL_UNHANDLED; 1434 + } 1435 + } 1436 + 1375 1437 static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, 1376 1438 struct iio_dev *indio_dev, 1377 1439 int index) ··· 1591 1473 struct iio_buffer *buffer; 1592 1474 int unwind_idx; 1593 1475 int ret, i; 1476 + size_t sz; 1594 1477 1595 1478 channels = indio_dev->channels; 1596 1479 if (channels) { ··· 1613 1494 goto error_unwind_sysfs_and_mask; 1614 1495 } 1615 1496 } 1497 + unwind_idx = iio_dev_opaque->attached_buffers_cnt - 1; 1498 + 1499 + sz = sizeof(*(iio_dev_opaque->buffer_ioctl_handler)); 1500 + iio_dev_opaque->buffer_ioctl_handler = kzalloc(sz, GFP_KERNEL); 1501 + if (!iio_dev_opaque->buffer_ioctl_handler) { 1502 + ret = -ENOMEM; 1503 + goto error_unwind_sysfs_and_mask; 1504 + } 1505 + 1506 + iio_dev_opaque->buffer_ioctl_handler->ioctl = iio_device_buffer_ioctl; 1507 + iio_device_ioctl_handler_register(indio_dev, 1508 + iio_dev_opaque->buffer_ioctl_handler); 1616 1509 1617 1510 return 0; 1618 1511 ··· 1645 1514 1646 1515 if (!iio_dev_opaque->attached_buffers_cnt) 1647 1516 return; 1517 + 1518 + iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler); 1519 + kfree(iio_dev_opaque->buffer_ioctl_handler); 1648 1520 1649 1521 iio_buffer_unregister_legacy_sysfs_groups(indio_dev); 1650 1522
+5
include/linux/iio/buffer_impl.h
··· 6 6 7 7 #ifdef CONFIG_IIO_BUFFER 8 8 9 + #include <uapi/linux/iio/buffer.h> 10 + 9 11 struct iio_dev; 10 12 struct iio_buffer; 11 13 ··· 73 71 struct iio_buffer { 74 72 /** @length: Number of datums in buffer. */ 75 73 unsigned int length; 74 + 75 + /** @flags: File ops flags including busy flag. */ 76 + unsigned long flags; 76 77 77 78 /** @bytes_per_datum: Size of individual datum including timestamp. */ 78 79 size_t bytes_per_datum;
+2
include/linux/iio/iio-opaque.h
··· 9 9 * @event_interface: event chrdevs associated with interrupt lines 10 10 * @attached_buffers: array of buffers statically attached by the driver 11 11 * @attached_buffers_cnt: number of buffers in the array of statically attached buffers 12 + * @buffer_ioctl_handler: ioctl() handler for this IIO device's buffer interface 12 13 * @buffer_list: list of all buffers currently attached 13 14 * @channel_attr_list: keep track of automatically created channel 14 15 * attributes ··· 29 28 struct iio_event_interface *event_interface; 30 29 struct iio_buffer **attached_buffers; 31 30 unsigned int attached_buffers_cnt; 31 + struct iio_ioctl_handler *buffer_ioctl_handler; 32 32 struct list_head buffer_list; 33 33 struct list_head channel_attr_list; 34 34 struct attribute_group chan_attr_group;
+10
include/uapi/linux/iio/buffer.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + /* industrial I/O buffer definitions needed both in and out of kernel 3 + */ 4 + 5 + #ifndef _UAPI_IIO_BUFFER_H_ 6 + #define _UAPI_IIO_BUFFER_H_ 7 + 8 + #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int) 9 + 10 + #endif /* _UAPI_IIO_BUFFER_H_ */