2 * ACPI AML interfacing userspace utility
4 * Copyright (C) 2015, Intel Corporation
5 * Authors: Lv Zheng <lv.zheng@intel.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <acpi/acpi.h>
14 /* Headers not included by include/acpi/platform/aclinux.h */
23 #include <sys/select.h>
24 #include "../../../../../include/linux/circ_buf.h"
26 #define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
27 #define ACPI_AML_SEC_TICK 1
28 #define ACPI_AML_USEC_PEEK 200
29 #define ACPI_AML_BUF_SIZE 4096
31 #define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
32 #define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
33 #define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
35 #define ACPI_AML_LOG_START 0x00
36 #define ACPI_AML_PROMPT_START 0x01
37 #define ACPI_AML_PROMPT_STOP 0x02
38 #define ACPI_AML_LOG_STOP 0x03
39 #define ACPI_AML_PROMPT_ROLL 0x04
41 #define ACPI_AML_INTERACTIVE 0x00
42 #define ACPI_AML_BATCH 0x01
44 #define circ_count(circ) \
45 (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
46 #define circ_count_to_end(circ) \
47 (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
48 #define circ_space(circ) \
49 (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
50 #define circ_space_to_end(circ) \
51 (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
53 #define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
54 #define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
55 #define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
56 #define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
58 #define ACPI_AML_DO(_fd, _op, _buf, _ret) \
60 _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
63 "%s %s pipe closed.\n", #_buf, #_op); \
67 #define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
69 _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
70 &acpi_aml_##_buf##_crc); \
76 static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
77 static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
78 static struct circ_buf acpi_aml_cmd_crc = {
79 .buf = acpi_aml_cmd_buf,
83 static struct circ_buf acpi_aml_log_crc = {
84 .buf = acpi_aml_log_buf,
88 static const char *acpi_aml_file_path = ACPI_AML_FILE;
89 static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
90 static bool acpi_aml_exit;
92 static bool acpi_aml_batch_drain;
93 static unsigned long acpi_aml_batch_state;
94 static char acpi_aml_batch_prompt;
95 static char acpi_aml_batch_roll;
96 static unsigned long acpi_aml_log_state;
97 static char *acpi_aml_batch_cmd = NULL;
98 static char *acpi_aml_batch_pos = NULL;
100 static int acpi_aml_set_fl(int fd, int flags)
104 ret = fcntl(fd, F_GETFL, 0);
106 perror("fcntl(F_GETFL)");
110 ret = fcntl(fd, F_SETFL, flags);
112 perror("fcntl(F_SETFL)");
118 static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
126 static int acpi_aml_read(int fd, struct circ_buf *crc)
131 p = &crc->buf[crc->head];
132 len = circ_space_to_end(crc);
133 len = read(fd, p, len);
137 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
141 static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
145 int remained = strlen(acpi_aml_batch_pos);
147 p = &crc->buf[crc->head];
148 len = circ_space_to_end(crc);
149 if (len > remained) {
150 memcpy(p, acpi_aml_batch_pos, remained);
151 acpi_aml_batch_pos += remained;
154 memcpy(p, acpi_aml_batch_pos, len);
155 acpi_aml_batch_pos += len;
158 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
162 static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
168 p = &crc->buf[crc->head];
169 len = circ_space_to_end(crc);
170 while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
171 if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
172 *p = acpi_aml_batch_roll;
174 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
176 acpi_aml_log_state = ACPI_AML_LOG_START;
178 len = read(fd, p, 1);
186 switch (acpi_aml_log_state) {
187 case ACPI_AML_LOG_START:
189 acpi_aml_log_state = ACPI_AML_PROMPT_START;
190 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
193 case ACPI_AML_PROMPT_START:
194 if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
195 *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
196 acpi_aml_batch_prompt = *p;
197 acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
200 acpi_aml_log_state = ACPI_AML_LOG_START;
201 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
205 case ACPI_AML_PROMPT_STOP:
207 acpi_aml_log_state = ACPI_AML_LOG_STOP;
208 acpi_aml_exit = true;
211 acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
212 acpi_aml_batch_roll = *p;
213 *p = acpi_aml_batch_prompt;
214 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
226 static int acpi_aml_write(int fd, struct circ_buf *crc)
231 p = &crc->buf[crc->tail];
232 len = circ_count_to_end(crc);
233 len = write(fd, p, len);
237 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
241 static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
246 p = &crc->buf[crc->tail];
247 len = circ_count_to_end(crc);
248 if (!acpi_aml_batch_drain) {
249 len = write(fd, p, len);
254 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
258 static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
262 len = acpi_aml_write(fd, crc);
263 if (circ_count_to_end(crc) == 0)
264 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
268 static void acpi_aml_loop(int fd)
276 if (acpi_aml_mode == ACPI_AML_BATCH) {
277 acpi_aml_log_state = ACPI_AML_LOG_START;
278 acpi_aml_batch_pos = acpi_aml_batch_cmd;
279 if (acpi_aml_batch_drain)
280 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
282 acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
284 acpi_aml_exit = false;
285 while (!acpi_aml_exit) {
286 tv.tv_sec = ACPI_AML_SEC_TICK;
291 if (acpi_aml_cmd_space()) {
292 if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
293 maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
294 else if (strlen(acpi_aml_batch_pos) &&
295 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
296 ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
298 if (acpi_aml_cmd_count() &&
299 (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
300 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
301 maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
302 if (acpi_aml_log_space() &&
303 (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
304 acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
305 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
306 if (acpi_aml_log_count())
307 maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
309 ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
315 if (FD_ISSET(STDIN_FILENO, &rfds))
316 ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
317 if (FD_ISSET(fd, &wfds)) {
318 if (acpi_aml_mode == ACPI_AML_BATCH)
319 ACPI_AML_BATCH_DO(fd, write, cmd, ret);
321 ACPI_AML_DO(fd, write, cmd, ret);
323 if (FD_ISSET(fd, &rfds)) {
324 if (acpi_aml_mode == ACPI_AML_BATCH)
325 ACPI_AML_BATCH_DO(fd, read, log, ret);
327 ACPI_AML_DO(fd, read, log, ret);
329 if (FD_ISSET(STDOUT_FILENO, &wfds)) {
330 if (acpi_aml_mode == ACPI_AML_BATCH)
331 ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
333 ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
339 static bool acpi_aml_readable(int fd)
347 tv.tv_usec = ACPI_AML_USEC_PEEK;
349 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
350 ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
353 if (ret > 0 && FD_ISSET(fd, &rfds))
359 * This is a userspace IO flush implementation, replying on the prompt
360 * characters and can be turned into a flush() call after kernel implements
361 * .flush() filesystem operation.
363 static void acpi_aml_flush(int fd)
365 while (acpi_aml_readable(fd)) {
366 acpi_aml_batch_drain = true;
368 acpi_aml_batch_drain = false;
372 void usage(FILE *file, char *progname)
374 fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
375 fprintf(file, "\nOptions:\n");
376 fprintf(file, " -b Specify command to be executed in batch mode\n");
377 fprintf(file, " -f Specify interface file other than");
378 fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
379 fprintf(file, " -h Print this help message\n");
382 int main(int argc, char **argv)
387 int ret = EXIT_SUCCESS;
389 while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
392 if (acpi_aml_batch_cmd) {
393 fprintf(stderr, "Already specify %s\n",
398 len = strlen(optarg);
399 acpi_aml_batch_cmd = calloc(len + 2, 1);
400 if (!acpi_aml_batch_cmd) {
405 memcpy(acpi_aml_batch_cmd, optarg, len);
406 acpi_aml_batch_cmd[len] = '\n';
407 acpi_aml_mode = ACPI_AML_BATCH;
410 acpi_aml_file_path = optarg;
413 usage(stdout, argv[0]);
418 usage(stderr, argv[0]);
425 fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
431 acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
432 acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
434 if (acpi_aml_mode == ACPI_AML_BATCH)
441 if (acpi_aml_batch_cmd)
442 free(acpi_aml_batch_cmd);