diff --git a/README b/README.md similarity index 67% rename from README rename to README.md index 6b91b0f..cf7541a 100644 --- a/README +++ b/README.md @@ -1,9 +1,14 @@ -A kernel module that enables you to call ACPI methods by writing the method -name followed by arguments to /proc/acpi/call. +A kernel simple module that enables you to call ACPI methods by writing the +method name followed by arguments to `/proc/acpi/call`. + +This module is to be considered a proof-of-concept and has been superseeded by +projects like [bbswitch](https://github.com/Bumblebee-Project/bbswitch). It +allows you to tamper with your system and should be used with caution. Usage: echo '' | sudo tee /proc/acpi/call + You can then retrieve the result of the call by checking your dmesg or: sudo cat /proc/acpi/call @@ -11,28 +16,30 @@ You can then retrieve the result of the call by checking your dmesg or: An example to turn off discrete graphics card in a dual graphics environment (like NVIDIA Optimus): -# turn off discrete graphics card -echo '\_SB.PCI0.PEG1.GFX0.DOFF' > /proc/acpi/call -# turn it back on -echo '\_SB.PCI0.PEG1.GFX0.DON' > /proc/acpi/call + # turn off discrete graphics card + echo '\_SB.PCI0.PEG1.GFX0.DOFF' > /proc/acpi/call + # turn it back on + echo '\_SB.PCI0.PEG1.GFX0.DON' > /proc/acpi/call These work on my ASUS K52J notebook, but may not work for you. For a list of methods to try, see http://linux-hybrid-graphics.blogspot.com/ or try running -the provided script test_off.sh +the provided script `examples/turn_off_gpu.sh` It SHOULD be ok to test all of the methods, until you see a drop in battery -drain rate (grep rate /proc/acpi/battery/BAT0/state), however it comes -with NO WARRANTY. +drain rate (`grep rate /proc/acpi/battery/BAT0/state`), however it comes +with NO WARRANTY - it may hang your computer/laptop, fail to work, etc. -You can pass parameters to acpi_call by writing them after the method, +You can pass parameters to `acpi_call` by writing them after the method, separated by single space. Currently, you can pass the following parameter types: + * ACPI_INTEGER - by writing NNN or 0xNNN, where NNN is an integer/hex * ACPI_STRING - by enclosing the string in quotes: "hello, world" * ACPI_BUFFER - by writing bXXXX, where XXXX is a hex string without spaces, or by writing { b1, b2, b3, b4 }, where b1-4 are integers -The status after a call can be read back from /proc/acpi/call: +The status after a call can be read back from `/proc/acpi/call`: + * 'not called' - nothing to report * 'Error: ' - the call failed * '0xNN' - the call succeeded, and returned an integer diff --git a/acpi_call.c b/acpi_call.c index 0b4246f..0fc506d 100644 --- a/acpi_call.c +++ b/acpi_call.c @@ -9,7 +9,13 @@ MODULE_LICENSE("GPL"); +/* Uncomment the following line to enable debug messages */ +/* +#define DEBUG +*/ + #define BUFFER_SIZE 256 +#define MAX_ACPI_ARGS 16 extern struct proc_dir_entry *acpi_root_dir; @@ -23,6 +29,7 @@ static size_t get_avail_bytes(void) { 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 @@ -87,7 +94,9 @@ static void do_acpi_call(const char * method, int argc, union acpi_object *argv) 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); @@ -117,7 +126,9 @@ static void do_acpi_call(const char * method, int argc, union acpi_object *argv) 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 @@ -146,7 +157,7 @@ static char *parse_acpi_args(char *input, int *nargs, union acpi_object **args) if (*s == 0) return input; - *args = (union acpi_object *) kmalloc(16 * sizeof(union acpi_object), GFP_KERNEL); + *args = (union acpi_object *) kmalloc(MAX_ACPI_ARGS * sizeof(union acpi_object), GFP_KERNEL); while (*s) { if (*s == ' ') { @@ -290,8 +301,11 @@ static int acpi_proc_read(char *page, char **start, off_t off, 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; @@ -311,7 +325,10 @@ static int __init init_acpi_call(void) acpi_entry->write_proc = acpi_proc_write; acpi_entry->read_proc = acpi_proc_read; + +#ifdef DEBUG printk(KERN_INFO "acpi_call: Module loaded successfully\n"); +#endif return 0; } @@ -319,7 +336,10 @@ static int __init init_acpi_call(void) 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); diff --git a/asus1215n.sh b/examples/asus1215n.sh similarity index 100% rename from asus1215n.sh rename to examples/asus1215n.sh diff --git a/examples/dellL702X.sh b/examples/dellL702X.sh new file mode 100755 index 0000000..8f29cbf --- /dev/null +++ b/examples/dellL702X.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# Power control for Dell L702X +# by Jos Hickson +# adapted from "Power control for Asus 1215N Optimus by Pete Eberlein" + +if ! lsmod | grep -q acpi_call; then + echo "Error: acpi_call module not loaded" + exit +fi + +acpi_call () { + echo "$*" > /proc/acpi/call + cat /proc/acpi/call +} + + +case "$1" in +off) + echo _DSM $(acpi_call "\_SB.PCI0.PEG0.PEGP._DSM" \ + "{0xF8,0xD8,0x86,0xA4,0xDA,0x0B,0x1B,0x47," \ + "0xA7,0x2B,0x60,0x42,0xA6,0xB5,0xBE,0xE0}" \ + "0x100 0x1A {0x1,0x0,0x0,0x3}") + # ok to turn off: Buffer {0x59 0x0 0x0 0x11} + # is already off: Buffer {0x41 0x0 0x0 0x11} + echo _PS3 $(acpi_call "\_SB.PCI0.PEG0.PEGP._PS3") +;; +on) + echo _PS0 $(acpi_call "\_SB.PCI0.PEG0.PEGP._PS0") +;; +*) + echo "Usage: $0 [on|off]" +esac + + +PSC=$(acpi_call "\_SB.PCI0.PEG0.PEGP._PSC") +#echo _PSC ${PSC} +case "$PSC" in +0x0) + PSC="on" +;; +0x3) + PSC="off" +;; +esac +echo "Dell L702X Optimus appears to be ${PSC}" + diff --git a/m11xr2.sh b/examples/m11xr2.sh similarity index 100% rename from m11xr2.sh rename to examples/m11xr2.sh diff --git a/test_off.sh b/examples/turn_off_gpu.sh old mode 100644 new mode 100755 similarity index 69% rename from test_off.sh rename to examples/turn_off_gpu.sh index 4686d4b..14db28f --- a/test_off.sh +++ b/examples/turn_off_gpu.sh @@ -27,23 +27,26 @@ methods=" \_SB.PCI0.LPC.EC.PUBS._OFF \_SB.PCI0.P0P2.NVID._OFF \_SB.PCI0.P0P2.VGA.PX02 +\_SB_.PCI0.PEGP.DGFX._OFF +\_SB_.PCI0.VGA.PX02 " - for m in $methods; do -echo -n "Trying $m: " - echo $m > /proc/acpi/call - result=$(cat /proc/acpi/call) - case "$result" in +for m in $methods; do + echo -n "Trying $m: " + echo $m > /proc/acpi/call + result=$(cat /proc/acpi/call) + case "$result" in Error*) echo "failed" ;; *) echo "works!" - #break + # break # try out outher methods too ;; - esac + esac done + else -echo 'The acpi_call module is not loaded' + echo "The acpi_call module is not loaded, try running 'modprobe acpi_call' or 'insmod acpi_call.ko' as root" exit 1 fi diff --git a/query_dsdt.pl b/support/query_dsdt.pl similarity index 100% rename from query_dsdt.pl rename to support/query_dsdt.pl diff --git a/windump_hack/windump.c b/support/windump_hack/windump.c similarity index 100% rename from windump_hack/windump.c rename to support/windump_hack/windump.c diff --git a/windump_hack/xorg.conf b/support/windump_hack/xorg.conf similarity index 100% rename from windump_hack/xorg.conf rename to support/windump_hack/xorg.conf