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

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.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 738f6ba1
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -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);

+7 −3
Original line number Diff line number Diff line
@@ -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;
}
+1 −3
Original line number Diff line number Diff line
@@ -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);

+6 −4
Original line number Diff line number Diff line
@@ -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

+81 −19
Original line number Diff line number Diff line
@@ -193,13 +193,15 @@ __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;

	if (!buffer)
		return;
	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];
		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,24 +1483,48 @@ 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;

	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 __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;

	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);
	}

	kfree(iio_dev_opaque->attached_buffers);
}

/**
 * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
 * @indio_dev: the iio device
@@ -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,
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);
Loading