GNU Linux-libre 4.14.266-gnu1
[releases.git] / tools / power / acpi / tools / acpidbg / acpidbg.c
1 /*
2  * ACPI AML interfacing userspace utility
3  *
4  * Copyright (C) 2015, Intel Corporation
5  * Authors: Lv Zheng <lv.zheng@intel.com>
6  *
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.
10  */
11
12 #include <acpi/acpi.h>
13
14 /* Headers not included by include/acpi/platform/aclinux.h */
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <error.h>
20 #include <stdbool.h>
21 #include <fcntl.h>
22 #include <assert.h>
23 #include <sys/select.h>
24 #include "../../../../../include/linux/circ_buf.h"
25
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
30
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 */
34
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
40
41 #define ACPI_AML_INTERACTIVE    0x00
42 #define ACPI_AML_BATCH          0x01
43
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))
52
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)
57
58 #define ACPI_AML_DO(_fd, _op, _buf, _ret)                               \
59         do {                                                            \
60                 _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc);     \
61                 if (_ret == 0) {                                        \
62                         fprintf(stderr,                                 \
63                                 "%s %s pipe closed.\n", #_buf, #_op);   \
64                         return;                                         \
65                 }                                                       \
66         } while (0)
67 #define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret)                         \
68         do {                                                            \
69                 _ret = acpi_aml_##_op##_batch_##_buf(_fd,               \
70                          &acpi_aml_##_buf##_crc);                       \
71                 if (_ret == 0)                                          \
72                         return;                                         \
73         } while (0)
74
75
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,
80         .head = 0,
81         .tail = 0,
82 };
83 static struct circ_buf acpi_aml_log_crc = {
84         .buf = acpi_aml_log_buf,
85         .head = 0,
86         .tail = 0,
87 };
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;
91
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;
99
100 static int acpi_aml_set_fl(int fd, int flags)
101 {
102         int ret;
103
104         ret = fcntl(fd, F_GETFL, 0);
105         if (ret < 0) {
106                 perror("fcntl(F_GETFL)");
107                 return ret;
108         }
109         flags |= ret;
110         ret = fcntl(fd, F_SETFL, flags);
111         if (ret < 0) {
112                 perror("fcntl(F_SETFL)");
113                 return ret;
114         }
115         return ret;
116 }
117
118 static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
119 {
120         if (fd > maxfd)
121                 maxfd = fd;
122         FD_SET(fd, set);
123         return maxfd;
124 }
125
126 static int acpi_aml_read(int fd, struct circ_buf *crc)
127 {
128         char *p;
129         int len;
130
131         p = &crc->buf[crc->head];
132         len = circ_space_to_end(crc);
133         len = read(fd, p, len);
134         if (len < 0)
135                 perror("read");
136         else if (len > 0)
137                 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
138         return len;
139 }
140
141 static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
142 {
143         char *p;
144         int len;
145         int remained = strlen(acpi_aml_batch_pos);
146
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;
152                 len = remained;
153         } else {
154                 memcpy(p, acpi_aml_batch_pos, len);
155                 acpi_aml_batch_pos += len;
156         }
157         if (len > 0)
158                 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
159         return len;
160 }
161
162 static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
163 {
164         char *p;
165         int len;
166         int ret = 0;
167
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;
173                         len = 1;
174                         crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
175                         ret += 1;
176                         acpi_aml_log_state = ACPI_AML_LOG_START;
177                 } else {
178                         len = read(fd, p, 1);
179                         if (len <= 0) {
180                                 if (len < 0)
181                                         perror("read");
182                                 ret = len;
183                                 break;
184                         }
185                 }
186                 switch (acpi_aml_log_state) {
187                 case ACPI_AML_LOG_START:
188                         if (*p == '\n')
189                                 acpi_aml_log_state = ACPI_AML_PROMPT_START;
190                         crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
191                         ret += 1;
192                         break;
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;
198                         } else {
199                                 if (*p != '\n')
200                                         acpi_aml_log_state = ACPI_AML_LOG_START;
201                                 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
202                                 ret += 1;
203                         }
204                         break;
205                 case ACPI_AML_PROMPT_STOP:
206                         if (*p == ' ') {
207                                 acpi_aml_log_state = ACPI_AML_LOG_STOP;
208                                 acpi_aml_exit = true;
209                         } else {
210                                 /* Roll back */
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);
215                                 ret += 1;
216                         }
217                         break;
218                 default:
219                         assert(0);
220                         break;
221                 }
222         }
223         return ret;
224 }
225
226 static int acpi_aml_write(int fd, struct circ_buf *crc)
227 {
228         char *p;
229         int len;
230
231         p = &crc->buf[crc->tail];
232         len = circ_count_to_end(crc);
233         len = write(fd, p, len);
234         if (len < 0)
235                 perror("write");
236         else if (len > 0)
237                 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
238         return len;
239 }
240
241 static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
242 {
243         char *p;
244         int len;
245
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);
250                 if (len < 0)
251                         perror("write");
252         }
253         if (len > 0)
254                 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
255         return len;
256 }
257
258 static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
259 {
260         int len;
261
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;
265         return len;
266 }
267
268 static void acpi_aml_loop(int fd)
269 {
270         fd_set rfds;
271         fd_set wfds;
272         struct timeval tv;
273         int ret;
274         int maxfd = 0;
275
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;
281                 else
282                         acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
283         }
284         acpi_aml_exit = false;
285         while (!acpi_aml_exit) {
286                 tv.tv_sec = ACPI_AML_SEC_TICK;
287                 tv.tv_usec = 0;
288                 FD_ZERO(&rfds);
289                 FD_ZERO(&wfds);
290
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);
297                 }
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);
308
309                 ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
310                 if (ret < 0) {
311                         perror("select");
312                         break;
313                 }
314                 if (ret > 0) {
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);
320                                 else
321                                         ACPI_AML_DO(fd, write, cmd, ret);
322                         }
323                         if (FD_ISSET(fd, &rfds)) {
324                                 if (acpi_aml_mode == ACPI_AML_BATCH)
325                                         ACPI_AML_BATCH_DO(fd, read, log, ret);
326                                 else
327                                         ACPI_AML_DO(fd, read, log, ret);
328                         }
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);
332                                 else
333                                         ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
334                         }
335                 }
336         }
337 }
338
339 static bool acpi_aml_readable(int fd)
340 {
341         fd_set rfds;
342         struct timeval tv;
343         int ret;
344         int maxfd = 0;
345
346         tv.tv_sec = 0;
347         tv.tv_usec = ACPI_AML_USEC_PEEK;
348         FD_ZERO(&rfds);
349         maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
350         ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
351         if (ret < 0)
352                 perror("select");
353         if (ret > 0 && FD_ISSET(fd, &rfds))
354                 return true;
355         return false;
356 }
357
358 /*
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.
362  */
363 static void acpi_aml_flush(int fd)
364 {
365         while (acpi_aml_readable(fd)) {
366                 acpi_aml_batch_drain = true;
367                 acpi_aml_loop(fd);
368                 acpi_aml_batch_drain = false;
369         }
370 }
371
372 void usage(FILE *file, char *progname)
373 {
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");
380 }
381
382 int main(int argc, char **argv)
383 {
384         int fd = -1;
385         int ch;
386         int len;
387         int ret = EXIT_SUCCESS;
388
389         while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
390                 switch (ch) {
391                 case 'b':
392                         if (acpi_aml_batch_cmd) {
393                                 fprintf(stderr, "Already specify %s\n",
394                                         acpi_aml_batch_cmd);
395                                 ret = EXIT_FAILURE;
396                                 goto exit;
397                         }
398                         len = strlen(optarg);
399                         acpi_aml_batch_cmd = calloc(len + 2, 1);
400                         if (!acpi_aml_batch_cmd) {
401                                 perror("calloc");
402                                 ret = EXIT_FAILURE;
403                                 goto exit;
404                         }
405                         memcpy(acpi_aml_batch_cmd, optarg, len);
406                         acpi_aml_batch_cmd[len] = '\n';
407                         acpi_aml_mode = ACPI_AML_BATCH;
408                         break;
409                 case 'f':
410                         acpi_aml_file_path = optarg;
411                         break;
412                 case 'h':
413                         usage(stdout, argv[0]);
414                         goto exit;
415                         break;
416                 case '?':
417                 default:
418                         usage(stderr, argv[0]);
419                         ret = EXIT_FAILURE;
420                         goto exit;
421                         break;
422                 }
423         }
424
425         fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
426         if (fd < 0) {
427                 perror("open");
428                 ret = EXIT_FAILURE;
429                 goto exit;
430         }
431         acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
432         acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
433
434         if (acpi_aml_mode == ACPI_AML_BATCH)
435                 acpi_aml_flush(fd);
436         acpi_aml_loop(fd);
437
438 exit:
439         if (fd >= 0)
440                 close(fd);
441         if (acpi_aml_batch_cmd)
442                 free(acpi_aml_batch_cmd);
443         return ret;
444 }