diff options
Diffstat (limited to 'SOURCES/0001-add-acpi_call.patch')
-rw-r--r-- | SOURCES/0001-add-acpi_call.patch | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/SOURCES/0001-add-acpi_call.patch b/SOURCES/0001-add-acpi_call.patch new file mode 100644 index 0000000..b0a185a --- /dev/null +++ b/SOURCES/0001-add-acpi_call.patch @@ -0,0 +1,506 @@ +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 + |