1 /*
2  * Copyright (C) 2019-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <sys/cdefs.h>
8 #include <sys/types.h>
9 #include <stdio.h>
10 #include <errno.h>
11 #include <pthread.h>
12 #include <signal.h>
13 #include <stdbool.h>
14 #include <pthread.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <linux/input.h>
18 #include <dirent.h>
19 #include <string.h>
20 #include <stdlib.h>
21 
22 #include "vmmapi.h"
23 #include "acpi.h"
24 #include "mevent.h"
25 #include "monitor.h"
26 #include "log.h"
27 
28 #define POWER_BUTTON_NAME	"power_button"
29 #define POWER_BUTTON_ACPI_DRV	"/sys/bus/acpi/drivers/button/LNXPWRBN:00/"
30 #define POWER_BUTTON_INPUT_DIR POWER_BUTTON_ACPI_DRV"input"
31 #define POWER_BUTTON_PNP0C0C_DRV "/sys/bus/acpi/drivers/button/PNP0C0C:00/"
32 #define POWER_BUTTON_PNP0C0C_DIR POWER_BUTTON_PNP0C0C_DRV"input"
33 
34 static struct mevent *input_evt0;
35 static int pwrbtn_fd = -1;
36 static bool monitor_run;
37 
38 static void
input_event0_handler(int fd,enum ev_type type,void * arg)39 input_event0_handler(int fd, enum ev_type type, void *arg)
40 {
41 	struct input_event ev;
42 	int rc;
43 
44 	rc = read(fd, &ev, sizeof(ev));
45 	if (rc < 0 || rc != sizeof(ev))
46 		return;
47 
48 	/*
49 	 * The input key defines in input-event-codes.h
50 	 * KEY_POWER 116 SC System Power Down
51 	 */
52 	if (ev.code == KEY_POWER && ev.value == 1)
53 		inject_power_button_event(arg);
54 }
55 
56 static int
vm_stop_handler(void * arg)57 vm_stop_handler(void *arg)
58 {
59 	if (!arg)
60 		return -EINVAL;
61 
62 	inject_power_button_event(arg);
63 	return 0;
64 }
65 
66 static int
vm_suspend_handler(void * arg)67 vm_suspend_handler(void *arg)
68 {
69 	/*
70 	 * Invoke vm_stop_handler directly in here since suspend of User VM is
71 	 * set by User VM power button setting.
72 	 */
73 	return vm_stop_handler(arg);
74 }
75 
76 static struct monitor_vm_ops vm_ops = {
77 	.stop = vm_stop_handler,
78 	.suspend = vm_suspend_handler,
79 };
80 
81 static int
input_dir_filter(const struct dirent * dir)82 input_dir_filter(const struct dirent *dir)
83 {
84 	return !strncmp(dir->d_name, "input", 5);
85 }
86 
87 static int
event_dir_filter(const struct dirent * dir)88 event_dir_filter(const struct dirent *dir)
89 {
90 	return !strncmp(dir->d_name, "event", 5);
91 }
92 
93 static int
open_power_button_input_device(const char * drv,const char * dir)94 open_power_button_input_device(const char *drv, const char *dir)
95 {
96 	struct dirent **input_dirs = NULL;
97 	struct dirent **event_dirs = NULL;
98 	int ninput = 0;
99 	int nevent = 0;
100 	char path[256] = {0};
101 	char name[256] = {0};
102 	int rc, fd;
103 
104 	if (access(drv, F_OK) != 0)
105 		return -1;
106 	/*
107 	 * Scan path to get inputN
108 	 * path is /sys/bus/acpi/drivers/button/LNXPWRBN:00/input
109 	 */
110 	ninput = scandir(dir, &input_dirs, input_dir_filter,
111 			alphasort);
112 	if (ninput < 0) {
113 		pr_err("failed to scan power button %s\n",
114 				dir);
115 		goto err;
116 	} else if (ninput == 1) {
117 		rc = snprintf(path, sizeof(path), "%s/%s",
118 				dir, input_dirs[0]->d_name);
119 		if (rc < 0 || rc >= sizeof(path)) {
120 			pr_err("failed to set power button path %d\n",
121 					rc);
122 			goto err_input;
123 		}
124 
125 		/*
126 		 * Scan path to get eventN
127 		 * path is /sys/bus/acpi/drivers/button/LNXPWRBN:00/input/inputN
128 		 */
129 		nevent = scandir(path, &event_dirs, event_dir_filter,
130 				alphasort);
131 		if (nevent < 0) {
132 			pr_err("failed to get power button event %s\n",
133 					path);
134 			goto err_input;
135 		} else if (nevent == 1) {
136 
137 			/* Get the power button input event name */
138 			rc = snprintf(name, sizeof(name), "/dev/input/%s",
139 					event_dirs[0]->d_name);
140 			if (rc < 0 || rc >= sizeof(name)) {
141 				pr_err("power button error %d\n", rc);
142 				goto err_input;
143 			}
144 		} else {
145 			pr_err("power button event number error %d\n",
146 					nevent);
147 			goto err_event;
148 		}
149 	} else {
150 		pr_err("power button input number error %d\n", nevent);
151 		goto err_input;
152 	}
153 
154 	/* Open the input device */
155 	fd = open(name, O_RDONLY);
156 	if (fd > 0)
157 		pr_info("Watching power button on %s\n", name);
158 
159 	while (nevent--)
160 		free(event_dirs[nevent]);
161 	free(event_dirs);
162 	while (ninput--)
163 		free(input_dirs[ninput]);
164 	free(input_dirs);
165 	return fd;
166 
167 err_event:
168 	while (nevent--)
169 		free(event_dirs[nevent]);
170 	free(event_dirs);
171 
172 err_input:
173 	while (ninput--)
174 		free(input_dirs[ninput]);
175 	free(input_dirs);
176 
177 err:
178 	return -1;
179 }
180 
181 static int
open_native_power_button()182 open_native_power_button()
183 {
184 	int fd;
185 
186 	/*
187 	 * Open fixed power button firstly, if it can't be opened
188 	 * try to open control method power button.
189 	 */
190 	fd = open_power_button_input_device(POWER_BUTTON_ACPI_DRV,
191 			POWER_BUTTON_INPUT_DIR);
192 	if (fd < 0)
193 		return open_power_button_input_device(
194 				POWER_BUTTON_PNP0C0C_DRV,
195 				POWER_BUTTON_PNP0C0C_DIR);
196 	else
197 		return fd;
198 }
199 
200 void
power_button_init(struct vmctx * ctx)201 power_button_init(struct vmctx *ctx)
202 {
203 	if (input_evt0 == NULL) {
204 		pwrbtn_fd = open_native_power_button();
205 		if (pwrbtn_fd < 0)
206 			pr_err("open power button error=%d\n",
207 					errno);
208 		else
209 			input_evt0 = mevent_add(pwrbtn_fd, EVF_READ,
210 					input_event0_handler, ctx, NULL, NULL);
211 	}
212 
213 	/*
214 	 * Suspend or shutdown User VM by acrnctl suspend and
215 	 * stop command.
216 	 */
217 	if (monitor_run == false) {
218 		if (monitor_register_vm_ops(&vm_ops, ctx,
219 					POWER_BUTTON_NAME) < 0)
220 			pr_err("failed to register vm ops for power button\n");
221 		else
222 			monitor_run = true;
223 	}
224 }
225 
226 void
power_button_deinit(struct vmctx * ctx)227 power_button_deinit(struct vmctx *ctx)
228 {
229 	if (input_evt0 != NULL) {
230 		mevent_delete_close(input_evt0);
231 		input_evt0 = NULL;
232 		pwrbtn_fd = -1;
233 	}
234 }
235