// SPDX-License-Identifier: GPL-2.0 /* * drivers/samsung/debug/sec_key_notifier.c * * COPYRIGHT(C) 2016-2019 Samsung Electronics Co., Ltd. All Right Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__ #include #include #include #include #include #include "sec_key_notifier.h" static DEFINE_SPINLOCK(sec_kn_event_lock); static ATOMIC_NOTIFIER_HEAD(sec_kn_notifier_list); static atomic_t sec_kn_acceptable_event[KEY_MAX] __read_mostly; static void inline update_acceptable_event(unsigned int event_code, bool is_add) { if (is_add) atomic_inc(&(sec_kn_acceptable_event[event_code])); else atomic_dec(&(sec_kn_acceptable_event[event_code])); } int sec_kn_register_notifier(struct notifier_block *nb, const unsigned int *events, const size_t nr_events) { size_t i; for (i = 0; i < nr_events; i++) update_acceptable_event(events[i], true); return atomic_notifier_chain_register(&sec_kn_notifier_list, nb); } int sec_kn_unregister_notifier(struct notifier_block *nb, const unsigned int *events, const size_t nr_events) { size_t i; for (i = 0; i < nr_events; i++) update_acceptable_event(events[i], false); return atomic_notifier_chain_unregister(&sec_kn_notifier_list, nb); } static inline bool is_event_supported(unsigned int event_type, unsigned int event_code) { bool ret; if (event_type != EV_KEY || event_code >= KEY_MAX) return false; ret = !!atomic_read(&(sec_kn_acceptable_event[event_code])); return ret; } static void sec_kn_event(struct input_handle *handle, unsigned int event_type, unsigned int event_code, int value) { struct sec_key_notifier_param param = { .keycode = event_code, .down = value, }; if (!is_event_supported(event_type, event_code)) return; spin_lock(&sec_kn_event_lock); atomic_notifier_call_chain(&sec_kn_notifier_list, 0, ¶m); spin_unlock(&sec_kn_event_lock); } static int sec_kn_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct input_handle *handle; int error; handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); if (!handle) return -ENOMEM; handle->dev = dev; handle->handler = handler; handle->name = "sec_key_notifier"; error = input_register_handle(handle); if (error) goto err_free_handle; error = input_open_device(handle); if (error) goto err_unregister_handle; return 0; err_unregister_handle: input_unregister_handle(handle); err_free_handle: kfree(handle); return error; } static void sec_kn_disconnect(struct input_handle *handle) { input_close_device(handle); input_unregister_handle(handle); kfree(handle); } static const struct input_device_id sec_kn_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT, .evbit = { BIT_MASK(EV_KEY) }, }, {}, }; static struct input_handler sec_kn_handler = { .event = sec_kn_event, .connect = sec_kn_connect, .disconnect = sec_kn_disconnect, .name = "sec_key_notifier", .id_table = sec_kn_ids, }; static int __init sec_kn_init(void) { int err; size_t i; for (i = 0; i < KEY_MAX; i++) atomic_set(&(sec_kn_acceptable_event[i]), 0); spin_lock_init(&sec_kn_event_lock); err = input_register_handler(&sec_kn_handler); return err; } static void __exit sec_kn_exit(void) { input_unregister_handler(&sec_kn_handler); } arch_initcall(sec_kn_init); module_exit(sec_kn_exit);