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 */
18 #include <linux/circ_buf.h>
20 #define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
21 #define ACPI_AML_SEC_TICK 1
22 #define ACPI_AML_USEC_PEEK 200
23 #define ACPI_AML_BUF_SIZE 4096
25 #define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
26 #define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
27 #define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
29 #define ACPI_AML_LOG_START 0x00
30 #define ACPI_AML_PROMPT_START 0x01
31 #define ACPI_AML_PROMPT_STOP 0x02
32 #define ACPI_AML_LOG_STOP 0x03
33 #define ACPI_AML_PROMPT_ROLL 0x04
35 #define ACPI_AML_INTERACTIVE 0x00
36 #define ACPI_AML_BATCH 0x01
38 #define circ_count(circ) \
39 (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
40 #define circ_count_to_end(circ) \
41 (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
42 #define circ_space(circ) \
43 (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
44 #define circ_space_to_end(circ) \
45 (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
47 #define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
48 #define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
49 #define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
50 #define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
52 #define ACPI_AML_DO(_fd, _op, _buf, _ret) \
54 _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
57 "%s %s pipe closed.\n", #_buf, #_op); \
61 #define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
63 _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
64 &acpi_aml_##_buf##_crc); \
70 static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
71 static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
72 static struct circ_buf acpi_aml_cmd_crc = {
73 .buf = acpi_aml_cmd_buf,
77 static struct circ_buf acpi_aml_log_crc = {
78 .buf = acpi_aml_log_buf,
82 static const char *acpi_aml_file_path = ACPI_AML_FILE;
83 static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
84 static bool acpi_aml_exit;
86 static bool acpi_aml_batch_drain;
87 static unsigned long acpi_aml_batch_state;
88 static char acpi_aml_batch_prompt;
89 static char acpi_aml_batch_roll;
90 static unsigned long acpi_aml_log_state;
91 static char *acpi_aml_batch_cmd = NULL;
92 static char *acpi_aml_batch_pos = NULL;
94 static int acpi_aml_set_fl(int fd, int flags)
98 ret = fcntl(fd, F_GETFL, 0);
100 perror("fcntl(F_GETFL)");
104 ret = fcntl(fd, F_SETFL, flags);
106 perror("fcntl(F_SETFL)");
112 static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
120 static int acpi_aml_read(int fd, struct circ_buf *crc)
125 p = &crc->buf[crc->head];
126 len = circ_space_to_end(crc);
127 len = read(fd, p, len);
131 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
135 static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
139 int remained = strlen(acpi_aml_batch_pos);
141 p = &crc->buf[crc->head];
142 len = circ_space_to_end(crc);
143 if (len > remained) {
144 memcpy(p, acpi_aml_batch_pos, remained);
145 acpi_aml_batch_pos += remained;
148 memcpy(p, acpi_aml_batch_pos, len);
149 acpi_aml_batch_pos += len;
152 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
156 static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
162 p = &crc->buf[crc->head];
163 len = circ_space_to_end(crc);
164 while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
165 if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
166 *p = acpi_aml_batch_roll;
168 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
170 acpi_aml_log_state = ACPI_AML_LOG_START;
172 len = read(fd, p, 1);
180 switch (acpi_aml_log_state) {
181 case ACPI_AML_LOG_START:
183 acpi_aml_log_state = ACPI_AML_PROMPT_START;
184 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
187 case ACPI_AML_PROMPT_START:
188 if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
189 *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
190 acpi_aml_batch_prompt = *p;
191 acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
194 acpi_aml_log_state = ACPI_AML_LOG_START;
195 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
199 case ACPI_AML_PROMPT_STOP:
201 acpi_aml_log_state = ACPI_AML_LOG_STOP;
202 acpi_aml_exit = true;
205 acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
206 acpi_aml_batch_roll = *p;
207 *p = acpi_aml_batch_prompt;
208 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
220 static int acpi_aml_write(int fd, struct circ_buf *crc)
225 p = &crc->buf[crc->tail];
226 len = circ_count_to_end(crc);
227 len = write(fd, p, len);
231 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
235 static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
240 p = &crc->buf[crc->tail];
241 len = circ_count_to_end(crc);
242 if (!acpi_aml_batch_drain) {
243 len = write(fd, p, len);
248 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
252 static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
256 len = acpi_aml_write(fd, crc);
257 if (circ_count_to_end(crc) == 0)
258 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
262 static void acpi_aml_loop(int fd)
270 if (acpi_aml_mode == ACPI_AML_BATCH) {
271 acpi_aml_log_state = ACPI_AML_LOG_START;
272 acpi_aml_batch_pos = acpi_aml_batch_cmd;
273 if (acpi_aml_batch_drain)
274 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
276 acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
278 acpi_aml_exit = false;
279 while (!acpi_aml_exit) {
280 tv.tv_sec = ACPI_AML_SEC_TICK;
285 if (acpi_aml_cmd_space()) {
286 if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
287 maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
288 else if (strlen(acpi_aml_batch_pos) &&
289 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
290 ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
292 if (acpi_aml_cmd_count() &&
293 (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
294 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
295 maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
296 if (acpi_aml_log_space() &&
297 (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
298 acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
299 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
300 if (acpi_aml_log_count())
301 maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
303 ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
309 if (FD_ISSET(STDIN_FILENO, &rfds))
310 ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
311 if (FD_ISSET(fd, &wfds)) {
312 if (acpi_aml_mode == ACPI_AML_BATCH)
313 ACPI_AML_BATCH_DO(fd, write, cmd, ret);
315 ACPI_AML_DO(fd, write, cmd, ret);
317 if (FD_ISSET(fd, &rfds)) {
318 if (acpi_aml_mode == ACPI_AML_BATCH)
319 ACPI_AML_BATCH_DO(fd, read, log, ret);
321 ACPI_AML_DO(fd, read, log, ret);
323 if (FD_ISSET(STDOUT_FILENO, &wfds)) {
324 if (acpi_aml_mode == ACPI_AML_BATCH)
325 ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
327 ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
333 static bool acpi_aml_readable(int fd)
341 tv.tv_usec = ACPI_AML_USEC_PEEK;
343 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
344 ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
347 if (ret > 0 && FD_ISSET(fd, &rfds))
353 * This is a userspace IO flush implementation, replying on the prompt
354 * characters and can be turned into a flush() call after kernel implements
355 * .flush() filesystem operation.
357 static void acpi_aml_flush(int fd)
359 while (acpi_aml_readable(fd)) {
360 acpi_aml_batch_drain = true;
362 acpi_aml_batch_drain = false;
366 void usage(FILE *file, char *progname)
368 fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
369 fprintf(file, "\nOptions:\n");
370 fprintf(file, " -b Specify command to be executed in batch mode\n");
371 fprintf(file, " -f Specify interface file other than");
372 fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
373 fprintf(file, " -h Print this help message\n");
376 int main(int argc, char **argv)
381 int ret = EXIT_SUCCESS;
383 while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
386 if (acpi_aml_batch_cmd) {
387 fprintf(stderr, "Already specify %s\n",
392 len = strlen(optarg);
393 acpi_aml_batch_cmd = calloc(len + 2, 1);
394 if (!acpi_aml_batch_cmd) {
399 memcpy(acpi_aml_batch_cmd, optarg, len);
400 acpi_aml_batch_cmd[len] = '\n';
401 acpi_aml_mode = ACPI_AML_BATCH;
404 acpi_aml_file_path = optarg;
407 usage(stdout, argv[0]);
412 usage(stderr, argv[0]);
419 fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
425 acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
426 acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
428 if (acpi_aml_mode == ACPI_AML_BATCH)
435 if (acpi_aml_batch_cmd)
436 free(acpi_aml_batch_cmd);