#include <sys/param.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <uuid.h>
#include <contrib/dev/acpica/source/include/acpi.h>
#include <dev/acpica/acpiio_mcall.h>
#define MAX_ACPI_PATH 4096
static char dev_path[MAXPATHLEN] = "/dev/acpi";
static char method_path[MAX_ACPI_PATH] = "";
static size_t result_buf_size = 1024;
static char output_format = 'o';
static int verbose;
static ACPI_OBJECT args[ACPI_METHOD_NUM_ARGS];
static struct acpi_mcall_ioctl_arg params;
static void usage(void);
static int parse_buffer(ACPI_OBJECT *, char *);
static void print_params(struct acpi_mcall_ioctl_arg *);
static void print_acpi_object(ACPI_OBJECT *);
static void print_acpi_buffer(ACPI_BUFFER *, char);
int
main(int argc, char *argv[])
{
char c;
int i, fd, status;
uuid_t uuid;
bzero(¶ms, sizeof(params));
params.path = method_path;
params.args.Count = 0;
params.args.Pointer = args;
while ((c = getopt(argc, argv, "b:d:i:o:s:U:v")) != -1) {
switch (c) {
case 'b':
case 'i':
case 's':
case 'U':
i = params.args.Count;
if (i >= ACPI_METHOD_NUM_ARGS) {
fprintf(stderr,
"maximum number of %d args exceeded\n",
ACPI_METHOD_NUM_ARGS);
exit(1);
}
switch (optopt) {
case 'b':
if (parse_buffer(&args[i], optarg) != 0) {
fprintf(stderr,
"unable to parse hexstring: %s\n",
optarg);
exit(1);
}
break;
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 'U':
uuid_from_string(optarg, &uuid, &status);
if (status != uuid_s_ok) {
fprintf(stderr, "invalid uuid %s\n",
optarg);
exit(1);
}
args[i].Type = ACPI_TYPE_BUFFER;
args[i].Buffer.Length = 16;
if ((args[i].Buffer.Pointer = malloc(16)) == NULL) {
fprintf(stderr, "malloc failure\n");
exit(1);
}
uuid_enc_le(args[i].Buffer.Pointer, &uuid);
break;
}
params.args.Count++;
break;
case 'd':
strlcpy(dev_path, optarg, MAXPATHLEN);
break;
case 'o':
switch (optarg[0]) {
case 'b':
case 'i':
case 'o':
case 's':
output_format = optarg[0];
break;
default:
fprintf(stderr,
"incorrect output format: %c\n",
optarg[0]);
usage();
break;
}
break;
case 'v':
verbose = 1;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
strlcpy(method_path, argv[0], MAX_ACPI_PATH);
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(¶ms);
fd = open(dev_path, O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
if (ioctl(fd, ACPIIO_DO_MCALL, ¶ms) == -1) {
perror("ioctl");
return 1;
}
if (verbose)
printf("status: %d\nresult: ", params.retval);
print_acpi_buffer(¶ms.result, output_format);
printf("\n");
return params.retval;
}
static void
usage(void)
{
fprintf(stderr,
"usage: acpicall [-v] [-b hexstring] [-d file] [-i number] "
"[-o i | s | b | o]\n");
fprintf(stderr, " [-s string] [-U uuid] path\n");
exit(1);
}
static 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 = malloc(len)) == NULL) {
fprintf(stderr,
"%s: failed to allocate %zu bytes\n", __func__, 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;
}
static void
print_params(struct acpi_mcall_ioctl_arg *p)
{
int i;
printf("path: %s\n", p->path);
printf("number of arguments: %d\n", p->args.Count);
for (i = 0; i < (int)p->args.Count; i++) {
printf("argument %d type: ", i + 1);
switch (p->args.Pointer[i].Type) {
case ACPI_TYPE_INTEGER:
printf("integer\n");
break;
case ACPI_TYPE_STRING:
printf("string\n");
break;
case ACPI_TYPE_BUFFER:
printf("buffer\n");
break;
}
printf("argument %d value: ", i + 1);
print_acpi_object(&(p->args.Pointer[i]));
printf("\n");
}
}
static void
print_acpi_object(ACPI_OBJECT *obj)
{
int i;
switch (obj->Type) {
case ACPI_TYPE_INTEGER:
printf("%ju", (uintmax_t)obj->Integer.Value);
break;
case ACPI_TYPE_STRING:
printf("%s", obj->String.Pointer);
break;
case ACPI_TYPE_BUFFER:
for (i = 0; i < (int)obj->Buffer.Length; i++)
printf("%02x", obj->Buffer.Pointer[i]);
break;
default:
printf("unknown object type '%d'", obj->Type);
break;
}
}
static void
print_acpi_buffer(ACPI_BUFFER *buf, char format)
{
int i;
switch (format) {
case 'i':
printf("%ju", (uintmax_t)*((UINT64 *)buf->Pointer));
break;
case 's':
printf("%s", (char *)buf->Pointer);
break;
case 'b':
for (i = 0; i < (int)buf->Length; i++)
printf("%02x", ((UINT8 *)(buf->Pointer))[i]);
break;
case 'o':
print_acpi_object((ACPI_OBJECT *)buf->Pointer);
break;
}
}