2024-09-09 08:52:07 +00:00
|
|
|
/* drivers/input/keyreset.c
|
|
|
|
*
|
2024-09-09 08:57:42 +00:00
|
|
|
* Copyright (C) 2014 Google, Inc.
|
2024-09-09 08:52:07 +00:00
|
|
|
*
|
|
|
|
* This software is licensed under the terms of the GNU General Public
|
|
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
|
|
* may be copied, distributed, and modified under those terms.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/input.h>
|
|
|
|
#include <linux/keyreset.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/reboot.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/syscalls.h>
|
2024-09-09 08:57:42 +00:00
|
|
|
#include <linux/keycombo.h>
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
struct keyreset_state {
|
2024-09-09 08:57:42 +00:00
|
|
|
int restart_requested;
|
2024-09-09 08:52:07 +00:00
|
|
|
int (*reset_fn)(void);
|
2024-09-09 08:57:42 +00:00
|
|
|
struct platform_device *pdev_child;
|
|
|
|
struct work_struct restart_work;
|
2024-09-09 08:52:07 +00:00
|
|
|
};
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
static void do_restart(struct work_struct *unused)
|
2024-09-09 08:52:07 +00:00
|
|
|
{
|
|
|
|
sys_sync();
|
|
|
|
kernel_restart(NULL);
|
|
|
|
}
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
static void do_reset_fn(void *priv)
|
2024-09-09 08:52:07 +00:00
|
|
|
{
|
2024-09-09 08:57:42 +00:00
|
|
|
struct keyreset_state *state = priv;
|
|
|
|
if (state->restart_requested)
|
|
|
|
panic("keyboard reset failed, %d", state->restart_requested);
|
|
|
|
if (state->reset_fn) {
|
|
|
|
state->restart_requested = state->reset_fn();
|
2024-09-09 08:52:07 +00:00
|
|
|
} else {
|
2024-09-09 08:57:42 +00:00
|
|
|
pr_info("keyboard reset\n");
|
|
|
|
schedule_work(&state->restart_work);
|
|
|
|
state->restart_requested = 1;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int keyreset_probe(struct platform_device *pdev)
|
|
|
|
{
|
2024-09-09 08:57:42 +00:00
|
|
|
int ret = -ENOMEM;
|
|
|
|
struct keycombo_platform_data *pdata_child;
|
|
|
|
struct keyreset_platform_data *pdata = pdev->dev.platform_data;
|
|
|
|
int up_size = 0, down_size = 0, size;
|
2024-09-09 08:52:07 +00:00
|
|
|
int key, *keyp;
|
|
|
|
struct keyreset_state *state;
|
|
|
|
|
|
|
|
if (!pdata)
|
|
|
|
return -EINVAL;
|
2024-09-09 08:57:42 +00:00
|
|
|
state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
|
2024-09-09 08:52:07 +00:00
|
|
|
if (!state)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
state->pdev_child = platform_device_alloc(KEYCOMBO_NAME,
|
|
|
|
PLATFORM_DEVID_AUTO);
|
|
|
|
if (!state->pdev_child)
|
|
|
|
return -ENOMEM;
|
|
|
|
state->pdev_child->dev.parent = &pdev->dev;
|
|
|
|
INIT_WORK(&state->restart_work, do_restart);
|
|
|
|
|
2024-09-09 08:52:07 +00:00
|
|
|
keyp = pdata->keys_down;
|
|
|
|
while ((key = *keyp++)) {
|
|
|
|
if (key >= KEY_MAX)
|
|
|
|
continue;
|
2024-09-09 08:57:42 +00:00
|
|
|
down_size++;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
if (pdata->keys_up) {
|
|
|
|
keyp = pdata->keys_up;
|
|
|
|
while ((key = *keyp++)) {
|
|
|
|
if (key >= KEY_MAX)
|
|
|
|
continue;
|
2024-09-09 08:57:42 +00:00
|
|
|
up_size++;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-09 08:57:42 +00:00
|
|
|
size = sizeof(struct keycombo_platform_data)
|
|
|
|
+ sizeof(int) * (down_size + 1);
|
|
|
|
pdata_child = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
|
|
|
|
if (!pdata_child)
|
|
|
|
goto error;
|
|
|
|
memcpy(pdata_child->keys_down, pdata->keys_down,
|
|
|
|
sizeof(int) * down_size);
|
|
|
|
if (up_size > 0) {
|
|
|
|
pdata_child->keys_up = devm_kzalloc(&pdev->dev, up_size + 1,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!pdata_child->keys_up)
|
|
|
|
goto error;
|
|
|
|
memcpy(pdata_child->keys_up, pdata->keys_up,
|
|
|
|
sizeof(int) * up_size);
|
|
|
|
if (!pdata_child->keys_up)
|
|
|
|
goto error;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
2024-09-09 08:57:42 +00:00
|
|
|
state->reset_fn = pdata->reset_fn;
|
|
|
|
pdata_child->key_down_fn = do_reset_fn;
|
|
|
|
pdata_child->priv = state;
|
|
|
|
pdata_child->key_down_delay = pdata->key_down_delay;
|
|
|
|
ret = platform_device_add_data(state->pdev_child, pdata_child, size);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
2024-09-09 08:52:07 +00:00
|
|
|
platform_set_drvdata(pdev, state);
|
2024-09-09 08:57:42 +00:00
|
|
|
return platform_device_add(state->pdev_child);
|
|
|
|
error:
|
|
|
|
platform_device_put(state->pdev_child);
|
|
|
|
return ret;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int keyreset_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct keyreset_state *state = platform_get_drvdata(pdev);
|
2024-09-09 08:57:42 +00:00
|
|
|
platform_device_put(state->pdev_child);
|
2024-09-09 08:52:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct platform_driver keyreset_driver = {
|
|
|
|
.driver.name = KEYRESET_NAME,
|
|
|
|
.probe = keyreset_probe,
|
|
|
|
.remove = keyreset_remove,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init keyreset_init(void)
|
|
|
|
{
|
|
|
|
return platform_driver_register(&keyreset_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit keyreset_exit(void)
|
|
|
|
{
|
|
|
|
return platform_driver_unregister(&keyreset_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(keyreset_init);
|
|
|
|
module_exit(keyreset_exit);
|