Commit ee708e6b authored by Alexandru Ardelean's avatar Alexandru Ardelean Committed by Jonathan Cameron

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: default avatarAlexandru Ardelean <alexandru.ardelean@analog.com>
Link: https://lore.kernel.org/r/20210215104043.91251-20-alexandru.ardelean@analog.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 738f6ba1
...@@ -290,9 +290,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev, ...@@ -290,9 +290,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
indio_dev->modes |= INDIO_BUFFER_HARDWARE; indio_dev->modes |= INDIO_BUFFER_HARDWARE;
iio_device_attach_buffer(indio_dev, buffer); return iio_device_attach_buffer(indio_dev, buffer);
return 0;
} }
EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup); EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup);
......
...@@ -50,8 +50,6 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, ...@@ -50,8 +50,6 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
goto error_ret; goto error_ret;
} }
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->pollfunc = iio_alloc_pollfunc(h, indio_dev->pollfunc = iio_alloc_pollfunc(h,
thread, thread,
IRQF_ONESHOT, IRQF_ONESHOT,
...@@ -72,10 +70,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, ...@@ -72,10 +70,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
buffer->attrs = buffer_attrs; buffer->attrs = buffer_attrs;
ret = iio_device_attach_buffer(indio_dev, buffer);
if (ret < 0)
goto error_dealloc_pollfunc;
return 0; return 0;
error_dealloc_pollfunc:
iio_dealloc_pollfunc(indio_dev->pollfunc);
error_kfifo_free: error_kfifo_free:
iio_kfifo_free(indio_dev->buffer); iio_kfifo_free(buffer);
error_ret: error_ret:
return ret; return ret;
} }
......
...@@ -235,12 +235,10 @@ int devm_iio_kfifo_buffer_setup(struct device *dev, ...@@ -235,12 +235,10 @@ int devm_iio_kfifo_buffer_setup(struct device *dev,
if (!buffer) if (!buffer)
return -ENOMEM; return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->modes |= mode_flags; indio_dev->modes |= mode_flags;
indio_dev->setup_ops = setup_ops; indio_dev->setup_ops = setup_ops;
return 0; return iio_device_attach_buffer(indio_dev, buffer);
} }
EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup); EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup);
......
...@@ -69,29 +69,31 @@ __poll_t iio_buffer_poll(struct file *filp, ...@@ -69,29 +69,31 @@ __poll_t iio_buffer_poll(struct file *filp,
ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps); size_t n, loff_t *f_ps);
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev); int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
void iio_buffer_free_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_poll_addr (&iio_buffer_poll)
#define iio_buffer_read_outer_addr (&iio_buffer_read_outer) #define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_disable_all_buffers(struct iio_dev *indio_dev);
void iio_buffer_wakeup_poll(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 #else
#define iio_buffer_poll_addr NULL #define iio_buffer_poll_addr NULL
#define iio_buffer_read_outer_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; 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_disable_all_buffers(struct iio_dev *indio_dev) {}
static inline void iio_buffer_wakeup_poll(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 #endif
......
...@@ -193,12 +193,14 @@ __poll_t iio_buffer_poll(struct file *filp, ...@@ -193,12 +193,14 @@ __poll_t iio_buffer_poll(struct file *filp,
*/ */
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) 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;
if (!buffer) unsigned int i;
return;
for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
buffer = iio_dev_opaque->attached_buffers[i];
wake_up(&buffer->pollq); wake_up(&buffer->pollq);
}
} }
void iio_buffer_init(struct iio_buffer *buffer) void iio_buffer_init(struct iio_buffer *buffer)
...@@ -212,6 +214,18 @@ 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); 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, static ssize_t iio_show_scan_index(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -1452,11 +1466,13 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) ...@@ -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); 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; const struct iio_chan_spec *channels;
int i; struct iio_buffer *buffer;
int unwind_idx;
int ret, i;
channels = indio_dev->channels; channels = indio_dev->channels;
if (channels) { if (channels) {
...@@ -1467,22 +1483,46 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) ...@@ -1467,22 +1483,46 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
indio_dev->masklength = ml; indio_dev->masklength = ml;
} }
if (!buffer) if (!iio_dev_opaque->attached_buffers_cnt)
return 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; return 0;
return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 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; return;
iio_buffer_unregister_legacy_sysfs_groups(indio_dev); iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
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); __iio_buffer_free_sysfs_and_mask(buffer);
}
kfree(iio_dev_opaque->attached_buffers);
} }
/** /**
...@@ -1600,13 +1640,35 @@ EXPORT_SYMBOL_GPL(iio_buffer_put); ...@@ -1600,13 +1640,35 @@ EXPORT_SYMBOL_GPL(iio_buffer_put);
* @indio_dev: The device the buffer should be attached to * @indio_dev: The device the buffer should be attached to
* @buffer: The buffer to attach to the device * @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 * 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 * the device until the device is freed. For legacy reasons, the first attached
* most once per device. * buffer will also be assigned to 'indio_dev->buffer'.
*/ */
void iio_device_attach_buffer(struct iio_dev *indio_dev, int iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer) 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); EXPORT_SYMBOL_GPL(iio_device_attach_buffer);
...@@ -1585,7 +1585,7 @@ static void iio_dev_release(struct device *device) ...@@ -1585,7 +1585,7 @@ static void iio_dev_release(struct device *device)
iio_device_unregister_eventset(indio_dev); iio_device_unregister_eventset(indio_dev);
iio_device_unregister_sysfs(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); ida_simple_remove(&iio_ida, indio_dev->id);
kfree(iio_dev_opaque); kfree(iio_dev_opaque);
...@@ -1862,7 +1862,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) ...@@ -1862,7 +1862,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
iio_device_register_debugfs(indio_dev); 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) { if (ret) {
dev_err(indio_dev->dev.parent, dev_err(indio_dev->dev.parent,
"Failed to create buffer sysfs interfaces\n"); "Failed to create buffer sysfs interfaces\n");
...@@ -1888,12 +1888,12 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) ...@@ -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 == NULL)
indio_dev->setup_ops = &noop_ring_setup_ops; 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); cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
else if (iio_dev_opaque->event_interface) else if (iio_dev_opaque->event_interface)
cdev_init(&indio_dev->chrdev, &iio_event_fileops); 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->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
indio_dev->chrdev.owner = this_mod; indio_dev->chrdev.owner = this_mod;
} }
...@@ -1912,7 +1912,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) ...@@ -1912,7 +1912,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
error_free_sysfs: error_free_sysfs:
iio_device_unregister_sysfs(indio_dev); iio_device_unregister_sysfs(indio_dev);
error_buffer_free_sysfs: error_buffer_free_sysfs:
iio_buffer_free_sysfs_and_mask(indio_dev); iio_buffers_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs: error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev); iio_device_unregister_debugfs(indio_dev);
return ret; return ret;
...@@ -1946,7 +1946,7 @@ void iio_device_unregister(struct iio_dev *indio_dev) ...@@ -1946,7 +1946,7 @@ void iio_device_unregister(struct iio_dev *indio_dev)
mutex_unlock(&indio_dev->info_exist_lock); 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); EXPORT_SYMBOL(iio_device_unregister);
......
...@@ -41,7 +41,7 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev, ...@@ -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, bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
const unsigned long *mask); const unsigned long *mask);
void iio_device_attach_buffer(struct iio_dev *indio_dev, int iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer); struct iio_buffer *buffer);
#endif /* _IIO_BUFFER_GENERIC_H_ */ #endif /* _IIO_BUFFER_GENERIC_H_ */
...@@ -112,6 +112,9 @@ struct iio_buffer { ...@@ -112,6 +112,9 @@ struct iio_buffer {
/* @demux_bounce: Buffer for doing gather from incoming scan. */ /* @demux_bounce: Buffer for doing gather from incoming scan. */
void *demux_bounce; 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. */ /* @buffer_list: Entry in the devices list of current buffers. */
struct list_head buffer_list; struct list_head buffer_list;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* struct iio_dev_opaque - industrial I/O device opaque information * struct iio_dev_opaque - industrial I/O device opaque information
* @indio_dev: public industrial I/O device information * @indio_dev: public industrial I/O device information
* @event_interface: event chrdevs associated with interrupt lines * @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 * @buffer_list: list of all buffers currently attached
* @channel_attr_list: keep track of automatically created channel * @channel_attr_list: keep track of automatically created channel
* attributes * attributes
...@@ -24,6 +26,8 @@ ...@@ -24,6 +26,8 @@
struct iio_dev_opaque { struct iio_dev_opaque {
struct iio_dev indio_dev; struct iio_dev indio_dev;
struct iio_event_interface *event_interface; struct iio_event_interface *event_interface;
struct iio_buffer **attached_buffers;
unsigned int attached_buffers_cnt;
struct list_head buffer_list; struct list_head buffer_list;
struct list_head channel_attr_list; struct list_head channel_attr_list;
struct attribute_group chan_attr_group; struct attribute_group chan_attr_group;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment