]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - tools/power/acpi/tools/acpidbg/acpidbg.c
UBUNTU: Ubuntu-4.13.0-45.50
[mirror_ubuntu-artful-kernel.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 }