• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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