From 00754ef62faa44a0e1f416f9d7c6dd99f3b9e28f Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sun, 30 Aug 2015 16:04:09 +0200
Subject: [PATCH] WIP acpicall

---
 sys/dev/acpica/acpi.c             |  46 +++++++
 sys/dev/acpica/acpiio.h           |  10 ++
 usr.sbin/acpi/Makefile            |   1 +
 usr.sbin/acpi/acpicall/Makefile   |   4 +
 usr.sbin/acpi/acpicall/acpicall.c | 281 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 342 insertions(+)
 create mode 100644 usr.sbin/acpi/acpicall/Makefile
 create mode 100644 usr.sbin/acpi/acpicall/acpicall.c

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index 5098ff2..82c5db9 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -293,6 +293,10 @@ SYSCTL_INT(_debug_acpi, OID_AUTO, do_powerstate, CTLFLAG_RW,
 /* Allow users to override quirks. */
 TUNABLE_INT("debug.acpi.quirks", &acpi_quirks);
 
+/* Allow to call ACPI methods from userland. */
+static int acpi_allow_mcall;
+TUNABLE_INT("debug.acpi.allow_method_calls", &acpi_allow_mcall);
+
 static int acpi_susp_bounce;
 SYSCTL_INT(_debug_acpi, OID_AUTO, suspend_bounce, CTLFLAG_RW,
     &acpi_susp_bounce, 0, "Don't actually suspend, just test devices.");
@@ -3185,6 +3189,48 @@ acpiioctl(struct dev_ioctl_args *ap)
 	    if (ACPI_SUCCESS(acpi_SetSleepState(sc, state)))
 		error = 0;
 	break;
+    case ACPIIO_DO_MCALL:
+	if (acpi_allow_mcall == 1) {
+	    struct acpi_mcall_ioctl_arg *params;
+	    ACPI_BUFFER result = { ACPI_ALLOCATE_BUFFER, NULL };
+	    ACPI_OBJECT *resobj;
+
+	    error = EINVAL;
+	    params = (struct acpi_mcall_ioctl_arg *)ap->a_data;
+	    params->retval = AcpiEvaluateObject(NULL, params->path,
+		&params->args, &result);
+	    if (ACPI_SUCCESS(params->retval) && result.Pointer != NULL &&
+		params->result.Pointer != NULL) {
+		params->result.Length = min(params->result.Length,
+		    result.Length);
+		copyout(result.Pointer, params->result.Pointer,
+		    params->result.Length);
+		params->reslen = result.Length;
+		if (result.Length >= sizeof(ACPI_OBJECT)) {
+		    resobj = (ACPI_OBJECT *)params->result.Pointer;
+		    switch (resobj->Type) {
+		    case ACPI_TYPE_STRING:
+			resobj->String.Pointer = (char *)
+			    ((UINT8 *)(resobj->String.Pointer) -
+				(UINT8 *)result.Pointer +
+				(UINT8 *)resobj);
+			break;
+		    case ACPI_TYPE_BUFFER:
+			resobj->Buffer.Pointer -= (UINT8 *)result.Pointer -
+			    (UINT8 *)resobj;
+			break;
+		    }
+		}
+		error = 0;
+	    }
+	    if (result.Pointer != NULL)
+		AcpiOsFree(result.Pointer);
+	} else {
+		device_printf(sc->acpi_dev,
+		    "debug.acpi.allow_method_calls must be set\n");
+		error = ENXIO;
+	}
+	break;
     default:
 	error = ENXIO;
 	break;
diff --git a/sys/dev/acpica/acpiio.h b/sys/dev/acpica/acpiio.h
index 7829438..1da0933 100644
--- a/sys/dev/acpica/acpiio.h
+++ b/sys/dev/acpica/acpiio.h
@@ -118,6 +118,16 @@ union acpi_battery_ioctl_arg {
 /* Get AC adapter status. */
 #define ACPIIO_ACAD_GET_STATUS	  _IOR('A', 1, int)
 
+struct acpi_mcall_ioctl_arg {
+	char		*path;
+	ACPI_OBJECT_LIST args;
+	ACPI_STATUS	 retval;
+	ACPI_BUFFER	 result;
+	ACPI_SIZE	 reslen; /* length returned by AcpiEvaluateObject */
+};
+
+#define ACPIIO_DO_MCALL		  _IOWR('X', 0x100, struct acpi_mcall_ioctl_arg)
+
 #ifdef _KERNEL
 typedef int	(*acpi_ioctl_fn)(u_long cmd, caddr_t addr, void *arg);
 extern int	acpi_register_ioctl(u_long cmd, acpi_ioctl_fn fn, void *arg);
diff --git a/usr.sbin/acpi/Makefile b/usr.sbin/acpi/Makefile
index deeb0fd..a512bff 100644
--- a/usr.sbin/acpi/Makefile
+++ b/usr.sbin/acpi/Makefile
@@ -1,5 +1,6 @@
 SUBDIR=	\
 	acpibin \
+	acpicall \
 	acpiconf \
 	acpidump \
 	acpiexec \
diff --git a/usr.sbin/acpi/acpicall/Makefile b/usr.sbin/acpi/acpicall/Makefile
new file mode 100644
index 0000000..d998644
--- /dev/null
+++ b/usr.sbin/acpi/acpicall/Makefile
@@ -0,0 +1,4 @@
+PROG=	acpicall
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/acpicall/acpicall.c b/usr.sbin/acpi/acpicall/acpicall.c
new file mode 100644
index 0000000..51c1408
--- /dev/null
+++ b/usr.sbin/acpi/acpicall/acpicall.c
@@ -0,0 +1,281 @@
+/*-
+ *   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 <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <contrib/dev/acpica/source/include/acpi.h>
+#include <dev/acpica/acpiio.h>
+
+#define	MAX_ACPI_PATH	1024 // XXX
+#define MAX_ACPI_ARGS	7
+
+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[MAX_ACPI_ARGS];
+static struct acpi_mcall_ioctl_arg params;
+
+static void parse_opts(int, char *[]);
+static void show_help(FILE*);
+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[])
+{
+	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, ACPIIO_DO_MCALL, &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;
+}
+
+static 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);
+		}
+	}
+}
+
+static 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\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");
+}
+
+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 = (UINT8 *)malloc(len)) == NULL) {
+		fprintf(stderr, "parse_buffer: Failed to allocate %zd 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;
+}
+
+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 < 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");
+	}
+}
+
+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 < obj->Buffer.Length; i++)
+			printf("%02X", obj->Buffer.Pointer[i]);
+		break;
+	default:
+		printf("Unknown object type '%d'", obj->Type);
+	}	
+}
+
+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 < buf->Length; i++)
+			printf("%02X", ((UINT8 *)(buf->Pointer))[i]);
+		break;
+	case 'o':
+		print_acpi_object((ACPI_OBJECT *)(buf->Pointer));
+		break;
+	}
+}
-- 
2.4.6

