1 /* drivers/input/keyreset.c
2 *
3 * Copyright (C) 2014 Google, Inc.
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>
24 #include <linux/keycombo.h>
25
26 struct keyreset_state {
27 int restart_requested;
28 int (*reset_fn)(void);
29 struct platform_device *pdev_child;
30 struct work_struct restart_work;
31 };
32
do_restart(struct work_struct * unused)33 static void do_restart(struct work_struct *unused)
34 {
35 orderly_reboot();
36 }
37
do_reset_fn(void * priv)38 static void do_reset_fn(void *priv)
39 {
40 struct keyreset_state *state = priv;
41 if (state->restart_requested)
42 panic("keyboard reset failed, %d", state->restart_requested);
43 if (state->reset_fn) {
44 state->restart_requested = state->reset_fn();
45 } else {
46 pr_info("keyboard reset\n");
47 schedule_work(&state->restart_work);
48 state->restart_requested = 1;
49 }
50 }
51
keyreset_probe(struct platform_device * pdev)52 static int keyreset_probe(struct platform_device *pdev)
53 {
54 int ret = -ENOMEM;
55 struct keycombo_platform_data *pdata_child;
56 struct keyreset_platform_data *pdata = pdev->dev.platform_data;
57 int up_size = 0, down_size = 0, size;
58 int key, *keyp;
59 struct keyreset_state *state;
60
61 if (!pdata)
62 return -EINVAL;
63 state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
64 if (!state)
65 return -ENOMEM;
66
67 state->pdev_child = platform_device_alloc(KEYCOMBO_NAME,
68 PLATFORM_DEVID_AUTO);
69 if (!state->pdev_child)
70 return -ENOMEM;
71 state->pdev_child->dev.parent = &pdev->dev;
72 INIT_WORK(&state->restart_work, do_restart);
73
74 keyp = pdata->keys_down;
75 while ((key = *keyp++)) {
76 if (key >= KEY_MAX)
77 continue;
78 down_size++;
79 }
80 if (pdata->keys_up) {
81 keyp = pdata->keys_up;
82 while ((key = *keyp++)) {
83 if (key >= KEY_MAX)
84 continue;
85 up_size++;
86 }
87 }
88 size = sizeof(struct keycombo_platform_data)
89 + sizeof(int) * (down_size + 1);
90 pdata_child = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
91 if (!pdata_child)
92 goto error;
93 memcpy(pdata_child->keys_down, pdata->keys_down,
94 sizeof(int) * down_size);
95 if (up_size > 0) {
96 pdata_child->keys_up = devm_kzalloc(&pdev->dev, up_size + 1,
97 GFP_KERNEL);
98 if (!pdata_child->keys_up)
99 goto error;
100 memcpy(pdata_child->keys_up, pdata->keys_up,
101 sizeof(int) * up_size);
102 if (!pdata_child->keys_up)
103 goto error;
104 }
105 state->reset_fn = pdata->reset_fn;
106 pdata_child->key_down_fn = do_reset_fn;
107 pdata_child->priv = state;
108 pdata_child->key_down_delay = pdata->key_down_delay;
109 ret = platform_device_add_data(state->pdev_child, pdata_child, size);
110 if (ret)
111 goto error;
112 platform_set_drvdata(pdev, state);
113 return platform_device_add(state->pdev_child);
114 error:
115 platform_device_put(state->pdev_child);
116 return ret;
117 }
118
keyreset_remove(struct platform_device * pdev)119 int keyreset_remove(struct platform_device *pdev)
120 {
121 struct keyreset_state *state = platform_get_drvdata(pdev);
122 platform_device_put(state->pdev_child);
123 return 0;
124 }
125
126
127 struct platform_driver keyreset_driver = {
128 .driver.name = KEYRESET_NAME,
129 .probe = keyreset_probe,
130 .remove = keyreset_remove,
131 };
132
keyreset_init(void)133 static int __init keyreset_init(void)
134 {
135 return platform_driver_register(&keyreset_driver);
136 }
137
keyreset_exit(void)138 static void __exit keyreset_exit(void)
139 {
140 return platform_driver_unregister(&keyreset_driver);
141 }
142
143 module_init(keyreset_init);
144 module_exit(keyreset_exit);
145