summaryrefslogtreecommitdiff
path: root/SOURCES/0001-add-acpi_call.patch
diff options
context:
space:
mode:
Diffstat (limited to 'SOURCES/0001-add-acpi_call.patch')
-rw-r--r--SOURCES/0001-add-acpi_call.patch506
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
+