root/src/bin/acpi_call/acpi_call.cpp
/*-
 *   Copyright (C) 2011 by Maxim Ignatenko
 *   gelraen.ua@gmail.com
 *
 *   All rights reserved.                                                  *
 *                                                                         *
 *   Redistribution and use in source and binary forms, with or without    *
 *    modification, are permitted provided that the following conditions   *
 *    are met:                                                             *
 *     * Redistributions of source code must retain the above copyright    *
 *       notice, this list of conditions and the following disclaimer.     *
 *     * Redistributions in binary form must reproduce the above copyright *
 *       notice, this list of conditions and the following disclaimer in   *
 *       the documentation and/or other materials provided with the        *
 *       distribution.                                                     *
 *                                                                         *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   *
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     *
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR *
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  *
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      *
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY *
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   *
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  *
 *
 */

//#include "acpi_call_io.h"

#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/param.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "acpi.h"


struct acpi_call_descr
{
        char*           path;
        ACPI_OBJECT_LIST        args;
        ACPI_STATUS     retval;
        ACPI_BUFFER     result;
        ACPI_SIZE       reslen;
};


#define MAX_ACPI_PATH   1024 // XXX
#define MAX_ACPI_ARGS   7

char dev_path[MAXPATHLEN] = "/dev/acpi/call";
char method_path[MAX_ACPI_PATH] = "";
size_t result_buf_size = 1024;
char output_format = 'o';

int verbose;

ACPI_OBJECT args[MAX_ACPI_ARGS];
struct acpi_call_descr params;

void parse_opts(int, char *[]);
void show_help(FILE*);
int parse_buffer(ACPI_OBJECT*, char*);
void print_params(struct acpi_call_descr*);
void print_acpi_object(ACPI_OBJECT*);
void print_acpi_buffer(ACPI_BUFFER*, char);

int main(int argc, char * argv[])
{
        int fd;

        bzero(&params, sizeof(params));
        params.path = method_path;
        params.args.Count = 0;
        params.args.Pointer = args;

        verbose = 0;

        parse_opts(argc, argv);

        params.result.Length = result_buf_size;
        params.result.Pointer = malloc(result_buf_size);

        if (params.result.Pointer == NULL)
        {
                perror("malloc");
                return 1;
        }

        if (method_path[0] == 0)
        {
                fprintf(stderr, "Please specify path to method with -p flag\n");
                return 1;
        }

        if (verbose)
                print_params(&params);

        fd = open(dev_path, O_RDWR);
        if (fd < 0)
        {
                perror("open");
                return 1;
        }
        if (ioctl(fd, 'ACCA', &params) == -1)
        {
                perror("ioctl");
                return 1;
        }

        if (verbose)
                printf("Status: %d\nResult: ", params.retval);
        print_acpi_buffer(&params.result, output_format);
        printf("\n");

        return params.retval;
}

void parse_opts(int argc, char * argv[])
{
        char c;
        int i;

        while ((c = getopt(argc, argv, "hvd:p:i:s:b:o:")) != -1)
        {
                switch(c)
                {
                case 'h':
                        show_help(stdout);
                        exit(0);
                        break;
                case 'v':
                        verbose = 1;
                        break;
                case 'd':
                        strlcpy(dev_path, optarg, MAXPATHLEN);
                        break;
                case 'p':
                        strlcpy(method_path, optarg, MAX_ACPI_PATH);
                        break;
                case 'i':
                case 's':
                case 'b':
                        i = params.args.Count;
                        if (i >= MAX_ACPI_ARGS)
                        {
                                fprintf(stderr, "Maximum number of arguments exceeded\n");
                                exit(1);
                        }
                        switch (optopt)
                        {
                        case 'i':
                                args[i].Type = ACPI_TYPE_INTEGER;
                                args[i].Integer.Value = strtol(optarg, NULL, 10);
                                break;
                        case 's':
                                args[i].Type = ACPI_TYPE_STRING;
                                args[i].String.Length = strlen(optarg);
                                args[i].String.Pointer = optarg;
                                break;
                        case 'b':
                                if (parse_buffer(&args[i], optarg))
                                {
                                        fprintf(stderr, "Unable to parse hexstring to buffer: %s\n", optarg);
                                        exit(1);
                                }
                                break;
                        }
                        params.args.Count++;
                        break;
                case 'o':
                        output_format = optarg[0];
                        switch (optarg[0])
                        {
                        case 'i':
                        case 's':
                        case 'b':
                        case 'o':
                                break;
                        default:
                                fprintf(stderr, "Incorrect output format: %c\n", optarg[0]);
                                show_help(stderr);
                                exit(1);
                        }
                        break;
                default:
                        show_help(stderr);
                        exit(1);
                }
        }
}

void show_help(FILE* f)
{
        fprintf(f, "Options:\n");
        fprintf(f, "  -h              - print this help\n");
        fprintf(f, "  -v              - be verbose\n");
        fprintf(f, "  -d filename     - specify path to ACPI control pseudo-device. Default: /dev/acpi/call\n");
        fprintf(f, "  -p path         - full path to ACPI method\n");
        fprintf(f, "  -i number       - add integer argument\n");
        fprintf(f, "  -s string       - add string argument\n");
        fprintf(f, "  -b hexstring    - add buffer argument\n");
        fprintf(f, "  -o i|s|b|o      - print result as integer|string|hexstring|object\n");
}

int parse_buffer(ACPI_OBJECT *dst, char *src)
{
        char tmp[3] = {0};
        size_t len = strlen(src)/2, i;

        dst->Type = ACPI_TYPE_BUFFER;
        dst->Buffer.Length = len;
        if ((dst->Buffer.Pointer = (UINT8*)malloc(len)) == NULL)
        {
                fprintf(stderr, "parse_buffer: Failed to allocate %" B_PRIuSIZE " bytes\n", len);
                exit(1);
        }

        for(i = 0; i < len; i++)
        {
                tmp[0] = src[i*2];
                tmp[1] = src[i*2+1];
                dst->Buffer.Pointer[i] = strtol(tmp, NULL, 16);
        }

        return 0;
}

void print_params(struct acpi_call_descr* p)
{
        printf("Path: %s\n", p->path);
        printf("Number of arguments: %d\n", p->args.Count);
        for(uint32 i = 0; i < p->args.Count; i++)
        {
                switch (p->args.Pointer[i].Type)
                {
                case ACPI_TYPE_INTEGER:
                        printf("Argument %d type: Integer\n", i+1);
                        break;
                case ACPI_TYPE_STRING:
                        printf("Argument %d type: String\n", i+1);
                        break;
                case ACPI_TYPE_BUFFER:
                        printf("Argument %d type: Buffer\n", i+1);
                        break;
                }
                printf("Argument %d value: ", i+1);
                print_acpi_object(&(p->args.Pointer[i]));
                printf("\n");
        }
}

void print_acpi_object(ACPI_OBJECT* obj)
{
        switch (obj->Type)
        {
        case ACPI_TYPE_INTEGER:
                printf("%" B_PRIu64, obj->Integer.Value);
                break;
        case ACPI_TYPE_STRING:
                printf("%s", obj->String.Pointer);
                break;
        case ACPI_TYPE_BUFFER:
                for(uint32 i = 0; i < obj->Buffer.Length; i++)
                {
                        printf("%02X", obj->Buffer.Pointer[i]);
                }
                break;
        default:
                printf("Unknown object type '%d'", obj->Type);
        }
}

void print_acpi_buffer(ACPI_BUFFER* buf, char format)
{
        switch (format)
        {
        case 'i':
                printf("%" B_PRIu64, *((ACPI_INTEGER*)(buf->Pointer)));
                break;
        case 's':
                printf("%s", (char*)buf->Pointer);
                break;
        case 'b':
                for(uint32 i = 0; i < buf->Length; i++)
                {
                        printf("%02X", ((UINT8*)(buf->Pointer))[i]);
                }
                break;
        case 'o':
                print_acpi_object((ACPI_OBJECT*)(buf->Pointer));
                break;
        }
}