1 // SPDX-License-Identifier: GPL-2.0-only
3 * ACPI AML interfacing userspace utility
5 * Copyright (C) 2015, Intel Corporation
6 * Authors: Lv Zheng <lv.zheng@intel.com>
11 /* Headers not included by include/acpi/platform/aclinux.h */
20 #include <sys/select.h>
21 #include "../../../../../include/linux/circ_buf.h"
23 #define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
24 #define ACPI_AML_SEC_TICK 1
25 #define ACPI_AML_USEC_PEEK 200
26 #define ACPI_AML_BUF_SIZE 4096
28 #define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
29 #define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
30 #define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
32 #define ACPI_AML_LOG_START 0x00
33 #define ACPI_AML_PROMPT_START 0x01
34 #define ACPI_AML_PROMPT_STOP 0x02
35 #define ACPI_AML_LOG_STOP 0x03
36 #define ACPI_AML_PROMPT_ROLL 0x04
38 #define ACPI_AML_INTERACTIVE 0x00
39 #define ACPI_AML_BATCH 0x01
41 #define circ_count(circ) \
42 (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
43 #define circ_count_to_end(circ) \
44 (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
45 #define circ_space(circ) \
46 (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
47 #define circ_space_to_end(circ) \
48 (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
50 #define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
51 #define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
52 #define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
53 #define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
55 #define ACPI_AML_DO(_fd, _op, _buf, _ret) \
57 _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
60 "%s %s pipe closed.\n", #_buf, #_op); \
64 #define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
66 _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
67 &acpi_aml_##_buf##_crc); \
73 static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
74 static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
75 static struct circ_buf acpi_aml_cmd_crc = {
76 .buf = acpi_aml_cmd_buf,
80 static struct circ_buf acpi_aml_log_crc = {
81 .buf = acpi_aml_log_buf,
85 static const char *acpi_aml_file_path = ACPI_AML_FILE;
86 static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
87 static bool acpi_aml_exit;
89 static bool acpi_aml_batch_drain;
90 static unsigned long acpi_aml_batch_state;
91 static char acpi_aml_batch_prompt;
92 static char acpi_aml_batch_roll;
93 static unsigned long acpi_aml_log_state;
94 static char *acpi_aml_batch_cmd = NULL;
95 static char *acpi_aml_batch_pos = NULL;
97 static int acpi_aml_set_fl(int fd, int flags)
101 ret = fcntl(fd, F_GETFL, 0);
103 perror("fcntl(F_GETFL)");
107 ret = fcntl(fd, F_SETFL, flags);
109 perror("fcntl(F_SETFL)");
115 static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
123 static int acpi_aml_read(int fd, struct circ_buf *crc)
128 p = &crc->buf[crc->head];
129 len = circ_space_to_end(crc);
130 len = read(fd, p, len);
134 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
138 static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
142 int remained = strlen(acpi_aml_batch_pos);
144 p = &crc->buf[crc->head];
145 len = circ_space_to_end(crc);
146 if (len > remained) {
147 memcpy(p, acpi_aml_batch_pos, remained);
148 acpi_aml_batch_pos += remained;
151 memcpy(p, acpi_aml_batch_pos, len);
152 acpi_aml_batch_pos += len;
155 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
159 static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
165 p = &crc->buf[crc->head];
166 len = circ_space_to_end(crc);
167 while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
168 if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
169 *p = acpi_aml_batch_roll;
171 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
173 acpi_aml_log_state = ACPI_AML_LOG_START;
175 len = read(fd, p, 1);
183 switch (acpi_aml_log_state) {
184 case ACPI_AML_LOG_START:
186 acpi_aml_log_state = ACPI_AML_PROMPT_START;
187 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
190 case ACPI_AML_PROMPT_START:
191 if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
192 *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
193 acpi_aml_batch_prompt = *p;
194 acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
197 acpi_aml_log_state = ACPI_AML_LOG_START;
198 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
202 case ACPI_AML_PROMPT_STOP:
204 acpi_aml_log_state = ACPI_AML_LOG_STOP;
205 acpi_aml_exit = true;
208 acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
209 acpi_aml_batch_roll = *p;
210 *p = acpi_aml_batch_prompt;
211 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
223 static int acpi_aml_write(int fd, struct circ_buf *crc)
228 p = &crc->buf[crc->tail];
229 len = circ_count_to_end(crc);
230 len = write(fd, p, len);
234 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
238 static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
243 p = &crc->buf[crc->tail];
244 len = circ_count_to_end(crc);
245 if (!acpi_aml_batch_drain) {
246 len = write(fd, p, len);
251 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
255 static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
259 len = acpi_aml_write(fd, crc);
260 if (circ_count_to_end(crc) == 0)
261 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
265 static void acpi_aml_loop(int fd)
273 if (acpi_aml_mode == ACPI_AML_BATCH) {
274 acpi_aml_log_state = ACPI_AML_LOG_START;
275 acpi_aml_batch_pos = acpi_aml_batch_cmd;
276 if (acpi_aml_batch_drain)
277 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
279 acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
281 acpi_aml_exit = false;
282 while (!acpi_aml_exit) {
283 tv.tv_sec = ACPI_AML_SEC_TICK;
288 if (acpi_aml_cmd_space()) {
289 if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
290 maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
291 else if (strlen(acpi_aml_batch_pos) &&
292 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
293 ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
295 if (acpi_aml_cmd_count() &&
296 (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
297 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
298 maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
299 if (acpi_aml_log_space() &&
300 (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
301 acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
302 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
303 if (acpi_aml_log_count())
304 maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
306 ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
312 if (FD_ISSET(STDIN_FILENO, &rfds))
313 ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
314 if (FD_ISSET(fd, &wfds)) {
315 if (acpi_aml_mode == ACPI_AML_BATCH)
316 ACPI_AML_BATCH_DO(fd, write, cmd, ret);
318 ACPI_AML_DO(fd, write, cmd, ret);
320 if (FD_ISSET(fd, &rfds)) {
321 if (acpi_aml_mode == ACPI_AML_BATCH)
322 ACPI_AML_BATCH_DO(fd, read, log, ret);
324 ACPI_AML_DO(fd, read, log, ret);
326 if (FD_ISSET(STDOUT_FILENO, &wfds)) {
327 if (acpi_aml_mode == ACPI_AML_BATCH)
328 ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
330 ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
336 static bool acpi_aml_readable(int fd)
344 tv.tv_usec = ACPI_AML_USEC_PEEK;
346 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
347 ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
350 if (ret > 0 && FD_ISSET(fd, &rfds))
356 * This is a userspace IO flush implementation, replying on the prompt
357 * characters and can be turned into a flush() call after kernel implements
358 * .flush() filesystem operation.
360 static void acpi_aml_flush(int fd)
362 while (acpi_aml_readable(fd)) {
363 acpi_aml_batch_drain = true;
365 acpi_aml_batch_drain = false;
369 void usage(FILE *file, char *progname)
371 fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
372 fprintf(file, "\nOptions:\n");
373 fprintf(file, " -b Specify command to be executed in batch mode\n");
374 fprintf(file, " -f Specify interface file other than");
375 fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
376 fprintf(file, " -h Print this help message\n");
379 int main(int argc, char **argv)
384 int ret = EXIT_SUCCESS;
386 while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
389 if (acpi_aml_batch_cmd) {
390 fprintf(stderr, "Already specify %s\n",
395 len = strlen(optarg);
396 acpi_aml_batch_cmd = calloc(len + 2, 1);
397 if (!acpi_aml_batch_cmd) {
402 memcpy(acpi_aml_batch_cmd, optarg, len);
403 acpi_aml_batch_cmd[len] = '\n';
404 acpi_aml_mode = ACPI_AML_BATCH;
407 acpi_aml_file_path = optarg;
410 usage(stdout, argv[0]);
415 usage(stderr, argv[0]);
422 fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
428 acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
429 acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
431 if (acpi_aml_mode == ACPI_AML_BATCH)
438 if (acpi_aml_batch_cmd)
439 free(acpi_aml_batch_cmd);