linux-pikaos-template/patches/nobara/0001-add-acpi_call.patch
Ward from fusion-voyager-3 169be065dc 6.6.6
2023-12-12 14:10:53 +03:00

507 lines
16 KiB
Diff

From 3f14226e2e90dba5d72c106da29e1876eb7b88ff Mon Sep 17 00:00:00 2001
From: Denis <benato.denis96@gmail.com>
Date: Thu, 28 Sep 2023 03:40:53 +0200
Subject: [PATCH] add acpi_call
---
drivers/platform/x86/Kconfig | 5 +
drivers/platform/x86/Makefile | 4 +
drivers/platform/x86/acpi_call.c | 449 +++++++++++++++++++++++++++++++
3 files changed, 458 insertions(+)
create mode 100644 drivers/platform/x86/acpi_call.c
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 49c2c4cd8d00..fde791e51261 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -170,6 +170,11 @@ config ACER_WIRELESS
If you choose to compile this driver as a module the module will be
called acer-wireless.
+config ACPI_CALL
+ tristate "acpi_call module"
+ help
+ This embeds acpi_call module into the kernel
+
config ACER_WMI
tristate "Acer WMI Laptop Extras"
depends on BACKLIGHT_CLASS_DEVICE
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 52dfdf574ac2..1e434fcb8273 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -4,10 +4,14 @@
# x86 Platform-Specific Drivers
#
+# ACPI calls
+
# Windows Management Interface
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
+obj-$(CONFIG_ACPI_CALL) += acpi_call.o
+
# WMI drivers
obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
diff --git a/drivers/platform/x86/acpi_call.c b/drivers/platform/x86/acpi_call.c
new file mode 100644
index 000000000000..d7bc238e16da
--- /dev/null
+++ b/drivers/platform/x86/acpi_call.c
@@ -0,0 +1,449 @@
+/* Copyright (c) 2010: Michal Kottman */
+
+#define BUILDING_ACPICA
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+#include <asm/uaccess.h>
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
+#include <linux/acpi.h>
+#else
+#include <acpi/acpi.h>
+#endif
+
+MODULE_LICENSE("GPL");
+
+/* Uncomment the following line to enable debug messages */
+/*
+#define DEBUG
+*/
+
+#define BUFFER_SIZE 4096
+#define INPUT_BUFFER_SIZE (2 * BUFFER_SIZE)
+#define MAX_ACPI_ARGS 16
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+#define HAVE_PROC_CREATE
+#endif
+
+extern struct proc_dir_entry *acpi_root_dir;
+
+static char input_buffer[INPUT_BUFFER_SIZE];
+static char result_buffer[BUFFER_SIZE];
+static char not_called_message[11] = "not called";
+
+static u8 temporary_buffer[BUFFER_SIZE];
+
+static size_t get_avail_bytes(void) {
+ return BUFFER_SIZE - strlen(result_buffer);
+}
+static char *get_buffer_end(void) {
+ return result_buffer + strlen(result_buffer);
+}
+
+/** Appends the contents of an acpi_object to the result buffer
+@param result An acpi object holding result data
+@returns 0 if the result could fully be saved, a higher value otherwise
+*/
+static int acpi_result_to_string(union acpi_object *result) {
+ if (result->type == ACPI_TYPE_INTEGER) {
+ snprintf(get_buffer_end(), get_avail_bytes(),
+ "0x%x", (int)result->integer.value);
+ } else if (result->type == ACPI_TYPE_STRING) {
+ snprintf(get_buffer_end(), get_avail_bytes(),
+ "\"%*s\"", result->string.length, result->string.pointer);
+ } else if (result->type == ACPI_TYPE_BUFFER) {
+ int i;
+ // do not store more than data if it does not fit. The first element is
+ // just 4 chars, but there is also two bytes from the curly brackets
+ int show_values = min((size_t)result->buffer.length, get_avail_bytes() / 6);
+
+ snprintf(get_buffer_end(), get_avail_bytes(), "{");
+ for (i = 0; i < show_values; i++)
+ sprintf(get_buffer_end(),
+ i == 0 ? "0x%02x" : ", 0x%02x", result->buffer.pointer[i]);
+
+ if (result->buffer.length > show_values) {
+ // if data was truncated, show a trailing comma if there is space
+ snprintf(get_buffer_end(), get_avail_bytes(), ",");
+ return 1;
+ } else {
+ // in case show_values == 0, but the buffer is too small to hold
+ // more values (i.e. the buffer cannot have anything more than "{")
+ snprintf(get_buffer_end(), get_avail_bytes(), "}");
+ }
+ } else if (result->type == ACPI_TYPE_PACKAGE) {
+ int i;
+ snprintf(get_buffer_end(), get_avail_bytes(), "[");
+ for (i=0; i<result->package.count; i++) {
+ if (i > 0)
+ snprintf(get_buffer_end(), get_avail_bytes(), ", ");
+
+ // abort if there is no more space available
+ if (!get_avail_bytes() || acpi_result_to_string(&result->package.elements[i]))
+ return 1;
+ }
+ snprintf(get_buffer_end(), get_avail_bytes(), "]");
+ } else {
+ snprintf(get_buffer_end(), get_avail_bytes(),
+ "Object type 0x%x\n", result->type);
+ }
+
+ // return 0 if there are still bytes available, 1 otherwise
+ return !get_avail_bytes();
+}
+
+/**
+@param method The full name of ACPI method to call
+@param argc The number of parameters
+@param argv A pre-allocated array of arguments of type acpi_object
+*/
+static void do_acpi_call(const char * method, int argc, union acpi_object *argv)
+{
+ acpi_status status;
+ acpi_handle handle;
+ struct acpi_object_list arg;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+#ifdef DEBUG
+ printk(KERN_INFO "acpi_call: Calling %s\n", method);
+#endif
+
+ // get the handle of the method, must be a fully qualified path
+ status = acpi_get_handle(NULL, (acpi_string) method, &handle);
+
+ if (ACPI_FAILURE(status))
+ {
+ snprintf(result_buffer, BUFFER_SIZE, "Error: %s", acpi_format_exception(status));
+ printk(KERN_ERR "acpi_call: Cannot get handle: %s\n", result_buffer);
+ return;
+ }
+
+ // prepare parameters
+ arg.count = argc;
+ arg.pointer = argv;
+
+ // call the method
+ status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
+ if (ACPI_FAILURE(status))
+ {
+ snprintf(result_buffer, BUFFER_SIZE, "Error: %s", acpi_format_exception(status));
+ printk(KERN_ERR "acpi_call: Method call failed: %s\n", result_buffer);
+ return;
+ }
+
+ // reset the result buffer
+ *result_buffer = '\0';
+ acpi_result_to_string(buffer.pointer);
+ kfree(buffer.pointer);
+
+#ifdef DEBUG
+ printk(KERN_INFO "acpi_call: Call successful: %s\n", result_buffer);
+#endif
+}
+
+/** Decodes 2 hex characters to an u8 int
+*/
+u8 decodeHex(char *hex) {
+ char buf[3] = { hex[0], hex[1], 0};
+ return (u8) simple_strtoul(buf, NULL, 16);
+}
+
+/** Parses method name and arguments
+@param input Input string to be parsed. Modified in the process.
+@param nargs Set to number of arguments parsed (output)
+@param args
+*/
+static char *parse_acpi_args(char *input, int *nargs, union acpi_object **args)
+{
+ char *s = input;
+ int i;
+
+ *nargs = 0;
+ *args = NULL;
+
+ // the method name is separated from the arguments by a space
+ while (*s && *s != ' ')
+ s++;
+ // if no space is found, return 0 arguments
+ if (*s == 0)
+ return input;
+
+ *args = (union acpi_object *) kmalloc(MAX_ACPI_ARGS * sizeof(union acpi_object), GFP_KERNEL);
+ if (!*args) {
+ printk(KERN_ERR "acpi_call: unable to allocate buffer\n");
+ return NULL;
+ }
+
+ while (*s) {
+ if (*s == ' ') {
+ if (*nargs == 0)
+ *s = 0; // change first space to nul
+ ++ *nargs;
+ ++ s;
+ } else {
+ union acpi_object *arg = (*args) + (*nargs - 1);
+ if (*s == '"') {
+ // decode string
+ arg->type = ACPI_TYPE_STRING;
+ arg->string.pointer = ++s;
+ arg->string.length = 0;
+ while (*s && *s++ != '"')
+ arg->string.length ++;
+ // skip the last "
+ if (*s == '"')
+ ++s;
+ } else if (*s == 'b') {
+ // decode buffer - bXXXX
+ char *p = ++s;
+ int len = 0, i;
+ u8 *buf = NULL;
+
+ while (*p && *p!=' ')
+ p++;
+
+ len = p - s;
+ if (len % 2 == 1) {
+ printk(KERN_ERR "acpi_call: buffer arg%d is not multiple of 8 bits\n", *nargs);
+ --*nargs;
+ goto err;
+ }
+ len /= 2;
+
+ buf = (u8*) kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_ERR "acpi_call: unable to allocate buffer\n");
+ --*nargs;
+ goto err;
+ }
+ for (i=0; i<len; i++) {
+ buf[i] = decodeHex(s + i*2);
+ }
+ s = p;
+
+ arg->type = ACPI_TYPE_BUFFER;
+ arg->buffer.pointer = buf;
+ arg->buffer.length = len;
+ } else if (*s == '{') {
+ // decode buffer - { b1, b2 ...}
+ u8 *buf = temporary_buffer;
+ arg->type = ACPI_TYPE_BUFFER;
+ arg->buffer.pointer = buf;
+ arg->buffer.length = 0;
+ while (*s && *s++ != '}') {
+ if (buf >= temporary_buffer + sizeof(temporary_buffer)) {
+ printk(KERN_ERR "acpi_call: buffer arg%d is truncated because the buffer is full\n", *nargs);
+ // clear remaining arguments
+ while (*s && *s != '}')
+ ++s;
+ break;
+ }
+ else if (*s >= '0' && *s <= '9') {
+ // decode integer into buffer
+ arg->buffer.length ++;
+ if (s[0] == '0' && s[1] == 'x')
+ *buf++ = simple_strtol(s+2, 0, 16);
+ else
+ *buf++ = simple_strtol(s, 0, 10);
+ }
+ // skip until space or comma or '}'
+ while (*s && *s != ' ' && *s != ',' && *s != '}')
+ ++s;
+ }
+ // store the result in new allocated buffer
+ buf = (u8*) kmalloc(arg->buffer.length, GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_ERR "acpi_call: unable to allocate buffer\n");
+ --*nargs;
+ goto err;
+ }
+ memcpy(buf, temporary_buffer, arg->buffer.length);
+ arg->buffer.pointer = buf;
+ } else {
+ // decode integer, N or 0xN
+ arg->type = ACPI_TYPE_INTEGER;
+ if (s[0] == '0' && s[1] == 'x') {
+ arg->integer.value = simple_strtol(s+2, 0, 16);
+ } else {
+ arg->integer.value = simple_strtol(s, 0, 10);
+ }
+ while (*s && *s != ' ') {
+ ++s;
+ }
+ }
+ }
+ }
+
+ return input;
+
+err:
+ for (i=0; i<*nargs; i++)
+ if ((*args)[i].type == ACPI_TYPE_BUFFER && (*args)[i].buffer.pointer)
+ kfree((*args)[i].buffer.pointer);
+ kfree(*args);
+ return NULL;
+}
+
+/** procfs write callback. Called when writing into /proc/acpi/call.
+*/
+#ifdef HAVE_PROC_CREATE
+static ssize_t acpi_proc_write( struct file *filp, const char __user *buff,
+ size_t len, loff_t *data )
+#else
+static int acpi_proc_write( struct file *filp, const char __user *buff,
+ unsigned long len, void *data )
+#endif
+{
+ union acpi_object *args;
+ int nargs, i;
+ char *method;
+
+ memset(input_buffer, 0, INPUT_BUFFER_SIZE);
+ if (len > sizeof(input_buffer) - 1) {
+#ifdef HAVE_PROC_CREATE
+ printk(KERN_ERR "acpi_call: Input too long! (%zu)\n", len);
+#else
+ printk(KERN_ERR "acpi_call: Input too long! (%lu)\n", len);
+#endif
+ return -ENOSPC;
+ }
+
+ if (copy_from_user( input_buffer, buff, len )) {
+ return -EFAULT;
+ }
+ input_buffer[len] = '\0';
+ if (input_buffer[len-1] == '\n')
+ input_buffer[len-1] = '\0';
+
+ method = parse_acpi_args(input_buffer, &nargs, &args);
+ if (method) {
+ do_acpi_call(method, nargs, args);
+ if (args) {
+ for (i=0; i<nargs; i++)
+ if (args[i].type == ACPI_TYPE_BUFFER)
+ kfree(args[i].buffer.pointer);
+ }
+ }
+ if (args)
+ kfree(args);
+
+ return len;
+}
+
+/** procfs 'call' read callback. Called when reading the content of /proc/acpi/call.
+Returns the last call status:
+- "not called" when no call was previously issued
+- "failed" if the call failed
+- "ok" if the call succeeded
+*/
+#ifdef HAVE_PROC_CREATE
+static ssize_t acpi_proc_read( struct file *filp, char __user *buff,
+ size_t count, loff_t *off )
+{
+ ssize_t ret;
+ int len = strlen(result_buffer);
+
+ if(len == 0) {
+ ret = simple_read_from_buffer(buff, count, off, not_called_message, strlen(not_called_message) + 1);
+ } else if(len + 1 > count) {
+ // user buffer is too small
+ ret = 0;
+ } else if(*off == len + 1) {
+ // we're done
+ ret = 0;
+ result_buffer[0] = '\0';
+ } else {
+ // output the current result buffer
+ ret = simple_read_from_buffer(buff, count, off, result_buffer, len + 1);
+ *off = ret;
+ }
+
+ return ret;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
+static struct proc_ops proc_acpi_operations = {
+ .proc_read = acpi_proc_read,
+ .proc_write = acpi_proc_write,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ .proc_lseek = default_llseek,
+#endif
+};
+#else
+static struct file_operations proc_acpi_operations = {
+ .owner = THIS_MODULE,
+ .read = acpi_proc_read,
+ .write = acpi_proc_write,
+};
+#endif
+
+#else
+static int acpi_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ if (off > 0) {
+ *eof = 1;
+ return 0;
+ }
+
+ // output the current result buffer
+ len = strlen(result_buffer);
+ memcpy(page, result_buffer, len + 1);
+
+ // initialize the result buffer for later
+ strcpy(result_buffer, "not called");
+
+ return len;
+}
+#endif
+
+/** module initialization function */
+static int __init init_acpi_call(void)
+{
+#ifdef HAVE_PROC_CREATE
+ struct proc_dir_entry *acpi_entry = proc_create("call",
+ 0660,
+ acpi_root_dir,
+ &proc_acpi_operations);
+#else
+ struct proc_dir_entry *acpi_entry = create_proc_entry("call", 0660, acpi_root_dir);
+#endif
+
+ strcpy(result_buffer, "not called");
+
+ if (acpi_entry == NULL) {
+ printk(KERN_ERR "acpi_call: Couldn't create proc entry\n");
+ return -ENOMEM;
+ }
+
+#ifndef HAVE_PROC_CREATE
+ acpi_entry->write_proc = acpi_proc_write;
+ acpi_entry->read_proc = acpi_proc_read;
+#endif
+
+#ifdef DEBUG
+ printk(KERN_INFO "acpi_call: Module loaded successfully\n");
+#endif
+
+ return 0;
+}
+
+static void __exit unload_acpi_call(void)
+{
+ remove_proc_entry("call", acpi_root_dir);
+
+#ifdef DEBUG
+ printk(KERN_INFO "acpi_call: Module unloaded successfully\n");
+#endif
+}
+
+module_init(init_acpi_call);
+module_exit(unload_acpi_call);
\ No newline at end of file
--
2.42.0