// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_COMPAT) #include #endif #include "ccci_util_log.h" #include "ccci_util_lib_main.h" /* PIN broadcast event defination */ struct pin_status_event { int pin_value; char pin_name[32]; }; struct pin_event_user_ctrl { struct pin_status_event pin_event; int pin_update; struct list_head entry; }; static dev_t s_pin_status_dev; static struct cdev s_pin_char_dev; static struct class *s_ccci_pin_class; static struct pin_status_event *pin_event; static wait_queue_head_t pin_event_wait; static spinlock_t pin_event_update_lock; static struct list_head user_list; void inject_pin_status_event(int pin_value, const char pin_name[]) { struct pin_event_user_ctrl *user_ctrl; unsigned long flags; spin_lock_irqsave(&pin_event_update_lock, flags); if (pin_name != NULL) scnprintf(pin_event->pin_name, 32, "%s", pin_name); else scnprintf(pin_event->pin_name, 32, "%s", "----"); pin_event->pin_value = pin_value; list_for_each_entry(user_ctrl, &user_list, entry) user_ctrl->pin_update = 1; wake_up_interruptible(&pin_event_wait); spin_unlock_irqrestore(&pin_event_update_lock, flags); } EXPORT_SYMBOL(inject_pin_status_event); static int ccci_util_pin_bc_open(struct inode *inode, struct file *filp) { struct pin_event_user_ctrl *user_ctrl; unsigned long flags; user_ctrl = kzalloc(sizeof(struct pin_event_user_ctrl), GFP_KERNEL); if (user_ctrl == NULL) return -ENOMEM; INIT_LIST_HEAD(&user_ctrl->entry); filp->private_data = user_ctrl; spin_lock_irqsave(&pin_event_update_lock, flags); list_add_tail(&user_ctrl->entry, &user_list); spin_unlock_irqrestore(&pin_event_update_lock, flags); return 0; } static int ccci_util_pin_bc_release(struct inode *inode, struct file *filp) { struct pin_event_user_ctrl *user_ctrl; unsigned long flags; spin_lock_irqsave(&pin_event_update_lock, flags); user_ctrl = filp->private_data; user_ctrl->pin_update = 0; list_del(&user_ctrl->entry); spin_unlock_irqrestore(&pin_event_update_lock, flags); kfree(user_ctrl); return 0; } static ssize_t ccci_util_pin_bc_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { struct pin_event_user_ctrl *user_ctrl; int ret; unsigned long flags; user_ctrl = filp->private_data; if (filp->f_flags & O_NONBLOCK) { spin_lock_irqsave(&pin_event_update_lock, flags); if (user_ctrl->pin_update == 0) { spin_unlock_irqrestore(&pin_event_update_lock, flags); return 0; } memcpy(&user_ctrl->pin_event, pin_event, sizeof(struct pin_status_event)); spin_unlock_irqrestore(&pin_event_update_lock, flags); if (copy_to_user(buf, &user_ctrl->pin_event, sizeof(struct pin_status_event))) return -EFAULT; } else { ret = wait_event_interruptible(pin_event_wait, user_ctrl->pin_update == 1); if (ret < 0) return -EINTR; spin_lock_irqsave(&pin_event_update_lock, flags); user_ctrl->pin_update = 0; memcpy(&user_ctrl->pin_event, pin_event, sizeof(struct pin_status_event)); spin_unlock_irqrestore(&pin_event_update_lock, flags); if (copy_to_user(buf, &user_ctrl->pin_event, sizeof(struct pin_status_event))) return -EFAULT; } return sizeof(struct pin_status_event); } static unsigned int ccci_util_pin_bc_poll(struct file *filp, struct poll_table_struct *wait) { struct pin_event_user_ctrl *user_ctrl; unsigned int mask = 0; user_ctrl = filp->private_data; poll_wait(filp, &pin_event_wait, wait); if (user_ctrl->pin_update == 1) mask |= POLLIN|POLLRDNORM; return mask; } static const struct file_operations pin_sta_bc_fops = { .owner = THIS_MODULE, .open = ccci_util_pin_bc_open, .read = ccci_util_pin_bc_read, .poll = ccci_util_pin_bc_poll, .release = ccci_util_pin_bc_release, }; int ccci_util_pin_broadcast_init(void) { int ret; pin_event = kzalloc(sizeof(struct pin_status_event), GFP_KERNEL); if (pin_event == NULL) goto _exit; spin_lock_init(&pin_event_update_lock); init_waitqueue_head(&pin_event_wait); INIT_LIST_HEAD(&user_list); ret = alloc_chrdev_region(&s_pin_status_dev, 0, 1, "ccci_pin_sta"); if (ret != 0) { CCCI_UTIL_ERR_MSG("alloc chrdev fail for ccci_pin_sta(%d)\n", ret); goto _exit_1; } cdev_init(&s_pin_char_dev, &pin_sta_bc_fops); s_pin_char_dev.owner = THIS_MODULE; ret = cdev_add(&s_pin_char_dev, s_pin_status_dev, 1); if (ret) { CCCI_UTIL_ERR_MSG("cdev_add failed\n"); goto _exit_2; } s_ccci_pin_class = class_create(THIS_MODULE, "ccci_pin_sta"); device_create(s_ccci_pin_class, NULL, s_pin_status_dev, NULL, "ccci_pin_sta"); return 0; _exit_2: cdev_del(&s_pin_char_dev); _exit_1: unregister_chrdev_region(s_pin_status_dev, 1); _exit: kfree(pin_event); return -1; }