diff --git a/drivers/input/misc/tri_state_key.c b/drivers/input/misc/tri_state_key.c index 5f622384e070..47c1f35cf8ee 100644 --- a/drivers/input/misc/tri_state_key.c +++ b/drivers/input/misc/tri_state_key.c @@ -1,344 +1,390 @@ /* - * drivers/input/misc/tri_state_key.c * - * 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. + * 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. */ -#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 +#include +#include -enum key_vals { - KEYCODE_TOTAL_SILENCE = 600, - KEYCODE_MODE_ALARMS_ONLY, - KEYCODE_MODE_PRIORITY_ONLY, - KEYCODE_MODE_NONE, - KEYCODE_MODE_MAX -}; +#include -static const char *const proc_names[] = { - "keyCode_top", "keyCode_middle", "keyCode_bottom" -}; +#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 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; + struct switch_dev sdev; + + struct timer_list s_timer; + + struct pinctrl *key_pinctrl; + struct pinctrl_state *set_state; }; -static struct tristate_data *tdata_g; +static struct switch_dev_data *switch_data; -static void send_input(struct tristate_data *t, int keycode) -{ - input_report_key(t->input, keycode, 1); - input_sync(t->input); - input_report_key(t->input, keycode, 0); - input_sync(t->input); -} +static DEFINE_MUTEX(sem); -static void tristate_process_state(struct tristate_data *t) +static void send_input(int keyCode) { - unsigned long key_state = 0; - int i; - - /* 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; - - /* Spurious interrupt; at least one of the pins should be zero */ - if (key_state == 0x7) - return; - - /* Redundant interrupt */ - if (key_state == t->curr_state) - return; - - t->curr_state = key_state; - - /* - * 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]); + 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); } -static irqreturn_t tristate_irq_handler(int irq, void *dev_id) +static void switch_dev_work(struct work_struct *work) { - struct tristate_data *t = dev_id; + int key1, key2, key3; + int mode = MODE_UNKNOWN; + int keyCode; + + mutex_lock(&sem); + + key1 = gpio_get_value(switch_data->key1_gpio); + key2 = gpio_get_value(switch_data->key2_gpio); + key3 = gpio_get_value(switch_data->key3_gpio); + + 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; + } - /* This handler is shared between three interrupt lines */ - mutex_lock(&t->irq_lock); - tristate_process_state(t); - mutex_unlock(&t->irq_lock); + 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); + } - return IRQ_HANDLED; + mutex_unlock(&sem); } -static int keycode_get_idx(const char *filename) +irqreturn_t switch_dev_interrupt(int irq, void *_dev) { - int i; - - for (i = 0; i < NR_STATES; i++) { - if (!strcmp(proc_names[i], filename)) - break; - } - - return i; + schedule_work(&switch_data->work); + return IRQ_HANDLED; } -static int keycode_show(struct seq_file *seq, void *offset) +static void timer_handle(unsigned long arg) { - struct tristate_data *t = tdata_g; - int idx = (int)(long)seq->private; - - seq_printf(seq, "%d\n", t->key_codes[idx]); - return 0; + schedule_work(&switch_data->work); } -static ssize_t tristate_keycode_write(struct file *file, - const char __user *page, size_t count, loff_t *offset) +#ifdef CONFIG_OF +static int switch_dev_get_devtree_pdata(struct device *dev) { - struct tristate_data *t = tdata_g; - char buf[sizeof("600")]; - int data, idx; + struct device_node *node; - 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; + node = dev->of_node; + if (!node) + return -EINVAL; - if (data < KEYCODE_TOTAL_SILENCE || data >= KEYCODE_MODE_MAX) - return count; + switch_data->key1_gpio = of_get_named_gpio(node, "tristate,gpio_key1", 0); + if (!gpio_is_valid(switch_data->key1_gpio)) + return -EINVAL; - idx = keycode_get_idx(file->f_path.dentry->d_iname); + switch_data->key2_gpio = of_get_named_gpio(node, "tristate,gpio_key2", 0); + if (!gpio_is_valid(switch_data->key2_gpio)) + return -EINVAL; - 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); + switch_data->key3_gpio = of_get_named_gpio(node, "tristate,gpio_key3", 0); + if (!gpio_is_valid(switch_data->key3_gpio)) + return -EINVAL; - return count; + 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_top);\ + } else if (current_mode == 2) {\ + send_input(keyCode_slider_middle);\ + } else if (current_mode == 3) {\ + send_input(keyCode_slider_bottom);\ + }\ + 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_keycode_open(struct inode *inode, struct file *file) +static int tristate_dev_probe(struct platform_device *pdev) { - int idx = keycode_get_idx(file->f_path.dentry->d_iname); + struct device *dev = &pdev->dev; + struct proc_dir_entry *procdir; + int error = 0; - /* Pass the index to keycode_show */ - return single_open(file, keycode_show, (void *)(long)idx); -} + switch_data = kzalloc(sizeof(struct switch_dev_data), GFP_KERNEL); + switch_data->dev = dev; -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, -}; + switch_data->input = input_allocate_device(); -static int tristate_parse_dt(struct tristate_data *t) -{ - struct device_node *np; - char prop_name[sizeof("tristate,gpio_key#")]; - int i; + // init input device - np = t->dev->of_node; - if (!np) - return -EINVAL; + 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); - for (i = 0; i < NR_STATES; i++) { - sprintf(prop_name, "tristate,gpio_key%d", i + 1); + input_set_drvdata(switch_data->input, switch_data); - 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; - } + error = input_register_device(switch_data->input); + if (error) { + dev_err(dev, "Failed to register input device\n"); + goto err_input_device_register; } - return 0; -} + // init pinctrl -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; - } + 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; } - 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; - } + 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; } - for (i = 0; i < NR_STATES; i++) - enable_irq_wake(key_irqs[i]); + pinctrl_select_state(switch_data->key_pinctrl, switch_data->set_state); - return 0; + // parse gpios from dt -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; -} + error = switch_dev_get_devtree_pdata(dev); + if (error) { + dev_err(dev, "parse device tree fail!!!\n"); + goto err_switch_dev_register; + } -static void tristate_create_procfs(void) -{ - struct proc_dir_entry *proc_dir; - int i; + // irqs and work, timer stuffs - proc_dir = proc_mkdir("tri-state-key", NULL); - if (!proc_dir) - return; + REGISTER_IRQ_FOR(key1); + REGISTER_IRQ_FOR(key2); + REGISTER_IRQ_FOR(key3); - for (i = 0; i < NR_STATES; i++) - proc_create_data(proc_names[i], 0644, proc_dir, - &tristate_keycode_proc, NULL); -} + INIT_WORK(&switch_data->work, switch_dev_work); -static int tristate_probe(struct platform_device *pdev) -{ - struct tristate_data *t; - int i, ret; + init_timer(&switch_data->s_timer); + switch_data->s_timer.function = &timer_handle; + switch_data->s_timer.expires = jiffies + 5*HZ; - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return -ENOMEM; + add_timer(&switch_data->s_timer); - t->dev = &pdev->dev; - tdata_g = t; + enable_irq_wake(switch_data->irq_key1); + enable_irq_wake(switch_data->irq_key2); + enable_irq_wake(switch_data->irq_key3); - ret = tristate_parse_dt(t); - if (ret) - goto free_t; + // init switch device - t->input = input_allocate_device(); - if (!t->input) { - dev_err(t->dev, "Failed to allocate input device\n"); - ret = -ENOMEM; - goto free_t; + 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; } - 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); + // init proc fs - ret = input_register_device(t->input); - if (ret) - goto free_input; + procdir = proc_mkdir("tri-state-key", NULL); - /* Initialize with default keycodes */ - for (i = 0; i < NR_STATES; i++) - t->key_codes[i] = KEYCODE_TOTAL_SILENCE + i; + proc_create_data("keyCode_top", 0666, procdir, + &proc_keyCode_top, NULL); - /* Process initial state */ - tristate_process_state(t); + proc_create_data("keyCode_middle", 0666, procdir, + &proc_keyCode_middle, NULL); - mutex_init(&t->irq_lock); - ret = tristate_register_irqs(t); - if (ret) - goto input_unregister; + proc_create_data("keyCode_bottom", 0666, procdir, + &proc_keyCode_bottom, NULL); - 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; +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 const struct of_device_id tristate_of_match[] = { +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); + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id tristate_dev_of_match[] = { { .compatible = "oneplus,tri-state-key", }, { }, }; +MODULE_DEVICE_TABLE(of, tristate_dev_of_match); +#endif -static struct platform_driver tristate_driver = { - .probe = tristate_probe, +static struct platform_driver tristate_dev_driver = { + .probe = tristate_dev_probe, + .remove = tristate_dev_remove, .driver = { - .name = DRIVER_NAME, + .name = DRV_NAME, .owner = THIS_MODULE, - .of_match_table = tristate_of_match, + .of_match_table = of_match_ptr(tristate_dev_of_match), }, }; +module_platform_driver(tristate_dev_driver); -static int __init tristate_init(void) -{ - return platform_driver_register(&tristate_driver); -} -device_initcall(tristate_init); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("switch Profiles by this triple key driver");