linux/tools/power/acpi/tools/acpidbg/acpidbg.c
<<
>>
Prefs
   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
  76static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
  77static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
  78static struct circ_buf acpi_aml_cmd_crc = {
  79        .buf = acpi_aml_cmd_buf,
  80        .head = 0,
  81        .tail = 0,
  82};
  83static struct circ_buf acpi_aml_log_crc = {
  84        .buf = acpi_aml_log_buf,
  85        .head = 0,
  86        .tail = 0,
  87};
  88static const char *acpi_aml_file_path = ACPI_AML_FILE;
  89static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
  90static bool acpi_aml_exit;
  91
  92static bool acpi_aml_batch_drain;
  93static unsigned long acpi_aml_batch_state;
  94static char acpi_aml_batch_prompt;
  95static char acpi_aml_batch_roll;
  96static unsigned long acpi_aml_log_state;
  97static char *acpi_aml_batch_cmd = NULL;
  98static char *acpi_aml_batch_pos = NULL;
  99
 100static 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
 118static 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
 126static 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
 141static 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
 162static 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
 226static 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
 241static 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
 258static 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
 268static 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
 339static 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 */
 363static 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
 372void 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
 382int 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
 438exit:
 439        if (fd >= 0)
 440                close(fd);
 441        if (acpi_aml_batch_cmd)
 442                free(acpi_aml_batch_cmd);
 443        return ret;
 444}
 445