linux/tools/power/acpi/tools/acpidbg/acpidbg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ACPI AML interfacing userspace utility
   4 *
   5 * Copyright (C) 2015, Intel Corporation
   6 * Authors: Lv Zheng <lv.zheng@intel.com>
   7 */
   8
   9#include <acpi/acpi.h>
  10
  11/* Headers not included by include/acpi/platform/aclinux.h */
  12#include <unistd.h>
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <string.h>
  16#include <error.h>
  17#include <stdbool.h>
  18#include <fcntl.h>
  19#include <assert.h>
  20#include <sys/select.h>
  21#include "../../../../../include/linux/circ_buf.h"
  22
  23#define ACPI_AML_FILE           "/sys/kernel/debug/acpi/acpidbg"
  24#define ACPI_AML_SEC_TICK       1
  25#define ACPI_AML_USEC_PEEK      200
  26#define ACPI_AML_BUF_SIZE       4096
  27
  28#define ACPI_AML_BATCH_WRITE_CMD        0x00 /* Write command to kernel */
  29#define ACPI_AML_BATCH_READ_LOG         0x01 /* Read log from kernel */
  30#define ACPI_AML_BATCH_WRITE_LOG        0x02 /* Write log to console */
  31
  32#define ACPI_AML_LOG_START              0x00
  33#define ACPI_AML_PROMPT_START           0x01
  34#define ACPI_AML_PROMPT_STOP            0x02
  35#define ACPI_AML_LOG_STOP               0x03
  36#define ACPI_AML_PROMPT_ROLL            0x04
  37
  38#define ACPI_AML_INTERACTIVE    0x00
  39#define ACPI_AML_BATCH          0x01
  40
  41#define circ_count(circ) \
  42        (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
  43#define circ_count_to_end(circ) \
  44        (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
  45#define circ_space(circ) \
  46        (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
  47#define circ_space_to_end(circ) \
  48        (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
  49
  50#define acpi_aml_cmd_count()    circ_count(&acpi_aml_cmd_crc)
  51#define acpi_aml_log_count()    circ_count(&acpi_aml_log_crc)
  52#define acpi_aml_cmd_space()    circ_space(&acpi_aml_cmd_crc)
  53#define acpi_aml_log_space()    circ_space(&acpi_aml_log_crc)
  54
  55#define ACPI_AML_DO(_fd, _op, _buf, _ret)                               \
  56        do {                                                            \
  57                _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc);     \
  58                if (_ret == 0) {                                        \
  59                        fprintf(stderr,                                 \
  60                                "%s %s pipe closed.\n", #_buf, #_op);   \
  61                        return;                                         \
  62                }                                                       \
  63        } while (0)
  64#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret)                         \
  65        do {                                                            \
  66                _ret = acpi_aml_##_op##_batch_##_buf(_fd,               \
  67                         &acpi_aml_##_buf##_crc);                       \
  68                if (_ret == 0)                                          \
  69                        return;                                         \
  70        } while (0)
  71
  72
  73static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
  74static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
  75static struct circ_buf acpi_aml_cmd_crc = {
  76        .buf = acpi_aml_cmd_buf,
  77        .head = 0,
  78        .tail = 0,
  79};
  80static struct circ_buf acpi_aml_log_crc = {
  81        .buf = acpi_aml_log_buf,
  82        .head = 0,
  83        .tail = 0,
  84};
  85static const char *acpi_aml_file_path = ACPI_AML_FILE;
  86static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
  87static bool acpi_aml_exit;
  88
  89static bool acpi_aml_batch_drain;
  90static unsigned long acpi_aml_batch_state;
  91static char acpi_aml_batch_prompt;
  92static char acpi_aml_batch_roll;
  93static unsigned long acpi_aml_log_state;
  94static char *acpi_aml_batch_cmd = NULL;
  95static char *acpi_aml_batch_pos = NULL;
  96
  97static int acpi_aml_set_fl(int fd, int flags)
  98{
  99        int ret;
 100
 101        ret = fcntl(fd, F_GETFL, 0);
 102        if (ret < 0) {
 103                perror("fcntl(F_GETFL)");
 104                return ret;
 105        }
 106        flags |= ret;
 107        ret = fcntl(fd, F_SETFL, flags);
 108        if (ret < 0) {
 109                perror("fcntl(F_SETFL)");
 110                return ret;
 111        }
 112        return ret;
 113}
 114
 115static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
 116{
 117        if (fd > maxfd)
 118                maxfd = fd;
 119        FD_SET(fd, set);
 120        return maxfd;
 121}
 122
 123static int acpi_aml_read(int fd, struct circ_buf *crc)
 124{
 125        char *p;
 126        int len;
 127
 128        p = &crc->buf[crc->head];
 129        len = circ_space_to_end(crc);
 130        len = read(fd, p, len);
 131        if (len < 0)
 132                perror("read");
 133        else if (len > 0)
 134                crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
 135        return len;
 136}
 137
 138static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
 139{
 140        char *p;
 141        int len;
 142        int remained = strlen(acpi_aml_batch_pos);
 143
 144        p = &crc->buf[crc->head];
 145        len = circ_space_to_end(crc);
 146        if (len > remained) {
 147                memcpy(p, acpi_aml_batch_pos, remained);
 148                acpi_aml_batch_pos += remained;
 149                len = remained;
 150        } else {
 151                memcpy(p, acpi_aml_batch_pos, len);
 152                acpi_aml_batch_pos += len;
 153        }
 154        if (len > 0)
 155                crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
 156        return len;
 157}
 158
 159static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
 160{
 161        char *p;
 162        int len;
 163        int ret = 0;
 164
 165        p = &crc->buf[crc->head];
 166        len = circ_space_to_end(crc);
 167        while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
 168                if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
 169                        *p = acpi_aml_batch_roll;
 170                        len = 1;
 171                        crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
 172                        ret += 1;
 173                        acpi_aml_log_state = ACPI_AML_LOG_START;
 174                } else {
 175                        len = read(fd, p, 1);
 176                        if (len <= 0) {
 177                                if (len < 0)
 178                                        perror("read");
 179                                ret = len;
 180                                break;
 181                        }
 182                }
 183                switch (acpi_aml_log_state) {
 184                case ACPI_AML_LOG_START:
 185                        if (*p == '\n')
 186                                acpi_aml_log_state = ACPI_AML_PROMPT_START;
 187                        crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
 188                        ret += 1;
 189                        break;
 190                case ACPI_AML_PROMPT_START:
 191                        if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
 192                            *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
 193                                acpi_aml_batch_prompt = *p;
 194                                acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
 195                        } else {
 196                                if (*p != '\n')
 197                                        acpi_aml_log_state = ACPI_AML_LOG_START;
 198                                crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
 199                                ret += 1;
 200                        }
 201                        break;
 202                case ACPI_AML_PROMPT_STOP:
 203                        if (*p == ' ') {
 204                                acpi_aml_log_state = ACPI_AML_LOG_STOP;
 205                                acpi_aml_exit = true;
 206                        } else {
 207                                /* Roll back */
 208                                acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
 209                                acpi_aml_batch_roll = *p;
 210                                *p = acpi_aml_batch_prompt;
 211                                crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
 212                                ret += 1;
 213                        }
 214                        break;
 215                default:
 216                        assert(0);
 217                        break;
 218                }
 219        }
 220        return ret;
 221}
 222
 223static int acpi_aml_write(int fd, struct circ_buf *crc)
 224{
 225        char *p;
 226        int len;
 227
 228        p = &crc->buf[crc->tail];
 229        len = circ_count_to_end(crc);
 230        len = write(fd, p, len);
 231        if (len < 0)
 232                perror("write");
 233        else if (len > 0)
 234                crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
 235        return len;
 236}
 237
 238static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
 239{
 240        char *p;
 241        int len;
 242
 243        p = &crc->buf[crc->tail];
 244        len = circ_count_to_end(crc);
 245        if (!acpi_aml_batch_drain) {
 246                len = write(fd, p, len);
 247                if (len < 0)
 248                        perror("write");
 249        }
 250        if (len > 0)
 251                crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
 252        return len;
 253}
 254
 255static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
 256{
 257        int len;
 258
 259        len = acpi_aml_write(fd, crc);
 260        if (circ_count_to_end(crc) == 0)
 261                acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
 262        return len;
 263}
 264
 265static void acpi_aml_loop(int fd)
 266{
 267        fd_set rfds;
 268        fd_set wfds;
 269        struct timeval tv;
 270        int ret;
 271        int maxfd = 0;
 272
 273        if (acpi_aml_mode == ACPI_AML_BATCH) {
 274                acpi_aml_log_state = ACPI_AML_LOG_START;
 275                acpi_aml_batch_pos = acpi_aml_batch_cmd;
 276                if (acpi_aml_batch_drain)
 277                        acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
 278                else
 279                        acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
 280        }
 281        acpi_aml_exit = false;
 282        while (!acpi_aml_exit) {
 283                tv.tv_sec = ACPI_AML_SEC_TICK;
 284                tv.tv_usec = 0;
 285                FD_ZERO(&rfds);
 286                FD_ZERO(&wfds);
 287
 288                if (acpi_aml_cmd_space()) {
 289                        if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
 290                                maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
 291                        else if (strlen(acpi_aml_batch_pos) &&
 292                                 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
 293                                ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
 294                }
 295                if (acpi_aml_cmd_count() &&
 296                    (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
 297                     acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
 298                        maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
 299                if (acpi_aml_log_space() &&
 300                    (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
 301                     acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
 302                        maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
 303                if (acpi_aml_log_count())
 304                        maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
 305
 306                ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
 307                if (ret < 0) {
 308                        perror("select");
 309                        break;
 310                }
 311                if (ret > 0) {
 312                        if (FD_ISSET(STDIN_FILENO, &rfds))
 313                                ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
 314                        if (FD_ISSET(fd, &wfds)) {
 315                                if (acpi_aml_mode == ACPI_AML_BATCH)
 316                                        ACPI_AML_BATCH_DO(fd, write, cmd, ret);
 317                                else
 318                                        ACPI_AML_DO(fd, write, cmd, ret);
 319                        }
 320                        if (FD_ISSET(fd, &rfds)) {
 321                                if (acpi_aml_mode == ACPI_AML_BATCH)
 322                                        ACPI_AML_BATCH_DO(fd, read, log, ret);
 323                                else
 324                                        ACPI_AML_DO(fd, read, log, ret);
 325                        }
 326                        if (FD_ISSET(STDOUT_FILENO, &wfds)) {
 327                                if (acpi_aml_mode == ACPI_AML_BATCH)
 328                                        ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
 329                                else
 330                                        ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
 331                        }
 332                }
 333        }
 334}
 335
 336static bool acpi_aml_readable(int fd)
 337{
 338        fd_set rfds;
 339        struct timeval tv;
 340        int ret;
 341        int maxfd = 0;
 342
 343        tv.tv_sec = 0;
 344        tv.tv_usec = ACPI_AML_USEC_PEEK;
 345        FD_ZERO(&rfds);
 346        maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
 347        ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
 348        if (ret < 0)
 349                perror("select");
 350        if (ret > 0 && FD_ISSET(fd, &rfds))
 351                return true;
 352        return false;
 353}
 354
 355/*
 356 * This is a userspace IO flush implementation, replying on the prompt
 357 * characters and can be turned into a flush() call after kernel implements
 358 * .flush() filesystem operation.
 359 */
 360static void acpi_aml_flush(int fd)
 361{
 362        while (acpi_aml_readable(fd)) {
 363                acpi_aml_batch_drain = true;
 364                acpi_aml_loop(fd);
 365                acpi_aml_batch_drain = false;
 366        }
 367}
 368
 369void usage(FILE *file, char *progname)
 370{
 371        fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
 372        fprintf(file, "\nOptions:\n");
 373        fprintf(file, "  -b     Specify command to be executed in batch mode\n");
 374        fprintf(file, "  -f     Specify interface file other than");
 375        fprintf(file, "         /sys/kernel/debug/acpi/acpidbg\n");
 376        fprintf(file, "  -h     Print this help message\n");
 377}
 378
 379int main(int argc, char **argv)
 380{
 381        int fd = -1;
 382        int ch;
 383        int len;
 384        int ret = EXIT_SUCCESS;
 385
 386        while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
 387                switch (ch) {
 388                case 'b':
 389                        if (acpi_aml_batch_cmd) {
 390                                fprintf(stderr, "Already specify %s\n",
 391                                        acpi_aml_batch_cmd);
 392                                ret = EXIT_FAILURE;
 393                                goto exit;
 394                        }
 395                        len = strlen(optarg);
 396                        acpi_aml_batch_cmd = calloc(len + 2, 1);
 397                        if (!acpi_aml_batch_cmd) {
 398                                perror("calloc");
 399                                ret = EXIT_FAILURE;
 400                                goto exit;
 401                        }
 402                        memcpy(acpi_aml_batch_cmd, optarg, len);
 403                        acpi_aml_batch_cmd[len] = '\n';
 404                        acpi_aml_mode = ACPI_AML_BATCH;
 405                        break;
 406                case 'f':
 407                        acpi_aml_file_path = optarg;
 408                        break;
 409                case 'h':
 410                        usage(stdout, argv[0]);
 411                        goto exit;
 412                        break;
 413                case '?':
 414                default:
 415                        usage(stderr, argv[0]);
 416                        ret = EXIT_FAILURE;
 417                        goto exit;
 418                        break;
 419                }
 420        }
 421
 422        fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
 423        if (fd < 0) {
 424                perror("open");
 425                ret = EXIT_FAILURE;
 426                goto exit;
 427        }
 428        acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
 429        acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
 430
 431        if (acpi_aml_mode == ACPI_AML_BATCH)
 432                acpi_aml_flush(fd);
 433        acpi_aml_loop(fd);
 434
 435exit:
 436        if (fd >= 0)
 437                close(fd);
 438        if (acpi_aml_batch_cmd)
 439                free(acpi_aml_batch_cmd);
 440        return ret;
 441}
 442