blob: 7fbf7247e65f5879faa006d19340d8711dc34883 [file] [log] [blame]
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -08001/* drivers/input/keyreset.c
2 *
Daniel Rosenberg215234c2014-05-07 14:17:47 -07003 * Copyright (C) 2014 Google, Inc.
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -08004 *
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 Rosenberg215234c2014-05-07 14:17:47 -070024#include <linux/keycombo.h>
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080025
26struct keyreset_state {
Daniel Rosenberg215234c2014-05-07 14:17:47 -070027 int restart_requested;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080028 int (*reset_fn)(void);
Daniel Rosenberg215234c2014-05-07 14:17:47 -070029 struct platform_device *pdev_child;
Daniel Rosenberg12daace2014-06-27 16:39:35 -070030 struct work_struct restart_work;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080031};
32
Daniel Rosenberg12daace2014-06-27 16:39:35 -070033static void do_restart(struct work_struct *unused)
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080034{
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080035 sys_sync();
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080036 kernel_restart(NULL);
37}
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080038
Daniel Rosenberg215234c2014-05-07 14:17:47 -070039static void do_reset_fn(void *priv)
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080040{
Daniel Rosenberg215234c2014-05-07 14:17:47 -070041 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åg6ca5fa02008-11-21 21:47:23 -080046 } else {
Daniel Rosenberg215234c2014-05-07 14:17:47 -070047 pr_info("keyboard reset\n");
Daniel Rosenberg12daace2014-06-27 16:39:35 -070048 schedule_work(&state->restart_work);
Daniel Rosenberg215234c2014-05-07 14:17:47 -070049 state->restart_requested = 1;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080050 }
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080051}
52
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080053static int keyreset_probe(struct platform_device *pdev)
54{
Daniel Rosenberg215234c2014-05-07 14:17:47 -070055 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åg6ca5fa02008-11-21 21:47:23 -080059 int key, *keyp;
60 struct keyreset_state *state;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080061
62 if (!pdata)
63 return -EINVAL;
Daniel Rosenberg215234c2014-05-07 14:17:47 -070064 state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080065 if (!state)
66 return -ENOMEM;
67
Daniel Rosenberg215234c2014-05-07 14:17:47 -070068 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 Rosenberg12daace2014-06-27 16:39:35 -070073 INIT_WORK(&state->restart_work, do_restart);
Daniel Rosenberg215234c2014-05-07 14:17:47 -070074
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080075 keyp = pdata->keys_down;
76 while ((key = *keyp++)) {
77 if (key >= KEY_MAX)
78 continue;
Daniel Rosenberg215234c2014-05-07 14:17:47 -070079 down_size++;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080080 }
81 if (pdata->keys_up) {
82 keyp = pdata->keys_up;
83 while ((key = *keyp++)) {
84 if (key >= KEY_MAX)
85 continue;
Daniel Rosenberg215234c2014-05-07 14:17:47 -070086 up_size++;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -080087 }
88 }
Daniel Rosenberg215234c2014-05-07 14:17:47 -070089 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åg6ca5fa02008-11-21 21:47:23 -0800105 }
Daniel Rosenberg215234c2014-05-07 14:17:47 -0700106 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åg6ca5fa02008-11-21 21:47:23 -0800113 platform_set_drvdata(pdev, state);
Daniel Rosenberg215234c2014-05-07 14:17:47 -0700114 return platform_device_add(state->pdev_child);
115error:
116 platform_device_put(state->pdev_child);
117 return ret;
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -0800118}
119
120int keyreset_remove(struct platform_device *pdev)
121{
122 struct keyreset_state *state = platform_get_drvdata(pdev);
Daniel Rosenberg215234c2014-05-07 14:17:47 -0700123 platform_device_put(state->pdev_child);
Arve Hjønnevåg6ca5fa02008-11-21 21:47:23 -0800124 return 0;
125}
126
127
128struct platform_driver keyreset_driver = {
129 .driver.name = KEYRESET_NAME,
130 .probe = keyreset_probe,
131 .remove = keyreset_remove,
132};
133
134static int __init keyreset_init(void)
135{
136 return platform_driver_register(&keyreset_driver);
137}
138
139static void __exit keyreset_exit(void)
140{
141 return platform_driver_unregister(&keyreset_driver);
142}
143
144module_init(keyreset_init);
145module_exit(keyreset_exit);