18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* The industrial I/O callback buffer
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
118c2ecf20Sopenharmony_ci#include <linux/iio/buffer_impl.h>
128c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct iio_cb_buffer {
158c2ecf20Sopenharmony_ci	struct iio_buffer buffer;
168c2ecf20Sopenharmony_ci	int (*cb)(const void *data, void *private);
178c2ecf20Sopenharmony_ci	void *private;
188c2ecf20Sopenharmony_ci	struct iio_channel *channels;
198c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	return container_of(buffer, struct iio_cb_buffer, buffer);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
308c2ecf20Sopenharmony_ci	return cb_buff->cb(data, cb_buff->private);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void iio_buffer_cb_release(struct iio_buffer *buffer)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	bitmap_free(cb_buff->buffer.scan_mask);
388c2ecf20Sopenharmony_ci	kfree(cb_buff);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic const struct iio_buffer_access_funcs iio_cb_access = {
428c2ecf20Sopenharmony_ci	.store_to = &iio_buffer_cb_store_to,
438c2ecf20Sopenharmony_ci	.release = &iio_buffer_cb_release,
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	.modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
498c2ecf20Sopenharmony_ci					     int (*cb)(const void *data,
508c2ecf20Sopenharmony_ci						       void *private),
518c2ecf20Sopenharmony_ci					     void *private)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int ret;
548c2ecf20Sopenharmony_ci	struct iio_cb_buffer *cb_buff;
558c2ecf20Sopenharmony_ci	struct iio_channel *chan;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (!cb) {
588c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid arguments: A callback must be provided!\n");
598c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
638c2ecf20Sopenharmony_ci	if (cb_buff == NULL)
648c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	iio_buffer_init(&cb_buff->buffer);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	cb_buff->private = private;
698c2ecf20Sopenharmony_ci	cb_buff->cb = cb;
708c2ecf20Sopenharmony_ci	cb_buff->buffer.access = &iio_cb_access;
718c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cb_buff->buffer.demux_list);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	cb_buff->channels = iio_channel_get_all(dev);
748c2ecf20Sopenharmony_ci	if (IS_ERR(cb_buff->channels)) {
758c2ecf20Sopenharmony_ci		ret = PTR_ERR(cb_buff->channels);
768c2ecf20Sopenharmony_ci		goto error_free_cb_buff;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
808c2ecf20Sopenharmony_ci	cb_buff->buffer.scan_mask = bitmap_zalloc(cb_buff->indio_dev->masklength,
818c2ecf20Sopenharmony_ci						  GFP_KERNEL);
828c2ecf20Sopenharmony_ci	if (cb_buff->buffer.scan_mask == NULL) {
838c2ecf20Sopenharmony_ci		ret = -ENOMEM;
848c2ecf20Sopenharmony_ci		goto error_release_channels;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	chan = &cb_buff->channels[0];
878c2ecf20Sopenharmony_ci	while (chan->indio_dev) {
888c2ecf20Sopenharmony_ci		if (chan->indio_dev != cb_buff->indio_dev) {
898c2ecf20Sopenharmony_ci			ret = -EINVAL;
908c2ecf20Sopenharmony_ci			goto error_free_scan_mask;
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci		set_bit(chan->channel->scan_index,
938c2ecf20Sopenharmony_ci			cb_buff->buffer.scan_mask);
948c2ecf20Sopenharmony_ci		chan++;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return cb_buff;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cierror_free_scan_mask:
1008c2ecf20Sopenharmony_ci	bitmap_free(cb_buff->buffer.scan_mask);
1018c2ecf20Sopenharmony_cierror_release_channels:
1028c2ecf20Sopenharmony_ci	iio_channel_release_all(cb_buff->channels);
1038c2ecf20Sopenharmony_cierror_free_cb_buff:
1048c2ecf20Sopenharmony_ci	kfree(cb_buff);
1058c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciint iio_channel_cb_set_buffer_watermark(struct iio_cb_buffer *cb_buff,
1108c2ecf20Sopenharmony_ci					size_t watermark)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (!watermark)
1138c2ecf20Sopenharmony_ci		return -EINVAL;
1148c2ecf20Sopenharmony_ci	cb_buff->buffer.watermark = watermark;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_cb_set_buffer_watermark);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ciint iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
1238c2ecf20Sopenharmony_ci				  NULL);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_civoid iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_civoid iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	iio_channel_release_all(cb_buff->channels);
1368c2ecf20Sopenharmony_ci	iio_buffer_put(&cb_buff->buffer);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct iio_channel
1418c2ecf20Sopenharmony_ci*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	return cb_buffer->channels;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistruct iio_dev
1488c2ecf20Sopenharmony_ci*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	return cb_buffer->indio_dev;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
1558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Industrial I/O callback buffer");
1568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
157