18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/slab.h> 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/device.h> 68c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 78c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 88c2ecf20Sopenharmony_ci#include <linux/mutex.h> 98c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 108c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 118c2ecf20Sopenharmony_ci#include <linux/iio/kfifo_buf.h> 128c2ecf20Sopenharmony_ci#include <linux/iio/buffer_impl.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/poll.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct iio_kfifo { 178c2ecf20Sopenharmony_ci struct iio_buffer buffer; 188c2ecf20Sopenharmony_ci struct kfifo kf; 198c2ecf20Sopenharmony_ci struct mutex user_lock; 208c2ecf20Sopenharmony_ci int update_needed; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic inline int __iio_allocate_kfifo(struct iio_kfifo *buf, 268c2ecf20Sopenharmony_ci size_t bytes_per_datum, unsigned int length) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci if ((length == 0) || (bytes_per_datum == 0)) 298c2ecf20Sopenharmony_ci return -EINVAL; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * Make sure we don't overflow an unsigned int after kfifo rounds up to 338c2ecf20Sopenharmony_ci * the next power of 2. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci if (roundup_pow_of_two(length) > UINT_MAX / bytes_per_datum) 368c2ecf20Sopenharmony_ci return -EINVAL; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return __kfifo_alloc((struct __kfifo *)&buf->kf, length, 398c2ecf20Sopenharmony_ci bytes_per_datum, GFP_KERNEL); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int iio_request_update_kfifo(struct iio_buffer *r) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int ret = 0; 458c2ecf20Sopenharmony_ci struct iio_kfifo *buf = iio_to_kfifo(r); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci mutex_lock(&buf->user_lock); 488c2ecf20Sopenharmony_ci if (buf->update_needed) { 498c2ecf20Sopenharmony_ci kfifo_free(&buf->kf); 508c2ecf20Sopenharmony_ci ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum, 518c2ecf20Sopenharmony_ci buf->buffer.length); 528c2ecf20Sopenharmony_ci if (ret >= 0) 538c2ecf20Sopenharmony_ci buf->update_needed = false; 548c2ecf20Sopenharmony_ci } else { 558c2ecf20Sopenharmony_ci kfifo_reset_out(&buf->kf); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci mutex_unlock(&buf->user_lock); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return ret; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int iio_mark_update_needed_kfifo(struct iio_buffer *r) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct iio_kfifo *kf = iio_to_kfifo(r); 658c2ecf20Sopenharmony_ci kf->update_needed = true; 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci if (r->bytes_per_datum != bpd) { 728c2ecf20Sopenharmony_ci r->bytes_per_datum = bpd; 738c2ecf20Sopenharmony_ci iio_mark_update_needed_kfifo(r); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int iio_set_length_kfifo(struct iio_buffer *r, unsigned int length) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci /* Avoid an invalid state */ 818c2ecf20Sopenharmony_ci if (length < 2) 828c2ecf20Sopenharmony_ci length = 2; 838c2ecf20Sopenharmony_ci if (r->length != length) { 848c2ecf20Sopenharmony_ci r->length = length; 858c2ecf20Sopenharmony_ci iio_mark_update_needed_kfifo(r); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int iio_store_to_kfifo(struct iio_buffer *r, 918c2ecf20Sopenharmony_ci const void *data) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci struct iio_kfifo *kf = iio_to_kfifo(r); 958c2ecf20Sopenharmony_ci ret = kfifo_in(&kf->kf, data, 1); 968c2ecf20Sopenharmony_ci if (ret != 1) 978c2ecf20Sopenharmony_ci return -EBUSY; 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int iio_read_kfifo(struct iio_buffer *r, size_t n, char __user *buf) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int ret, copied; 1048c2ecf20Sopenharmony_ci struct iio_kfifo *kf = iio_to_kfifo(r); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&kf->user_lock)) 1078c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf)) 1108c2ecf20Sopenharmony_ci ret = -EINVAL; 1118c2ecf20Sopenharmony_ci else 1128c2ecf20Sopenharmony_ci ret = kfifo_to_user(&kf->kf, buf, n, &copied); 1138c2ecf20Sopenharmony_ci mutex_unlock(&kf->user_lock); 1148c2ecf20Sopenharmony_ci if (ret < 0) 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return copied; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic size_t iio_kfifo_buf_data_available(struct iio_buffer *r) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct iio_kfifo *kf = iio_to_kfifo(r); 1238c2ecf20Sopenharmony_ci size_t samples; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci mutex_lock(&kf->user_lock); 1268c2ecf20Sopenharmony_ci samples = kfifo_len(&kf->kf); 1278c2ecf20Sopenharmony_ci mutex_unlock(&kf->user_lock); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return samples; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void iio_kfifo_buffer_release(struct iio_buffer *buffer) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct iio_kfifo *kf = iio_to_kfifo(buffer); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mutex_destroy(&kf->user_lock); 1378c2ecf20Sopenharmony_ci kfifo_free(&kf->kf); 1388c2ecf20Sopenharmony_ci kfree(kf); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct iio_buffer_access_funcs kfifo_access_funcs = { 1428c2ecf20Sopenharmony_ci .store_to = &iio_store_to_kfifo, 1438c2ecf20Sopenharmony_ci .read = &iio_read_kfifo, 1448c2ecf20Sopenharmony_ci .data_available = iio_kfifo_buf_data_available, 1458c2ecf20Sopenharmony_ci .request_update = &iio_request_update_kfifo, 1468c2ecf20Sopenharmony_ci .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, 1478c2ecf20Sopenharmony_ci .set_length = &iio_set_length_kfifo, 1488c2ecf20Sopenharmony_ci .release = &iio_kfifo_buffer_release, 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistruct iio_buffer *iio_kfifo_allocate(void) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct iio_kfifo *kf; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci kf = kzalloc(sizeof(*kf), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!kf) 1598c2ecf20Sopenharmony_ci return NULL; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci kf->update_needed = true; 1628c2ecf20Sopenharmony_ci iio_buffer_init(&kf->buffer); 1638c2ecf20Sopenharmony_ci kf->buffer.access = &kfifo_access_funcs; 1648c2ecf20Sopenharmony_ci kf->buffer.length = 2; 1658c2ecf20Sopenharmony_ci mutex_init(&kf->user_lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return &kf->buffer; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_kfifo_allocate); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_civoid iio_kfifo_free(struct iio_buffer *r) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci iio_buffer_put(r); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_kfifo_free); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void devm_iio_kfifo_release(struct device *dev, void *res) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci iio_kfifo_free(*(struct iio_buffer **)res); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate() 1848c2ecf20Sopenharmony_ci * @dev: Device to allocate kfifo buffer for 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * RETURNS: 1878c2ecf20Sopenharmony_ci * Pointer to allocated iio_buffer on success, NULL on failure. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistruct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct iio_buffer **ptr, *r; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (!ptr) 1958c2ecf20Sopenharmony_ci return NULL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci r = iio_kfifo_allocate(); 1988c2ecf20Sopenharmony_ci if (r) { 1998c2ecf20Sopenharmony_ci *ptr = r; 2008c2ecf20Sopenharmony_ci devres_add(dev, ptr); 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci devres_free(ptr); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return r; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_iio_kfifo_allocate); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 210