Merge tag 'pm-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
[linux-2.6-block.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 <stdbool.h>
16 #include <fcntl.h>
17 #include <assert.h>
18 #include <linux/circ_buf.h>
19
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
24
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 */
28
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
34
35 #define ACPI_AML_INTERACTIVE    0x00
36 #define ACPI_AML_BATCH          0x01
37
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))
46
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)
51
52 #define ACPI_AML_DO(_fd, _op, _buf, _ret)                               \
53         do {                                                            \
54                 _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc);     \
55                 if (_ret == 0) {                                        \
56                         fprintf(stderr,                                 \
57                                 "%s %s pipe closed.\n", #_buf, #_op);   \
58                         return;                                         \
59                 }                                                       \
60         } while (0)
61 #define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret)                         \
62         do {                                                            \
63                 _ret = acpi_aml_##_op##_batch_##_buf(_fd,               \
64                          &acpi_aml_##_buf##_crc);                       \
65                 if (_ret == 0)                                          \
66                         return;                                         \
67         } while (0)
68
69
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,
74         .head = 0,
75         .tail = 0,
76 };
77 static struct circ_buf acpi_aml_log_crc = {
78         .buf = acpi_aml_log_buf,
79         .head = 0,
80         .tail = 0,
81 };
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;
85
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;
93
94 static int acpi_aml_set_fl(int fd, int flags)
95 {
96         int ret;
97
98         ret = fcntl(fd, F_GETFL, 0);
99         if (ret < 0) {
100                 perror("fcntl(F_GETFL)");
101                 return ret;
102         }
103         flags |= ret;
104         ret = fcntl(fd, F_SETFL, flags);
105         if (ret < 0) {
106                 perror("fcntl(F_SETFL)");
107                 return ret;
108         }
109         return ret;
110 }
111
112 static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
113 {
114         if (fd > maxfd)
115                 maxfd = fd;
116         FD_SET(fd, set);
117         return maxfd;
118 }
119
120 static int acpi_aml_read(int fd, struct circ_buf *crc)
121 {
122         char *p;
123         int len;
124
125         p = &crc->buf[crc->head];
126         len = circ_space_to_end(crc);
127         len = read(fd, p, len);
128         if (len < 0)
129                 perror("read");
130         else if (len > 0)
131                 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
132         return len;
133 }
134
135 static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
136 {
137         char *p;
138         int len;
139         int remained = strlen(acpi_aml_batch_pos);
140
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;
146                 len = remained;
147         } else {
148                 memcpy(p, acpi_aml_batch_pos, len);
149                 acpi_aml_batch_pos += len;
150         }
151         if (len > 0)
152                 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
153         return len;
154 }
155
156 static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
157 {
158         char *p;
159         int len;
160         int ret = 0;
161
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;
167                         len = 1;
168                         crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
169                         ret += 1;
170                         acpi_aml_log_state = ACPI_AML_LOG_START;
171                 } else {
172                         len = read(fd, p, 1);
173                         if (len <= 0) {
174                                 if (len < 0)
175                                         perror("read");
176                                 ret = len;
177                                 break;
178                         }
179                 }
180                 switch (acpi_aml_log_state) {
181                 case ACPI_AML_LOG_START:
182                         if (*p == '\n')
183                                 acpi_aml_log_state = ACPI_AML_PROMPT_START;
184                         crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
185                         ret += 1;
186                         break;
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;
192                         } else {
193                                 if (*p != '\n')
194                                         acpi_aml_log_state = ACPI_AML_LOG_START;
195                                 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
196                                 ret += 1;
197                         }
198                         break;
199                 case ACPI_AML_PROMPT_STOP:
200                         if (*p == ' ') {
201                                 acpi_aml_log_state = ACPI_AML_LOG_STOP;
202                                 acpi_aml_exit = true;
203                         } else {
204                                 /* Roll back */
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);
209                                 ret += 1;
210                         }
211                         break;
212                 default:
213                         assert(0);
214                         break;
215                 }
216         }
217         return ret;
218 }
219
220 static int acpi_aml_write(int fd, struct circ_buf *crc)
221 {
222         char *p;
223         int len;
224
225         p = &crc->buf[crc->tail];
226         len = circ_count_to_end(crc);
227         len = write(fd, p, len);
228         if (len < 0)
229                 perror("write");
230         else if (len > 0)
231                 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
232         return len;
233 }
234
235 static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
236 {
237         char *p;
238         int len;
239
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);
244                 if (len < 0)
245                         perror("write");
246         }
247         if (len > 0)
248                 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
249         return len;
250 }
251
252 static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
253 {
254         int len;
255
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;
259         return len;
260 }
261
262 static void acpi_aml_loop(int fd)
263 {
264         fd_set rfds;
265         fd_set wfds;
266         struct timeval tv;
267         int ret;
268         int maxfd = 0;
269
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;
275                 else
276                         acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
277         }
278         acpi_aml_exit = false;
279         while (!acpi_aml_exit) {
280                 tv.tv_sec = ACPI_AML_SEC_TICK;
281                 tv.tv_usec = 0;
282                 FD_ZERO(&rfds);
283                 FD_ZERO(&wfds);
284
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);
291                 }
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);
302
303                 ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
304                 if (ret < 0) {
305                         perror("select");
306                         break;
307                 }
308                 if (ret > 0) {
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);
314                                 else
315                                         ACPI_AML_DO(fd, write, cmd, ret);
316                         }
317                         if (FD_ISSET(fd, &rfds)) {
318                                 if (acpi_aml_mode == ACPI_AML_BATCH)
319                                         ACPI_AML_BATCH_DO(fd, read, log, ret);
320                                 else
321                                         ACPI_AML_DO(fd, read, log, ret);
322                         }
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);
326                                 else
327                                         ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
328                         }
329                 }
330         }
331 }
332
333 static bool acpi_aml_readable(int fd)
334 {
335         fd_set rfds;
336         struct timeval tv;
337         int ret;
338         int maxfd = 0;
339
340         tv.tv_sec = 0;
341         tv.tv_usec = ACPI_AML_USEC_PEEK;
342         FD_ZERO(&rfds);
343         maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
344         ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
345         if (ret < 0)
346                 perror("select");
347         if (ret > 0 && FD_ISSET(fd, &rfds))
348                 return true;
349         return false;
350 }
351
352 /*
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.
356  */
357 static void acpi_aml_flush(int fd)
358 {
359         while (acpi_aml_readable(fd)) {
360                 acpi_aml_batch_drain = true;
361                 acpi_aml_loop(fd);
362                 acpi_aml_batch_drain = false;
363         }
364 }
365
366 void usage(FILE *file, char *progname)
367 {
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");
374 }
375
376 int main(int argc, char **argv)
377 {
378         int fd = 0;
379         int ch;
380         int len;
381         int ret = EXIT_SUCCESS;
382
383         while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
384                 switch (ch) {
385                 case 'b':
386                         if (acpi_aml_batch_cmd) {
387                                 fprintf(stderr, "Already specify %s\n",
388                                         acpi_aml_batch_cmd);
389                                 ret = EXIT_FAILURE;
390                                 goto exit;
391                         }
392                         len = strlen(optarg);
393                         acpi_aml_batch_cmd = calloc(len + 2, 1);
394                         if (!acpi_aml_batch_cmd) {
395                                 perror("calloc");
396                                 ret = EXIT_FAILURE;
397                                 goto exit;
398                         }
399                         memcpy(acpi_aml_batch_cmd, optarg, len);
400                         acpi_aml_batch_cmd[len] = '\n';
401                         acpi_aml_mode = ACPI_AML_BATCH;
402                         break;
403                 case 'f':
404                         acpi_aml_file_path = optarg;
405                         break;
406                 case 'h':
407                         usage(stdout, argv[0]);
408                         goto exit;
409                         break;
410                 case '?':
411                 default:
412                         usage(stderr, argv[0]);
413                         ret = EXIT_FAILURE;
414                         goto exit;
415                         break;
416                 }
417         }
418
419         fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
420         if (fd < 0) {
421                 perror("open");
422                 ret = EXIT_FAILURE;
423                 goto exit;
424         }
425         acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
426         acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
427
428         if (acpi_aml_mode == ACPI_AML_BATCH)
429                 acpi_aml_flush(fd);
430         acpi_aml_loop(fd);
431
432 exit:
433         if (fd < 0)
434                 close(fd);
435         if (acpi_aml_batch_cmd)
436                 free(acpi_aml_batch_cmd);
437         return ret;
438 }