235 lines
4.8 KiB
C
235 lines
4.8 KiB
C
/*
|
|
* Copyright (C) 2019-2022 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdbool.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <linux/input.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "vmmapi.h"
|
|
#include "acpi.h"
|
|
#include "mevent.h"
|
|
#include "monitor.h"
|
|
#include "log.h"
|
|
|
|
#define POWER_BUTTON_NAME "power_button"
|
|
#define POWER_BUTTON_ACPI_DRV "/sys/bus/acpi/drivers/button/LNXPWRBN:00/"
|
|
#define POWER_BUTTON_INPUT_DIR POWER_BUTTON_ACPI_DRV"input"
|
|
#define POWER_BUTTON_PNP0C0C_DRV "/sys/bus/acpi/drivers/button/PNP0C0C:00/"
|
|
#define POWER_BUTTON_PNP0C0C_DIR POWER_BUTTON_PNP0C0C_DRV"input"
|
|
|
|
static struct mevent *input_evt0;
|
|
static int pwrbtn_fd = -1;
|
|
static bool monitor_run;
|
|
|
|
static void
|
|
input_event0_handler(int fd, enum ev_type type, void *arg)
|
|
{
|
|
struct input_event ev;
|
|
int rc;
|
|
|
|
rc = read(fd, &ev, sizeof(ev));
|
|
if (rc < 0 || rc != sizeof(ev))
|
|
return;
|
|
|
|
/*
|
|
* The input key defines in input-event-codes.h
|
|
* KEY_POWER 116 SC System Power Down
|
|
*/
|
|
if (ev.code == KEY_POWER && ev.value == 1)
|
|
inject_power_button_event(arg);
|
|
}
|
|
|
|
static int
|
|
vm_stop_handler(void *arg)
|
|
{
|
|
if (!arg)
|
|
return -EINVAL;
|
|
|
|
inject_power_button_event(arg);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vm_suspend_handler(void *arg)
|
|
{
|
|
/*
|
|
* Invoke vm_stop_handler directly in here since suspend of User VM is
|
|
* set by User VM power button setting.
|
|
*/
|
|
return vm_stop_handler(arg);
|
|
}
|
|
|
|
static struct monitor_vm_ops vm_ops = {
|
|
.stop = vm_stop_handler,
|
|
.suspend = vm_suspend_handler,
|
|
};
|
|
|
|
static int
|
|
input_dir_filter(const struct dirent *dir)
|
|
{
|
|
return !strncmp(dir->d_name, "input", 5);
|
|
}
|
|
|
|
static int
|
|
event_dir_filter(const struct dirent *dir)
|
|
{
|
|
return !strncmp(dir->d_name, "event", 5);
|
|
}
|
|
|
|
static int
|
|
open_power_button_input_device(const char *drv, const char *dir)
|
|
{
|
|
struct dirent **input_dirs = NULL;
|
|
struct dirent **event_dirs = NULL;
|
|
int ninput = 0;
|
|
int nevent = 0;
|
|
char path[256] = {0};
|
|
char name[256] = {0};
|
|
int rc, fd;
|
|
|
|
if (access(drv, F_OK) != 0)
|
|
return -1;
|
|
/*
|
|
* Scan path to get inputN
|
|
* path is /sys/bus/acpi/drivers/button/LNXPWRBN:00/input
|
|
*/
|
|
ninput = scandir(dir, &input_dirs, input_dir_filter,
|
|
alphasort);
|
|
if (ninput < 0) {
|
|
pr_err("failed to scan power button %s\n",
|
|
dir);
|
|
goto err;
|
|
} else if (ninput == 1) {
|
|
rc = snprintf(path, sizeof(path), "%s/%s",
|
|
dir, input_dirs[0]->d_name);
|
|
if (rc < 0 || rc >= sizeof(path)) {
|
|
pr_err("failed to set power button path %d\n",
|
|
rc);
|
|
goto err_input;
|
|
}
|
|
|
|
/*
|
|
* Scan path to get eventN
|
|
* path is /sys/bus/acpi/drivers/button/LNXPWRBN:00/input/inputN
|
|
*/
|
|
nevent = scandir(path, &event_dirs, event_dir_filter,
|
|
alphasort);
|
|
if (nevent < 0) {
|
|
pr_err("failed to get power button event %s\n",
|
|
path);
|
|
goto err_input;
|
|
} else if (nevent == 1) {
|
|
|
|
/* Get the power button input event name */
|
|
rc = snprintf(name, sizeof(name), "/dev/input/%s",
|
|
event_dirs[0]->d_name);
|
|
if (rc < 0 || rc >= sizeof(name)) {
|
|
pr_err("power button error %d\n", rc);
|
|
goto err_input;
|
|
}
|
|
} else {
|
|
pr_err("power button event number error %d\n",
|
|
nevent);
|
|
goto err_event;
|
|
}
|
|
} else {
|
|
pr_err("power button input number error %d\n", nevent);
|
|
goto err_input;
|
|
}
|
|
|
|
/* Open the input device */
|
|
fd = open(name, O_RDONLY);
|
|
if (fd > 0)
|
|
pr_info("Watching power button on %s\n", name);
|
|
|
|
while (nevent--)
|
|
free(event_dirs[nevent]);
|
|
free(event_dirs);
|
|
while (ninput--)
|
|
free(input_dirs[ninput]);
|
|
free(input_dirs);
|
|
return fd;
|
|
|
|
err_event:
|
|
while (nevent--)
|
|
free(event_dirs[nevent]);
|
|
free(event_dirs);
|
|
|
|
err_input:
|
|
while (ninput--)
|
|
free(input_dirs[ninput]);
|
|
free(input_dirs);
|
|
|
|
err:
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
open_native_power_button()
|
|
{
|
|
int fd;
|
|
|
|
/*
|
|
* Open fixed power button firstly, if it can't be opened
|
|
* try to open control method power button.
|
|
*/
|
|
fd = open_power_button_input_device(POWER_BUTTON_ACPI_DRV,
|
|
POWER_BUTTON_INPUT_DIR);
|
|
if (fd < 0)
|
|
return open_power_button_input_device(
|
|
POWER_BUTTON_PNP0C0C_DRV,
|
|
POWER_BUTTON_PNP0C0C_DIR);
|
|
else
|
|
return fd;
|
|
}
|
|
|
|
void
|
|
power_button_init(struct vmctx *ctx)
|
|
{
|
|
if (input_evt0 == NULL) {
|
|
pwrbtn_fd = open_native_power_button();
|
|
if (pwrbtn_fd < 0)
|
|
pr_err("open power button error=%d\n",
|
|
errno);
|
|
else
|
|
input_evt0 = mevent_add(pwrbtn_fd, EVF_READ,
|
|
input_event0_handler, ctx, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
* Suspend or shutdown User VM by acrnctl suspend and
|
|
* stop command.
|
|
*/
|
|
if (monitor_run == false) {
|
|
if (monitor_register_vm_ops(&vm_ops, ctx,
|
|
POWER_BUTTON_NAME) < 0)
|
|
pr_err("failed to register vm ops for power button\n");
|
|
else
|
|
monitor_run = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
power_button_deinit(struct vmctx *ctx)
|
|
{
|
|
if (input_evt0 != NULL) {
|
|
mevent_delete_close(input_evt0);
|
|
input_evt0 = NULL;
|
|
pwrbtn_fd = -1;
|
|
}
|
|
}
|