18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Industrial I/O event handling 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2008 Jonathan Cameron 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on elements of hwmon and input subsystems. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/anon_inodes.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/fs.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/poll.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 198c2ecf20Sopenharmony_ci#include <linux/wait.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/iio-opaque.h> 228c2ecf20Sopenharmony_ci#include "iio_core.h" 238c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 248c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/** 278c2ecf20Sopenharmony_ci * struct iio_event_interface - chrdev interface for an event line 288c2ecf20Sopenharmony_ci * @wait: wait queue to allow blocking reads of events 298c2ecf20Sopenharmony_ci * @det_events: list of detected events 308c2ecf20Sopenharmony_ci * @dev_attr_list: list of event interface sysfs attribute 318c2ecf20Sopenharmony_ci * @flags: file operations related flags including busy flag. 328c2ecf20Sopenharmony_ci * @group: event interface sysfs attribute group 338c2ecf20Sopenharmony_ci * @read_lock: lock to protect kfifo read operations 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistruct iio_event_interface { 368c2ecf20Sopenharmony_ci wait_queue_head_t wait; 378c2ecf20Sopenharmony_ci DECLARE_KFIFO(det_events, struct iio_event_data, 16); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci struct list_head dev_attr_list; 408c2ecf20Sopenharmony_ci unsigned long flags; 418c2ecf20Sopenharmony_ci struct attribute_group group; 428c2ecf20Sopenharmony_ci struct mutex read_lock; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cibool iio_event_enabled(const struct iio_event_interface *ev_int) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return !!test_bit(IIO_BUSY_BIT_POS, &ev_int->flags); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/** 518c2ecf20Sopenharmony_ci * iio_push_event() - try to add event to the list for userspace reading 528c2ecf20Sopenharmony_ci * @indio_dev: IIO device structure 538c2ecf20Sopenharmony_ci * @ev_code: What event 548c2ecf20Sopenharmony_ci * @timestamp: When the event occurred 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * Note: The caller must make sure that this function is not running 578c2ecf20Sopenharmony_ci * concurrently for the same indio_dev more than once. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * This function may be safely used as soon as a valid reference to iio_dev has 608c2ecf20Sopenharmony_ci * been obtained via iio_device_alloc(), but any events that are submitted 618c2ecf20Sopenharmony_ci * before iio_device_register() has successfully completed will be silently 628c2ecf20Sopenharmony_ci * discarded. 638c2ecf20Sopenharmony_ci **/ 648c2ecf20Sopenharmony_ciint iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 678c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; 688c2ecf20Sopenharmony_ci struct iio_event_data ev; 698c2ecf20Sopenharmony_ci int copied; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!ev_int) 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Does anyone care? */ 758c2ecf20Sopenharmony_ci if (iio_event_enabled(ev_int)) { 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ev.id = ev_code; 788c2ecf20Sopenharmony_ci ev.timestamp = timestamp; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci copied = kfifo_put(&ev_int->det_events, ev); 818c2ecf20Sopenharmony_ci if (copied != 0) 828c2ecf20Sopenharmony_ci wake_up_poll(&ev_int->wait, EPOLLIN); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iio_push_event); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/** 908c2ecf20Sopenharmony_ci * iio_event_poll() - poll the event queue to find out if it has data 918c2ecf20Sopenharmony_ci * @filep: File structure pointer to identify the device 928c2ecf20Sopenharmony_ci * @wait: Poll table pointer to add the wait queue on 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading 958c2ecf20Sopenharmony_ci * or a negative error code on failure 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic __poll_t iio_event_poll(struct file *filep, 988c2ecf20Sopenharmony_ci struct poll_table_struct *wait) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = filep->private_data; 1018c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 1028c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; 1038c2ecf20Sopenharmony_ci __poll_t events = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!indio_dev->info) 1068c2ecf20Sopenharmony_ci return events; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci poll_wait(filep, &ev_int->wait, wait); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!kfifo_is_empty(&ev_int->det_events)) 1118c2ecf20Sopenharmony_ci events = EPOLLIN | EPOLLRDNORM; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return events; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic ssize_t iio_event_chrdev_read(struct file *filep, 1178c2ecf20Sopenharmony_ci char __user *buf, 1188c2ecf20Sopenharmony_ci size_t count, 1198c2ecf20Sopenharmony_ci loff_t *f_ps) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = filep->private_data; 1228c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 1238c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; 1248c2ecf20Sopenharmony_ci unsigned int copied; 1258c2ecf20Sopenharmony_ci int ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!indio_dev->info) 1288c2ecf20Sopenharmony_ci return -ENODEV; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (count < sizeof(struct iio_event_data)) 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci do { 1348c2ecf20Sopenharmony_ci if (kfifo_is_empty(&ev_int->det_events)) { 1358c2ecf20Sopenharmony_ci if (filep->f_flags & O_NONBLOCK) 1368c2ecf20Sopenharmony_ci return -EAGAIN; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ret = wait_event_interruptible(ev_int->wait, 1398c2ecf20Sopenharmony_ci !kfifo_is_empty(&ev_int->det_events) || 1408c2ecf20Sopenharmony_ci indio_dev->info == NULL); 1418c2ecf20Sopenharmony_ci if (ret) 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci if (indio_dev->info == NULL) 1448c2ecf20Sopenharmony_ci return -ENODEV; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&ev_int->read_lock)) 1488c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1498c2ecf20Sopenharmony_ci ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); 1508c2ecf20Sopenharmony_ci mutex_unlock(&ev_int->read_lock); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * If we couldn't read anything from the fifo (a different 1578c2ecf20Sopenharmony_ci * thread might have been faster) we either return -EAGAIN if 1588c2ecf20Sopenharmony_ci * the file descriptor is non-blocking, otherwise we go back to 1598c2ecf20Sopenharmony_ci * sleep and wait for more data to arrive. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci if (copied == 0 && (filep->f_flags & O_NONBLOCK)) 1628c2ecf20Sopenharmony_ci return -EAGAIN; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci } while (copied == 0); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return copied; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int iio_event_chrdev_release(struct inode *inode, struct file *filep) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = filep->private_data; 1728c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 1738c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci iio_device_put(indio_dev); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic const struct file_operations iio_event_chrdev_fileops = { 1838c2ecf20Sopenharmony_ci .read = iio_event_chrdev_read, 1848c2ecf20Sopenharmony_ci .poll = iio_event_poll, 1858c2ecf20Sopenharmony_ci .release = iio_event_chrdev_release, 1868c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1878c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciint iio_event_getfd(struct iio_dev *indio_dev) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 1938c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; 1948c2ecf20Sopenharmony_ci int fd; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (ev_int == NULL) 1978c2ecf20Sopenharmony_ci return -ENODEV; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci fd = mutex_lock_interruptible(&indio_dev->mlock); 2008c2ecf20Sopenharmony_ci if (fd) 2018c2ecf20Sopenharmony_ci return fd; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { 2048c2ecf20Sopenharmony_ci fd = -EBUSY; 2058c2ecf20Sopenharmony_ci goto unlock; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci iio_device_get(indio_dev); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, 2118c2ecf20Sopenharmony_ci indio_dev, O_RDONLY | O_CLOEXEC); 2128c2ecf20Sopenharmony_ci if (fd < 0) { 2138c2ecf20Sopenharmony_ci clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); 2148c2ecf20Sopenharmony_ci iio_device_put(indio_dev); 2158c2ecf20Sopenharmony_ci } else { 2168c2ecf20Sopenharmony_ci kfifo_reset_out(&ev_int->det_events); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ciunlock: 2208c2ecf20Sopenharmony_ci mutex_unlock(&indio_dev->mlock); 2218c2ecf20Sopenharmony_ci return fd; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic const char * const iio_ev_type_text[] = { 2258c2ecf20Sopenharmony_ci [IIO_EV_TYPE_THRESH] = "thresh", 2268c2ecf20Sopenharmony_ci [IIO_EV_TYPE_MAG] = "mag", 2278c2ecf20Sopenharmony_ci [IIO_EV_TYPE_ROC] = "roc", 2288c2ecf20Sopenharmony_ci [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive", 2298c2ecf20Sopenharmony_ci [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", 2308c2ecf20Sopenharmony_ci [IIO_EV_TYPE_CHANGE] = "change", 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic const char * const iio_ev_dir_text[] = { 2348c2ecf20Sopenharmony_ci [IIO_EV_DIR_EITHER] = "either", 2358c2ecf20Sopenharmony_ci [IIO_EV_DIR_RISING] = "rising", 2368c2ecf20Sopenharmony_ci [IIO_EV_DIR_FALLING] = "falling" 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const char * const iio_ev_info_text[] = { 2408c2ecf20Sopenharmony_ci [IIO_EV_INFO_ENABLE] = "en", 2418c2ecf20Sopenharmony_ci [IIO_EV_INFO_VALUE] = "value", 2428c2ecf20Sopenharmony_ci [IIO_EV_INFO_HYSTERESIS] = "hysteresis", 2438c2ecf20Sopenharmony_ci [IIO_EV_INFO_PERIOD] = "period", 2448c2ecf20Sopenharmony_ci [IIO_EV_INFO_HIGH_PASS_FILTER_3DB] = "high_pass_filter_3db", 2458c2ecf20Sopenharmony_ci [IIO_EV_INFO_LOW_PASS_FILTER_3DB] = "low_pass_filter_3db", 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci return attr->c->event_spec[attr->address & 0xffff].dir; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic enum iio_event_type iio_ev_attr_type(struct iio_dev_attr *attr) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci return attr->c->event_spec[attr->address & 0xffff].type; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic enum iio_event_info iio_ev_attr_info(struct iio_dev_attr *attr) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci return (attr->address >> 16) & 0xffff; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic ssize_t iio_ev_state_store(struct device *dev, 2648c2ecf20Sopenharmony_ci struct device_attribute *attr, 2658c2ecf20Sopenharmony_ci const char *buf, 2668c2ecf20Sopenharmony_ci size_t len) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 2698c2ecf20Sopenharmony_ci struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci bool val; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ret = strtobool(buf, &val); 2748c2ecf20Sopenharmony_ci if (ret < 0) 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci ret = indio_dev->info->write_event_config(indio_dev, 2788c2ecf20Sopenharmony_ci this_attr->c, iio_ev_attr_type(this_attr), 2798c2ecf20Sopenharmony_ci iio_ev_attr_dir(this_attr), val); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return (ret < 0) ? ret : len; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic ssize_t iio_ev_state_show(struct device *dev, 2858c2ecf20Sopenharmony_ci struct device_attribute *attr, 2868c2ecf20Sopenharmony_ci char *buf) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 2898c2ecf20Sopenharmony_ci struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 2908c2ecf20Sopenharmony_ci int val; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci val = indio_dev->info->read_event_config(indio_dev, 2938c2ecf20Sopenharmony_ci this_attr->c, iio_ev_attr_type(this_attr), 2948c2ecf20Sopenharmony_ci iio_ev_attr_dir(this_attr)); 2958c2ecf20Sopenharmony_ci if (val < 0) 2968c2ecf20Sopenharmony_ci return val; 2978c2ecf20Sopenharmony_ci else 2988c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic ssize_t iio_ev_value_show(struct device *dev, 3028c2ecf20Sopenharmony_ci struct device_attribute *attr, 3038c2ecf20Sopenharmony_ci char *buf) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3068c2ecf20Sopenharmony_ci struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 3078c2ecf20Sopenharmony_ci int val, val2, val_arr[2]; 3088c2ecf20Sopenharmony_ci int ret; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret = indio_dev->info->read_event_value(indio_dev, 3118c2ecf20Sopenharmony_ci this_attr->c, iio_ev_attr_type(this_attr), 3128c2ecf20Sopenharmony_ci iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr), 3138c2ecf20Sopenharmony_ci &val, &val2); 3148c2ecf20Sopenharmony_ci if (ret < 0) 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci val_arr[0] = val; 3178c2ecf20Sopenharmony_ci val_arr[1] = val2; 3188c2ecf20Sopenharmony_ci return iio_format_value(buf, ret, 2, val_arr); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic ssize_t iio_ev_value_store(struct device *dev, 3228c2ecf20Sopenharmony_ci struct device_attribute *attr, 3238c2ecf20Sopenharmony_ci const char *buf, 3248c2ecf20Sopenharmony_ci size_t len) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3278c2ecf20Sopenharmony_ci struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 3288c2ecf20Sopenharmony_ci int val, val2; 3298c2ecf20Sopenharmony_ci int ret; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (!indio_dev->info->write_event_value) 3328c2ecf20Sopenharmony_ci return -EINVAL; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = iio_str_to_fixpoint(buf, 100000, &val, &val2); 3358c2ecf20Sopenharmony_ci if (ret) 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci ret = indio_dev->info->write_event_value(indio_dev, 3388c2ecf20Sopenharmony_ci this_attr->c, iio_ev_attr_type(this_attr), 3398c2ecf20Sopenharmony_ci iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr), 3408c2ecf20Sopenharmony_ci val, val2); 3418c2ecf20Sopenharmony_ci if (ret < 0) 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return len; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int iio_device_add_event(struct iio_dev *indio_dev, 3488c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, unsigned int spec_index, 3498c2ecf20Sopenharmony_ci enum iio_event_type type, enum iio_event_direction dir, 3508c2ecf20Sopenharmony_ci enum iio_shared_by shared_by, const unsigned long *mask) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 3538c2ecf20Sopenharmony_ci ssize_t (*show)(struct device *, struct device_attribute *, char *); 3548c2ecf20Sopenharmony_ci ssize_t (*store)(struct device *, struct device_attribute *, 3558c2ecf20Sopenharmony_ci const char *, size_t); 3568c2ecf20Sopenharmony_ci unsigned int attrcount = 0; 3578c2ecf20Sopenharmony_ci unsigned int i; 3588c2ecf20Sopenharmony_ci char *postfix; 3598c2ecf20Sopenharmony_ci int ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for_each_set_bit(i, mask, sizeof(*mask)*8) { 3628c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(iio_ev_info_text)) 3638c2ecf20Sopenharmony_ci return -EINVAL; 3648c2ecf20Sopenharmony_ci if (dir != IIO_EV_DIR_NONE) 3658c2ecf20Sopenharmony_ci postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", 3668c2ecf20Sopenharmony_ci iio_ev_type_text[type], 3678c2ecf20Sopenharmony_ci iio_ev_dir_text[dir], 3688c2ecf20Sopenharmony_ci iio_ev_info_text[i]); 3698c2ecf20Sopenharmony_ci else 3708c2ecf20Sopenharmony_ci postfix = kasprintf(GFP_KERNEL, "%s_%s", 3718c2ecf20Sopenharmony_ci iio_ev_type_text[type], 3728c2ecf20Sopenharmony_ci iio_ev_info_text[i]); 3738c2ecf20Sopenharmony_ci if (postfix == NULL) 3748c2ecf20Sopenharmony_ci return -ENOMEM; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (i == IIO_EV_INFO_ENABLE) { 3778c2ecf20Sopenharmony_ci show = iio_ev_state_show; 3788c2ecf20Sopenharmony_ci store = iio_ev_state_store; 3798c2ecf20Sopenharmony_ci } else { 3808c2ecf20Sopenharmony_ci show = iio_ev_value_show; 3818c2ecf20Sopenharmony_ci store = iio_ev_value_store; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ret = __iio_add_chan_devattr(postfix, chan, show, store, 3858c2ecf20Sopenharmony_ci (i << 16) | spec_index, shared_by, &indio_dev->dev, 3868c2ecf20Sopenharmony_ci &iio_dev_opaque->event_interface->dev_attr_list); 3878c2ecf20Sopenharmony_ci kfree(postfix); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) 3908c2ecf20Sopenharmony_ci continue; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (ret) 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci attrcount++; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return attrcount; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int iio_device_add_event_sysfs(struct iio_dev *indio_dev, 4028c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int ret = 0, i, attrcount = 0; 4058c2ecf20Sopenharmony_ci enum iio_event_direction dir; 4068c2ecf20Sopenharmony_ci enum iio_event_type type; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for (i = 0; i < chan->num_event_specs; i++) { 4098c2ecf20Sopenharmony_ci type = chan->event_spec[i].type; 4108c2ecf20Sopenharmony_ci dir = chan->event_spec[i].dir; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = iio_device_add_event(indio_dev, chan, i, type, dir, 4138c2ecf20Sopenharmony_ci IIO_SEPARATE, &chan->event_spec[i].mask_separate); 4148c2ecf20Sopenharmony_ci if (ret < 0) 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci attrcount += ret; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ret = iio_device_add_event(indio_dev, chan, i, type, dir, 4198c2ecf20Sopenharmony_ci IIO_SHARED_BY_TYPE, 4208c2ecf20Sopenharmony_ci &chan->event_spec[i].mask_shared_by_type); 4218c2ecf20Sopenharmony_ci if (ret < 0) 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci attrcount += ret; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ret = iio_device_add_event(indio_dev, chan, i, type, dir, 4268c2ecf20Sopenharmony_ci IIO_SHARED_BY_DIR, 4278c2ecf20Sopenharmony_ci &chan->event_spec[i].mask_shared_by_dir); 4288c2ecf20Sopenharmony_ci if (ret < 0) 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci attrcount += ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = iio_device_add_event(indio_dev, chan, i, type, dir, 4338c2ecf20Sopenharmony_ci IIO_SHARED_BY_ALL, 4348c2ecf20Sopenharmony_ci &chan->event_spec[i].mask_shared_by_all); 4358c2ecf20Sopenharmony_ci if (ret < 0) 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci attrcount += ret; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci ret = attrcount; 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci int j, ret, attrcount = 0; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Dynamically created from the channels array */ 4488c2ecf20Sopenharmony_ci for (j = 0; j < indio_dev->num_channels; j++) { 4498c2ecf20Sopenharmony_ci ret = iio_device_add_event_sysfs(indio_dev, 4508c2ecf20Sopenharmony_ci &indio_dev->channels[j]); 4518c2ecf20Sopenharmony_ci if (ret < 0) 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci attrcount += ret; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci return attrcount; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic bool iio_check_for_dynamic_events(struct iio_dev *indio_dev) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci int j; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci for (j = 0; j < indio_dev->num_channels; j++) { 4638c2ecf20Sopenharmony_ci if (indio_dev->channels[j].num_event_specs != 0) 4648c2ecf20Sopenharmony_ci return true; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci return false; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void iio_setup_ev_int(struct iio_event_interface *ev_int) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci INIT_KFIFO(ev_int->det_events); 4728c2ecf20Sopenharmony_ci init_waitqueue_head(&ev_int->wait); 4738c2ecf20Sopenharmony_ci mutex_init(&ev_int->read_lock); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic const char *iio_event_group_name = "events"; 4778c2ecf20Sopenharmony_ciint iio_device_register_eventset(struct iio_dev *indio_dev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 4808c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int; 4818c2ecf20Sopenharmony_ci struct iio_dev_attr *p; 4828c2ecf20Sopenharmony_ci int ret = 0, attrcount_orig = 0, attrcount, attrn; 4838c2ecf20Sopenharmony_ci struct attribute **attr; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!(indio_dev->info->event_attrs || 4868c2ecf20Sopenharmony_ci iio_check_for_dynamic_events(indio_dev))) 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci ev_int = kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL); 4908c2ecf20Sopenharmony_ci if (ev_int == NULL) 4918c2ecf20Sopenharmony_ci return -ENOMEM; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci iio_dev_opaque->event_interface = ev_int; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ev_int->dev_attr_list); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci iio_setup_ev_int(ev_int); 4988c2ecf20Sopenharmony_ci if (indio_dev->info->event_attrs != NULL) { 4998c2ecf20Sopenharmony_ci attr = indio_dev->info->event_attrs->attrs; 5008c2ecf20Sopenharmony_ci while (*attr++ != NULL) 5018c2ecf20Sopenharmony_ci attrcount_orig++; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci attrcount = attrcount_orig; 5048c2ecf20Sopenharmony_ci if (indio_dev->channels) { 5058c2ecf20Sopenharmony_ci ret = __iio_add_event_config_attrs(indio_dev); 5068c2ecf20Sopenharmony_ci if (ret < 0) 5078c2ecf20Sopenharmony_ci goto error_free_setup_event_lines; 5088c2ecf20Sopenharmony_ci attrcount += ret; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ev_int->group.name = iio_event_group_name; 5128c2ecf20Sopenharmony_ci ev_int->group.attrs = kcalloc(attrcount + 1, 5138c2ecf20Sopenharmony_ci sizeof(ev_int->group.attrs[0]), 5148c2ecf20Sopenharmony_ci GFP_KERNEL); 5158c2ecf20Sopenharmony_ci if (ev_int->group.attrs == NULL) { 5168c2ecf20Sopenharmony_ci ret = -ENOMEM; 5178c2ecf20Sopenharmony_ci goto error_free_setup_event_lines; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci if (indio_dev->info->event_attrs) 5208c2ecf20Sopenharmony_ci memcpy(ev_int->group.attrs, 5218c2ecf20Sopenharmony_ci indio_dev->info->event_attrs->attrs, 5228c2ecf20Sopenharmony_ci sizeof(ev_int->group.attrs[0]) * attrcount_orig); 5238c2ecf20Sopenharmony_ci attrn = attrcount_orig; 5248c2ecf20Sopenharmony_ci /* Add all elements from the list. */ 5258c2ecf20Sopenharmony_ci list_for_each_entry(p, &ev_int->dev_attr_list, l) 5268c2ecf20Sopenharmony_ci ev_int->group.attrs[attrn++] = &p->dev_attr.attr; 5278c2ecf20Sopenharmony_ci indio_dev->groups[indio_dev->groupcounter++] = &ev_int->group; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cierror_free_setup_event_lines: 5328c2ecf20Sopenharmony_ci iio_free_chan_devattr_list(&ev_int->dev_attr_list); 5338c2ecf20Sopenharmony_ci kfree(ev_int); 5348c2ecf20Sopenharmony_ci iio_dev_opaque->event_interface = NULL; 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci/** 5398c2ecf20Sopenharmony_ci * iio_device_wakeup_eventset - Wakes up the event waitqueue 5408c2ecf20Sopenharmony_ci * @indio_dev: The IIO device 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * Wakes up the event waitqueue used for poll() and blocking read(). 5438c2ecf20Sopenharmony_ci * Should usually be called when the device is unregistered. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_civoid iio_device_wakeup_eventset(struct iio_dev *indio_dev) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (iio_dev_opaque->event_interface == NULL) 5508c2ecf20Sopenharmony_ci return; 5518c2ecf20Sopenharmony_ci wake_up(&iio_dev_opaque->event_interface->wait); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_civoid iio_device_unregister_eventset(struct iio_dev *indio_dev) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); 5578c2ecf20Sopenharmony_ci struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (ev_int == NULL) 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci iio_free_chan_devattr_list(&ev_int->dev_attr_list); 5628c2ecf20Sopenharmony_ci kfree(ev_int->group.attrs); 5638c2ecf20Sopenharmony_ci kfree(ev_int); 5648c2ecf20Sopenharmony_ci iio_dev_opaque->event_interface = NULL; 5658c2ecf20Sopenharmony_ci} 566