Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 1 | /* drivers/input/keyreset.c |
| 2 | * |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 3 | * Copyright (C) 2014 Google, Inc. |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 4 | * |
| 5 | * This software is licensed under the terms of the GNU General Public |
| 6 | * License version 2, as published by the Free Software Foundation, and |
| 7 | * may be copied, distributed, and modified under those terms. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | */ |
| 15 | |
| 16 | #include <linux/input.h> |
| 17 | #include <linux/keyreset.h> |
| 18 | #include <linux/module.h> |
| 19 | #include <linux/platform_device.h> |
| 20 | #include <linux/reboot.h> |
| 21 | #include <linux/sched.h> |
| 22 | #include <linux/slab.h> |
| 23 | #include <linux/syscalls.h> |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 24 | #include <linux/keycombo.h> |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 25 | |
| 26 | struct keyreset_state { |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 27 | int restart_requested; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 28 | int (*reset_fn)(void); |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 29 | struct platform_device *pdev_child; |
Daniel Rosenberg | 12daace | 2014-06-27 16:39:35 -0700 | [diff] [blame^] | 30 | struct work_struct restart_work; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 31 | }; |
| 32 | |
Daniel Rosenberg | 12daace | 2014-06-27 16:39:35 -0700 | [diff] [blame^] | 33 | static void do_restart(struct work_struct *unused) |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 34 | { |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 35 | sys_sync(); |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 36 | kernel_restart(NULL); |
| 37 | } |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 38 | |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 39 | static void do_reset_fn(void *priv) |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 40 | { |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 41 | struct keyreset_state *state = priv; |
| 42 | if (state->restart_requested) |
| 43 | panic("keyboard reset failed, %d", state->restart_requested); |
| 44 | if (state->reset_fn) { |
| 45 | state->restart_requested = state->reset_fn(); |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 46 | } else { |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 47 | pr_info("keyboard reset\n"); |
Daniel Rosenberg | 12daace | 2014-06-27 16:39:35 -0700 | [diff] [blame^] | 48 | schedule_work(&state->restart_work); |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 49 | state->restart_requested = 1; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 50 | } |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 51 | } |
| 52 | |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 53 | static int keyreset_probe(struct platform_device *pdev) |
| 54 | { |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 55 | int ret = -ENOMEM; |
| 56 | struct keycombo_platform_data *pdata_child; |
| 57 | struct keyreset_platform_data *pdata = pdev->dev.platform_data; |
| 58 | int up_size = 0, down_size = 0, size; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 59 | int key, *keyp; |
| 60 | struct keyreset_state *state; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 61 | |
| 62 | if (!pdata) |
| 63 | return -EINVAL; |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 64 | state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 65 | if (!state) |
| 66 | return -ENOMEM; |
| 67 | |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 68 | state->pdev_child = platform_device_alloc(KEYCOMBO_NAME, |
| 69 | PLATFORM_DEVID_AUTO); |
| 70 | if (!state->pdev_child) |
| 71 | return -ENOMEM; |
| 72 | state->pdev_child->dev.parent = &pdev->dev; |
Daniel Rosenberg | 12daace | 2014-06-27 16:39:35 -0700 | [diff] [blame^] | 73 | INIT_WORK(&state->restart_work, do_restart); |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 74 | |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 75 | keyp = pdata->keys_down; |
| 76 | while ((key = *keyp++)) { |
| 77 | if (key >= KEY_MAX) |
| 78 | continue; |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 79 | down_size++; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 80 | } |
| 81 | if (pdata->keys_up) { |
| 82 | keyp = pdata->keys_up; |
| 83 | while ((key = *keyp++)) { |
| 84 | if (key >= KEY_MAX) |
| 85 | continue; |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 86 | up_size++; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 87 | } |
| 88 | } |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 89 | size = sizeof(struct keycombo_platform_data) |
| 90 | + sizeof(int) * (down_size + 1); |
| 91 | pdata_child = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
| 92 | if (!pdata_child) |
| 93 | goto error; |
| 94 | memcpy(pdata_child->keys_down, pdata->keys_down, |
| 95 | sizeof(int) * down_size); |
| 96 | if (up_size > 0) { |
| 97 | pdata_child->keys_up = devm_kzalloc(&pdev->dev, up_size + 1, |
| 98 | GFP_KERNEL); |
| 99 | if (!pdata_child->keys_up) |
| 100 | goto error; |
| 101 | memcpy(pdata_child->keys_up, pdata->keys_up, |
| 102 | sizeof(int) * up_size); |
| 103 | if (!pdata_child->keys_up) |
| 104 | goto error; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 105 | } |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 106 | state->reset_fn = pdata->reset_fn; |
| 107 | pdata_child->key_down_fn = do_reset_fn; |
| 108 | pdata_child->priv = state; |
| 109 | pdata_child->key_down_delay = pdata->key_down_delay; |
| 110 | ret = platform_device_add_data(state->pdev_child, pdata_child, size); |
| 111 | if (ret) |
| 112 | goto error; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 113 | platform_set_drvdata(pdev, state); |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 114 | return platform_device_add(state->pdev_child); |
| 115 | error: |
| 116 | platform_device_put(state->pdev_child); |
| 117 | return ret; |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | int keyreset_remove(struct platform_device *pdev) |
| 121 | { |
| 122 | struct keyreset_state *state = platform_get_drvdata(pdev); |
Daniel Rosenberg | 215234c | 2014-05-07 14:17:47 -0700 | [diff] [blame] | 123 | platform_device_put(state->pdev_child); |
Arve Hjønnevåg | 6ca5fa0 | 2008-11-21 21:47:23 -0800 | [diff] [blame] | 124 | return 0; |
| 125 | } |
| 126 | |
| 127 | |
| 128 | struct platform_driver keyreset_driver = { |
| 129 | .driver.name = KEYRESET_NAME, |
| 130 | .probe = keyreset_probe, |
| 131 | .remove = keyreset_remove, |
| 132 | }; |
| 133 | |
| 134 | static int __init keyreset_init(void) |
| 135 | { |
| 136 | return platform_driver_register(&keyreset_driver); |
| 137 | } |
| 138 | |
| 139 | static void __exit keyreset_exit(void) |
| 140 | { |
| 141 | return platform_driver_unregister(&keyreset_driver); |
| 142 | } |
| 143 | |
| 144 | module_init(keyreset_init); |
| 145 | module_exit(keyreset_exit); |