iio: buffer: introduce support for attaching more IIO buffers

With this change, calling iio_device_attach_buffer() will actually attach
more buffers.
Right now this doesn't do any validation of whether a buffer is attached
twice; maybe that can be added later (if needed). Attaching a buffer more
than once should yield noticeably bad results.

The first buffer is the legacy buffer, so a reference is kept to it.

At this point, accessing the data for the extra buffers (that are added
after the first one) isn't possible yet.

The iio_device_attach_buffer() is also changed to return an error code,
which for now is -ENOMEM if the array could not be realloc-ed for more
buffers.
To adapt to this new change iio_device_attach_buffer() is called last in
all place where it's called. The realloc failure is a bit difficult to
handle during un-managed calls when unwinding, so it's better to have this
as the last error in the setup_buffer calls.

At this point, no driver should call iio_device_attach_buffer() directly,
it should call one of the {devm_}iio_triggered_buffer_setup() or
devm_iio_kfifo_buffer_setup() or devm_iio_dmaengine_buffer_setup()
functions. This makes iio_device_attach_buffer() a bit easier to handle.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
Link: https://lore.kernel.org/r/20210215104043.91251-20-alexandru.ardelean@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Alexandru Ardelean 2021-02-15 12:40:38 +02:00 коммит произвёл Jonathan Cameron
Родитель 738f6ba118
Коммит ee708e6baa
9 изменённых файлов: 111 добавлений и 40 удалений

Просмотреть файл

@ -290,9 +290,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
iio_device_attach_buffer(indio_dev, buffer);
return 0;
return iio_device_attach_buffer(indio_dev, buffer);
}
EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup);

Просмотреть файл

@ -50,8 +50,6 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
goto error_ret;
}
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->pollfunc = iio_alloc_pollfunc(h,
thread,
IRQF_ONESHOT,
@ -72,10 +70,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
buffer->attrs = buffer_attrs;
ret = iio_device_attach_buffer(indio_dev, buffer);
if (ret < 0)
goto error_dealloc_pollfunc;
return 0;
error_dealloc_pollfunc:
iio_dealloc_pollfunc(indio_dev->pollfunc);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
iio_kfifo_free(buffer);
error_ret:
return ret;
}

Просмотреть файл

@ -235,12 +235,10 @@ int devm_iio_kfifo_buffer_setup(struct device *dev,
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->modes |= mode_flags;
indio_dev->setup_ops = setup_ops;
return 0;
return iio_device_attach_buffer(indio_dev, buffer);
}
EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup);

Просмотреть файл

@ -69,29 +69,31 @@ __poll_t iio_buffer_poll(struct file *filp,
ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps);
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
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)
#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
void iio_disable_all_buffers(struct iio_dev *indio_dev);
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
void iio_buffers_put(struct iio_dev *indio_dev);
#else
#define iio_buffer_poll_addr NULL
#define iio_buffer_read_outer_addr NULL
static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
return 0;
}
static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
static inline void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
static inline void iio_buffers_put(struct iio_dev *indio_dev) {}
#endif

Просмотреть файл

@ -193,12 +193,14 @@ __poll_t iio_buffer_poll(struct file *filp,
*/
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_buffer *buffer;
unsigned int i;
if (!buffer)
return;
wake_up(&buffer->pollq);
for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
buffer = iio_dev_opaque->attached_buffers[i];
wake_up(&buffer->pollq);
}
}
void iio_buffer_init(struct iio_buffer *buffer)
@ -212,6 +214,18 @@ void iio_buffer_init(struct iio_buffer *buffer)
}
EXPORT_SYMBOL(iio_buffer_init);
void iio_buffers_put(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_buffer *buffer;
unsigned int i;
for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
buffer = iio_dev_opaque->attached_buffers[i];
iio_buffer_put(buffer);
}
}
static ssize_t iio_show_scan_index(struct device *dev,
struct device_attribute *attr,
char *buf)
@ -1452,11 +1466,13 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
iio_free_chan_devattr_list(&buffer->buffer_attr_list);
}
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
const struct iio_chan_spec *channels;
int i;
struct iio_buffer *buffer;
int unwind_idx;
int ret, i;
channels = indio_dev->channels;
if (channels) {
@ -1467,22 +1483,46 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
indio_dev->masklength = ml;
}
if (!buffer)
if (!iio_dev_opaque->attached_buffers_cnt)
return 0;
return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0);
for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
buffer = iio_dev_opaque->attached_buffers[i];
ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i);
if (ret) {
unwind_idx = i;
goto error_unwind_sysfs_and_mask;
}
}
return 0;
error_unwind_sysfs_and_mask:
for (; unwind_idx >= 0; unwind_idx--) {
buffer = iio_dev_opaque->attached_buffers[unwind_idx];
__iio_buffer_free_sysfs_and_mask(buffer);
}
kfree(iio_dev_opaque->attached_buffers);
return ret;
}
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_buffer *buffer;
int i;
if (!buffer)
if (!iio_dev_opaque->attached_buffers_cnt)
return;
iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
__iio_buffer_free_sysfs_and_mask(buffer);
for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
buffer = iio_dev_opaque->attached_buffers[i];
__iio_buffer_free_sysfs_and_mask(buffer);
}
kfree(iio_dev_opaque->attached_buffers);
}
/**
@ -1600,13 +1640,35 @@ EXPORT_SYMBOL_GPL(iio_buffer_put);
* @indio_dev: The device the buffer should be attached to
* @buffer: The buffer to attach to the device
*
* Return 0 if successful, negative if error.
*
* This function attaches a buffer to a IIO device. The buffer stays attached to
* the device until the device is freed. The function should only be called at
* most once per device.
* the device until the device is freed. For legacy reasons, the first attached
* buffer will also be assigned to 'indio_dev->buffer'.
*/
void iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
int iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
{
indio_dev->buffer = iio_buffer_get(buffer);
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_buffer **new, **old = iio_dev_opaque->attached_buffers;
unsigned int cnt = iio_dev_opaque->attached_buffers_cnt;
cnt++;
new = krealloc(old, sizeof(*new) * cnt, GFP_KERNEL);
if (!new)
return -ENOMEM;
iio_dev_opaque->attached_buffers = new;
buffer = iio_buffer_get(buffer);
/* first buffer is legacy; attach it to the IIO device directly */
if (!indio_dev->buffer)
indio_dev->buffer = buffer;
iio_dev_opaque->attached_buffers[cnt - 1] = buffer;
iio_dev_opaque->attached_buffers_cnt = cnt;
return 0;
}
EXPORT_SYMBOL_GPL(iio_device_attach_buffer);

Просмотреть файл

@ -1585,7 +1585,7 @@ static void iio_dev_release(struct device *device)
iio_device_unregister_eventset(indio_dev);
iio_device_unregister_sysfs(indio_dev);
iio_buffer_put(indio_dev->buffer);
iio_buffers_put(indio_dev);
ida_simple_remove(&iio_ida, indio_dev->id);
kfree(iio_dev_opaque);
@ -1862,7 +1862,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
iio_device_register_debugfs(indio_dev);
ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
ret = iio_buffers_alloc_sysfs_and_mask(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to create buffer sysfs interfaces\n");
@ -1888,12 +1888,12 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
indio_dev->setup_ops == NULL)
indio_dev->setup_ops = &noop_ring_setup_ops;
if (indio_dev->buffer)
if (iio_dev_opaque->attached_buffers_cnt)
cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
else if (iio_dev_opaque->event_interface)
cdev_init(&indio_dev->chrdev, &iio_event_fileops);
if (indio_dev->buffer || iio_dev_opaque->event_interface) {
if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) {
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
indio_dev->chrdev.owner = this_mod;
}
@ -1912,7 +1912,7 @@ error_unreg_eventset:
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
error_buffer_free_sysfs:
iio_buffer_free_sysfs_and_mask(indio_dev);
iio_buffers_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
@ -1946,7 +1946,7 @@ void iio_device_unregister(struct iio_dev *indio_dev)
mutex_unlock(&indio_dev->info_exist_lock);
iio_buffer_free_sysfs_and_mask(indio_dev);
iio_buffers_free_sysfs_and_mask(indio_dev);
}
EXPORT_SYMBOL(iio_device_unregister);

Просмотреть файл

@ -41,7 +41,7 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev,
bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
const unsigned long *mask);
void iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer);
int iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer);
#endif /* _IIO_BUFFER_GENERIC_H_ */

Просмотреть файл

@ -112,6 +112,9 @@ struct iio_buffer {
/* @demux_bounce: Buffer for doing gather from incoming scan. */
void *demux_bounce;
/* @attached_entry: Entry in the devices list of buffers attached by the driver. */
struct list_head attached_entry;
/* @buffer_list: Entry in the devices list of current buffers. */
struct list_head buffer_list;

Просмотреть файл

@ -7,6 +7,8 @@
* struct iio_dev_opaque - industrial I/O device opaque information
* @indio_dev: public industrial I/O device information
* @event_interface: event chrdevs associated with interrupt lines
* @attached_buffers: array of buffers statically attached by the driver
* @attached_buffers_cnt: number of buffers in the array of statically attached buffers
* @buffer_list: list of all buffers currently attached
* @channel_attr_list: keep track of automatically created channel
* attributes
@ -24,6 +26,8 @@
struct iio_dev_opaque {
struct iio_dev indio_dev;
struct iio_event_interface *event_interface;
struct iio_buffer **attached_buffers;
unsigned int attached_buffers_cnt;
struct list_head buffer_list;
struct list_head channel_attr_list;
struct attribute_group chan_attr_group;