Merge remote-tracking branch 'lsk/linux-linaro-lsk-v4.4-android' into linux-linaro...
[firefly-linux-kernel-4.4.55.git] / drivers / input / keyreset.c
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
33 static void do_restart(struct work_struct *unused)
34 {
35         sys_sync();
36         kernel_restart(NULL);
37 }
38
39 static void do_reset_fn(void *priv)
40 {
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();
46         } else {
47                 pr_info("keyboard reset\n");
48                 schedule_work(&state->restart_work);
49                 state->restart_requested = 1;
50         }
51 }
52
53 static int keyreset_probe(struct platform_device *pdev)
54 {
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;
59         int key, *keyp;
60         struct keyreset_state *state;
61
62         if (!pdata)
63                 return -EINVAL;
64         state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
65         if (!state)
66                 return -ENOMEM;
67
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;
73         INIT_WORK(&state->restart_work, do_restart);
74
75         keyp = pdata->keys_down;
76         while ((key = *keyp++)) {
77                 if (key >= KEY_MAX)
78                         continue;
79                 down_size++;
80         }
81         if (pdata->keys_up) {
82                 keyp = pdata->keys_up;
83                 while ((key = *keyp++)) {
84                         if (key >= KEY_MAX)
85                                 continue;
86                         up_size++;
87                 }
88         }
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;
105         }
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;
113         platform_set_drvdata(pdev, state);
114         return platform_device_add(state->pdev_child);
115 error:
116         platform_device_put(state->pdev_child);
117         return ret;
118 }
119
120 int keyreset_remove(struct platform_device *pdev)
121 {
122         struct keyreset_state *state = platform_get_drvdata(pdev);
123         platform_device_put(state->pdev_child);
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);