From 061081b187c6c2fd2cbbf59cb9a5ef437e2d37c8 Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sun, 10 Mar 2019 10:57:09 +0100 Subject: [PATCH] tri_state_key: import driver from msm8996 input: tri-state-key: Fix trivial code style issue in IRQ handler Change-Id: Ie1e9396cf4674586a68dbf606a1d51fbffeaaca4 Signed-off-by: Sultanxda input: tri-state-key: Use ffz() instead of find_first_zero_bit() find_first_zero_bit() is intended for large bitmaps; ffz() is much faster when only a single word needs to be searched through. Change-Id: Ib81b742805489947164af96bb8603f6732d64e8b Suggested-by: Jason A. Donenfeld Signed-off-by: Sultanxda input: tri-state-key: Clean up some code styling Use a macro to store the number of states, reduce the curr_state member in the struct to uint8_t (since it only needs to contain 3 bits), and use find_first_zero_bit() instead of a loop to find the first zero bit. Change-Id: I35f369dc30186ab95033a4d27b550a07e4dc2dd8 Suggested-by: Joel Porquet Signed-off-by: Sultanxda input: tri-state-key: Rewrite and optimize A driver for such a simple device shouldn't be so ugly. Clean it up and optimize it in the process. Summary of changes: -Remove unneeded switch device -Remove unused/unnecessary struct members -Remove module references (this driver will always be built in) -Utilize fixed loops and clever logic to eliminate significant amounts of code duplication -Remove unused pinctrl code -Use a threaded interrupt handler -Process interrupts directly in the threaded interrupt handler (which runs with realtime priority) rather than a worker -Read the initial switch state upon init (so userspace gets the correct switch state upon boot) -Refactor code wherever possible to make it cleaner Note that although the procfs naming scheme is non-standard (i.e. "keyCode_top" instead of "keycode_top"), the old naming scheme is retained in order to maintain compatibility with userspace. Change-Id: Ic2de6ddeb223aa7669d61c186be0b57a15e1488b Signed-off-by: Sultanxda --- drivers/input/misc/tri_state_key.c | 639 ++++++++++++++--------------- 1 file changed, 299 insertions(+), 340 deletions(-) diff --git a/drivers/input/misc/tri_state_key.c b/drivers/input/misc/tri_state_key.c index a501db1c63b5..5f622384e070 100644 --- a/drivers/input/misc/tri_state_key.c +++ b/drivers/input/misc/tri_state_key.c @@ -1,385 +1,344 @@ /* + * drivers/input/misc/tri_state_key.c * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * Copyright (C) 2018, Sultanxda + * + * 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. */ -#include -#include -#include -#include +#include #include -#include +#include +#include +#include #include #include -#include -#include -#include -#include +#include +#include -#include -#include -#include -#include +#define DRIVER_NAME "tri-state-key" +#define NR_STATES (3) -#include - -#include - -#define DRV_NAME "tri-state-key" - -/* - * Original tri-state modes designed by OnePlus - */ - -typedef enum { - MODE_UNKNOWN, - MODE_MUTE, - MODE_DO_NOT_DISTURB, - MODE_NORMAL, -} tri_mode_t; - -/* - * Target key codes sending to OPPO's Keyhandler - * see KeyHandler.java in device/oppo/common/keyhandler - */ - -#define KEY_MODE_TOTAL_SILENCE 600 -#define KEY_MODE_ALARMS_ONLY 601 -#define KEY_MODE_PRIORITY_ONLY 602 -#define KEY_MODE_NONE 603 - -static int current_mode = MODE_UNKNOWN; - -/* - * Default mapping between OP's sti-state switch and OPPO's key codes - * see Constants.java in device/oppo/common/configpanel - */ - -static int keyCode_slider_top = KEY_MODE_ALARMS_ONLY; -static int keyCode_slider_middle = KEY_MODE_PRIORITY_ONLY; -static int keyCode_slider_bottom = KEY_MODE_NONE; - -struct switch_dev_data { - int irq_key1; - int irq_key2; - int irq_key3; - - int key1_gpio; // Mute - int key2_gpio; // Do Not Disturb - int key3_gpio; // Normal - - struct work_struct work; - - struct device *dev; - struct input_dev *input; - struct switch_dev sdev; - - struct timer_list s_timer; - - struct pinctrl *key_pinctrl; - struct pinctrl_state *set_state; +enum key_vals { + KEYCODE_TOTAL_SILENCE = 600, + KEYCODE_MODE_ALARMS_ONLY, + KEYCODE_MODE_PRIORITY_ONLY, + KEYCODE_MODE_NONE, + KEYCODE_MODE_MAX }; -static struct switch_dev_data *switch_data; +static const char *const proc_names[] = { + "keyCode_top", "keyCode_middle", "keyCode_bottom" +}; -static DEFINE_MUTEX(sem); +struct tristate_data { + struct device *dev; + struct input_dev *input; + struct mutex irq_lock; + enum key_vals key_codes[NR_STATES]; + int key_gpios[NR_STATES]; + uint8_t curr_state; +}; -static void send_input(int keyCode) +static struct tristate_data *tdata_g; + +static void send_input(struct tristate_data *t, int keycode) { - input_report_key(switch_data->input, keyCode, 1); - input_sync(switch_data->input); - input_report_key(switch_data->input, keyCode, 0); - input_sync(switch_data->input); + input_report_key(t->input, keycode, 1); + input_sync(t->input); + input_report_key(t->input, keycode, 0); + input_sync(t->input); } -static void switch_dev_work(struct work_struct *work) +static void tristate_process_state(struct tristate_data *t) { - int key1, key2, key3; - int mode = MODE_UNKNOWN; - int keyCode; + unsigned long key_state = 0; + int i; - mutex_lock(&sem); + /* Store the GPIO values of the keys as a bitmap */ + for (i = 0; i < NR_STATES; i++) + key_state |= !!gpio_get_value(t->key_gpios[i]) << i; - key1 = gpio_get_value(switch_data->key1_gpio); - key2 = gpio_get_value(switch_data->key2_gpio); - key3 = gpio_get_value(switch_data->key3_gpio); + /* Spurious interrupt; at least one of the pins should be zero */ + if (key_state == 0x7) + return; - if (key1 == 0) { - mode = MODE_MUTE; - keyCode = keyCode_slider_top; - } else if (key2 == 0) { - mode = MODE_DO_NOT_DISTURB; - keyCode = keyCode_slider_middle; - } else if (key3 == 0) { - mode = MODE_NORMAL; - keyCode = keyCode_slider_bottom; - } + /* Redundant interrupt */ + if (key_state == t->curr_state) + return; - if (current_mode != mode && mode != MODE_UNKNOWN) { - current_mode = mode; - switch_set_state(&switch_data->sdev, current_mode); - send_input(keyCode); - printk(DRV_NAME " changed to mode: %d\n", switch_data->sdev.state); - } + t->curr_state = key_state; - mutex_unlock(&sem); + /* + * Find the first bit set to zero; this is the current position. Bit 0 + * corresponds to the top position, bit 1 corresponds to the middle + * position, and bit 2 corresponds to the bottom position. + */ + i = ffz(key_state); + send_input(t, t->key_codes[i]); } -irqreturn_t switch_dev_interrupt(int irq, void *_dev) +static irqreturn_t tristate_irq_handler(int irq, void *dev_id) { - schedule_work(&switch_data->work); + struct tristate_data *t = dev_id; + + /* This handler is shared between three interrupt lines */ + mutex_lock(&t->irq_lock); + tristate_process_state(t); + mutex_unlock(&t->irq_lock); + return IRQ_HANDLED; } -static void timer_handle(unsigned long arg) +static int keycode_get_idx(const char *filename) { - schedule_work(&switch_data->work); + int i; + + for (i = 0; i < NR_STATES; i++) { + if (!strcmp(proc_names[i], filename)) + break; + } + + return i; } -#ifdef CONFIG_OF -static int switch_dev_get_devtree_pdata(struct device *dev) +static int keycode_show(struct seq_file *seq, void *offset) { - struct device_node *node; + struct tristate_data *t = tdata_g; + int idx = (int)(long)seq->private; - node = dev->of_node; - if (!node) - return -EINVAL; - - switch_data->key1_gpio = of_get_named_gpio(node, "tristate,gpio_key1", 0); - if (!gpio_is_valid(switch_data->key1_gpio)) - return -EINVAL; - - switch_data->key2_gpio = of_get_named_gpio(node, "tristate,gpio_key2", 0); - if (!gpio_is_valid(switch_data->key2_gpio)) - return -EINVAL; - - switch_data->key3_gpio = of_get_named_gpio(node, "tristate,gpio_key3", 0); - if (!gpio_is_valid(switch_data->key3_gpio)) - return -EINVAL; - - return 0; -} -#else -static inline int -switch_dev_get_devtree_pdata(struct device *dev) -{ - return 0; -} -#endif - -#define KEYCODE_FOPS(WHICH)\ - static int keyCode_##WHICH##_show(struct seq_file *seq, void *offset)\ - {\ - seq_printf(seq, "%d\n", keyCode_slider_##WHICH);\ - return 0;\ - }\ - static ssize_t keyCode_##WHICH##_write(struct file *file,\ - const char __user *page, size_t t, loff_t *lo)\ - {\ - int data;\ - char buf[10];\ - if (copy_from_user(buf, page, t)) {\ - dev_err(switch_data->dev, "read proc input error.\n");\ - return t;\ - }\ - if (sscanf(buf, "%d", &data) != 1)\ - return t;\ - if (data < 600 || data > 603)\ - return t;\ - keyCode_slider_##WHICH = data;\ - if (current_mode == 1)\ - send_input(keyCode_slider_##WHICH);\ - return t;\ - }\ - static int keyCode_##WHICH##_open(struct inode *inode, struct file *file)\ - {\ - return single_open(file, keyCode_##WHICH##_show, inode->i_private);\ - }\ - const struct file_operations proc_keyCode_##WHICH = {\ - .owner = THIS_MODULE,\ - .open = keyCode_##WHICH##_open,\ - .read = seq_read,\ - .write = keyCode_##WHICH##_write,\ - .llseek = seq_lseek,\ - .release = single_release,\ - }; - -KEYCODE_FOPS(top); -KEYCODE_FOPS(middle); -KEYCODE_FOPS(bottom); - -#define REGISTER_IRQ_FOR(KEY)\ - switch_data->irq_##KEY = gpio_to_irq(switch_data->KEY##_gpio);\ - if (switch_data->irq_##KEY <= 0) {\ - dev_err(dev, "%s: irq number is not specified, irq #= %d, int pin=%d\n",\ - __func__, switch_data->irq_##KEY, switch_data->KEY##_gpio);\ - goto err_detect_irq_num_failed;\ - } else {\ - error = gpio_request(switch_data->KEY##_gpio, "tristate_" #KEY "-int");\ - if (error < 0) {\ - dev_err(dev, "%s: gpio_request, err=%d\n", __func__, error);\ - goto err_request_gpio;\ - }\ - error = gpio_direction_input(switch_data->KEY##_gpio);\ - if (error < 0) {\ - dev_err(dev, "%s: gpio_direction_input, err=%d\n", __func__, error);\ - goto err_set_gpio_input;\ - }\ - error = request_irq(switch_data->irq_##KEY, switch_dev_interrupt,\ - IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "tristate_" #KEY, switch_data);\ - if (error) {\ - dev_err(dev, "%s: request_irq, err=%d\n", __func__, error);\ - switch_data->irq_##KEY = -EINVAL;\ - goto err_request_irq;\ - }\ - } - -static int tristate_dev_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct proc_dir_entry *procdir; - int error = 0; - - switch_data = kzalloc(sizeof(struct switch_dev_data), GFP_KERNEL); - switch_data->dev = dev; - - switch_data->input = input_allocate_device(); - - // init input device - - switch_data->input->name = DRV_NAME; - switch_data->input->dev.parent = &pdev->dev; - - set_bit(EV_KEY, switch_data->input->evbit); - - set_bit(KEY_MODE_TOTAL_SILENCE, switch_data->input->keybit); - set_bit(KEY_MODE_ALARMS_ONLY, switch_data->input->keybit); - set_bit(KEY_MODE_PRIORITY_ONLY, switch_data->input->keybit); - set_bit(KEY_MODE_NONE, switch_data->input->keybit); - - input_set_drvdata(switch_data->input, switch_data); - - error = input_register_device(switch_data->input); - if (error) { - dev_err(dev, "Failed to register input device\n"); - goto err_input_device_register; - } - - // init pinctrl - - switch_data->key_pinctrl = devm_pinctrl_get(switch_data->dev); - if (IS_ERR_OR_NULL(switch_data->key_pinctrl)) { - dev_err(dev, "Failed to get pinctrl\n"); - goto err_switch_dev_register; - } - - switch_data->set_state = pinctrl_lookup_state(switch_data->key_pinctrl, - "pmx_tri_state_key_active"); - if (IS_ERR_OR_NULL(switch_data->set_state)) { - dev_err(dev, "Failed to lookup_state\n"); - goto err_switch_dev_register; - } - - pinctrl_select_state(switch_data->key_pinctrl, switch_data->set_state); - - // parse gpios from dt - - error = switch_dev_get_devtree_pdata(dev); - if (error) { - dev_err(dev, "parse device tree fail!!!\n"); - goto err_switch_dev_register; - } - - // irqs and work, timer stuffs - - REGISTER_IRQ_FOR(key1); - REGISTER_IRQ_FOR(key2); - REGISTER_IRQ_FOR(key3); - - INIT_WORK(&switch_data->work, switch_dev_work); - - init_timer(&switch_data->s_timer); - switch_data->s_timer.function = &timer_handle; - switch_data->s_timer.expires = jiffies + 5*HZ; - - add_timer(&switch_data->s_timer); - - enable_irq_wake(switch_data->irq_key1); - enable_irq_wake(switch_data->irq_key2); - enable_irq_wake(switch_data->irq_key3); - - // init switch device - - switch_data->sdev.name = DRV_NAME; - error = switch_dev_register(&switch_data->sdev); - if (error < 0) { - dev_err(dev, "Failed to register switch dev\n"); - goto err_request_gpio; - } - - // init proc fs - - procdir = proc_mkdir("tri-state-key", NULL); - - proc_create_data("keyCode_top", 0666, procdir, - &proc_keyCode_top, NULL); - - proc_create_data("keyCode_middle", 0666, procdir, - &proc_keyCode_middle, NULL); - - proc_create_data("keyCode_bottom", 0666, procdir, - &proc_keyCode_bottom, NULL); - - return 0; - -err_request_gpio: - switch_dev_unregister(&switch_data->sdev); -err_request_irq: -err_detect_irq_num_failed: -err_set_gpio_input: - gpio_free(switch_data->key1_gpio); - gpio_free(switch_data->key2_gpio); - gpio_free(switch_data->key3_gpio); -err_switch_dev_register: - kfree(switch_data); -err_input_device_register: - input_unregister_device(switch_data->input); - input_free_device(switch_data->input); - dev_err(dev, "%s error: %d\n", __func__, error); - return error; -} - -static int tristate_dev_remove(struct platform_device *pdev) -{ - cancel_work_sync(&switch_data->work); - gpio_free(switch_data->key1_gpio); - gpio_free(switch_data->key2_gpio); - gpio_free(switch_data->key3_gpio); - switch_dev_unregister(&switch_data->sdev); - kfree(switch_data); + seq_printf(seq, "%d\n", t->key_codes[idx]); return 0; } -#ifdef CONFIG_OF -static struct of_device_id tristate_dev_of_match[] = { +static ssize_t tristate_keycode_write(struct file *file, + const char __user *page, size_t count, loff_t *offset) +{ + struct tristate_data *t = tdata_g; + char buf[sizeof("600")]; + int data, idx; + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, page, min_t(int, count, 3))) { + dev_err(t->dev, "Failed to read procfs input\n"); + return count; + } + + if (kstrtoint(buf, 10, &data) < 0) + return count; + + if (data < KEYCODE_TOTAL_SILENCE || data >= KEYCODE_MODE_MAX) + return count; + + idx = keycode_get_idx(file->f_path.dentry->d_iname); + + mutex_lock(&t->irq_lock); + t->key_codes[idx] = data; + if (!(t->curr_state & BIT(idx))) + send_input(t, data); + mutex_unlock(&t->irq_lock); + + return count; +} + +static int tristate_keycode_open(struct inode *inode, struct file *file) +{ + int idx = keycode_get_idx(file->f_path.dentry->d_iname); + + /* Pass the index to keycode_show */ + return single_open(file, keycode_show, (void *)(long)idx); +} + +static const struct file_operations tristate_keycode_proc = { + .owner = THIS_MODULE, + .open = tristate_keycode_open, + .read = seq_read, + .write = tristate_keycode_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int tristate_parse_dt(struct tristate_data *t) +{ + struct device_node *np; + char prop_name[sizeof("tristate,gpio_key#")]; + int i; + + np = t->dev->of_node; + if (!np) + return -EINVAL; + + for (i = 0; i < NR_STATES; i++) { + sprintf(prop_name, "tristate,gpio_key%d", i + 1); + + t->key_gpios[i] = of_get_named_gpio(np, prop_name, 0); + if (!gpio_is_valid(t->key_gpios[i])) { + dev_err(t->dev, "Invalid %s property\n", prop_name); + return -EINVAL; + } + } + + return 0; +} + +static int tristate_register_irqs(struct tristate_data *t) +{ + char label[sizeof("tristate_key#-int")]; + int i, j, key_irqs[NR_STATES], ret; + + /* Get the IRQ numbers corresponding to the GPIOs */ + for (i = 0; i < NR_STATES; i++) { + key_irqs[i] = gpio_to_irq(t->key_gpios[i]); + if (key_irqs[i] < 0) { + dev_err(t->dev, "Invalid IRQ (%d) for GPIO%d\n", + key_irqs[i], i + 1); + return -EINVAL; + } + } + + for (i = 0; i < NR_STATES; i++) { + sprintf(label, "tristate_key%d-int", i + 1); + + ret = gpio_request(t->key_gpios[i], label); + if (ret < 0) { + dev_err(t->dev, "Failed to request GPIO%d, ret: %d\n", + i + 1, ret); + goto free_gpios; + } + + ret = gpio_direction_input(t->key_gpios[i]); + if (ret < 0) { + dev_err(t->dev, "Failed to set GPIO%d dir, ret: %d\n", + i + 1, ret); + goto free_gpios; + } + + sprintf(label, "tristate_key%d", i + 1); + + ret = request_threaded_irq(key_irqs[i], NULL, + tristate_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, label, t); + if (ret < 0) { + dev_err(t->dev, "Failed to register %s IRQ, ret: %d\n", + label, ret); + goto free_irqs; + } + } + + for (i = 0; i < NR_STATES; i++) + enable_irq_wake(key_irqs[i]); + + return 0; + +free_irqs: + for (j = i; j--;) + free_irq(key_irqs[j], t); + i = NR_STATES; +free_gpios: + for (j = i; j--;) + gpio_free(t->key_gpios[j]); + return -EINVAL; +} + +static void tristate_create_procfs(void) +{ + struct proc_dir_entry *proc_dir; + int i; + + proc_dir = proc_mkdir("tri-state-key", NULL); + if (!proc_dir) + return; + + for (i = 0; i < NR_STATES; i++) + proc_create_data(proc_names[i], 0644, proc_dir, + &tristate_keycode_proc, NULL); +} + +static int tristate_probe(struct platform_device *pdev) +{ + struct tristate_data *t; + int i, ret; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + + t->dev = &pdev->dev; + tdata_g = t; + + ret = tristate_parse_dt(t); + if (ret) + goto free_t; + + t->input = input_allocate_device(); + if (!t->input) { + dev_err(t->dev, "Failed to allocate input device\n"); + ret = -ENOMEM; + goto free_t; + } + + t->input->name = DRIVER_NAME; + t->input->dev.parent = t->dev; + input_set_drvdata(t->input, t); + + set_bit(EV_KEY, t->input->evbit); + set_bit(KEYCODE_TOTAL_SILENCE, t->input->keybit); + set_bit(KEYCODE_MODE_ALARMS_ONLY, t->input->keybit); + set_bit(KEYCODE_MODE_PRIORITY_ONLY, t->input->keybit); + set_bit(KEYCODE_MODE_NONE, t->input->keybit); + + ret = input_register_device(t->input); + if (ret) + goto free_input; + + /* Initialize with default keycodes */ + for (i = 0; i < NR_STATES; i++) + t->key_codes[i] = KEYCODE_TOTAL_SILENCE + i; + + /* Process initial state */ + tristate_process_state(t); + + mutex_init(&t->irq_lock); + ret = tristate_register_irqs(t); + if (ret) + goto input_unregister; + + tristate_create_procfs(); + return 0; + +input_unregister: + input_unregister_device(t->input); +free_input: + input_free_device(t->input); +free_t: + kfree(t); + return ret; +} + +static const struct of_device_id tristate_of_match[] = { { .compatible = "oneplus,tri-state-key", }, { }, }; -MODULE_DEVICE_TABLE(of, tristate_dev_of_match); -#endif -static struct platform_driver tristate_dev_driver = { - .probe = tristate_dev_probe, - .remove = tristate_dev_remove, +static struct platform_driver tristate_driver = { + .probe = tristate_probe, .driver = { - .name = DRV_NAME, + .name = DRIVER_NAME, .owner = THIS_MODULE, - .of_match_table = of_match_ptr(tristate_dev_of_match), + .of_match_table = tristate_of_match, }, }; -module_platform_driver(tristate_dev_driver); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("switch Profiles by this triple key driver"); +static int __init tristate_init(void) +{ + return platform_driver_register(&tristate_driver); +} +device_initcall(tristate_init);