From 8c0c5aa8631aaded829f40f8df86f901df2a721a Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 21 Jan 2012 12:30:41 +0100 Subject: [PATCH 01/16] Allow everyone to get the status of the card (Closes GH-8) --- bbswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbswitch.c b/bbswitch.c index 269ceed..6c57406 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -386,7 +386,7 @@ static int __init bbswitch_init(void) { } } - acpi_entry = proc_create("bbswitch", 0660, acpi_root_dir, &bbswitch_fops); + acpi_entry = proc_create("bbswitch", 0664, acpi_root_dir, &bbswitch_fops); if (acpi_entry == NULL) { printk(KERN_ERR "bbswitch: Couldn't create proc entry\n"); return -ENOMEM; From 4f73396ba78dc9012280ea7e800e76d59656f91b Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 21 Jan 2012 15:02:11 +0100 Subject: [PATCH 02/16] Add easier way to use DKMS --- Makefile.dkms | 24 ++++++++++++++++++++++++ README.md | 14 ++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 Makefile.dkms diff --git a/Makefile.dkms b/Makefile.dkms new file mode 100644 index 0000000..48ec687 --- /dev/null +++ b/Makefile.dkms @@ -0,0 +1,24 @@ +modname := bbswitch +DKMS := dkms +modver := $(shell awk -F'"' '/MODULE_VERSION/{print $$2}' < bbswitch.c) + +# directory in which generated files are stored +DKMS_DEST := /usr/src/$(modname)-$(modver) + +all: install + +src_install: + mkdir -p '$(DKMS_DEST)' + cp Makefile bbswitch.c '$(DKMS_DEST)' + sed 's/#MODULE_VERSION#/$(modver)/' dkms/dkms.conf > '$(DKMS_DEST)/dkms.conf' + +build: src_install + $(DKMS) build 'bbswitch/$(modver)' + +install: build + $(DKMS) install 'bbswitch/$(modver)' + +uninstall: + $(DKMS) remove bbswitch/$(modver) --all + +.PHONY: all src_install build install uninstall diff --git a/README.md b/README.md index 5376608..72e3d20 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,16 @@ information. DKMS support ------------ -Change `#MODULE_VERSION#` to the current version of bbswitch. Copy the -Makefile, C source and dkms.conf file to `/usr/src/bbswitch-VERSION/` (replace -VERSION with the current version of bbswitch which has been inserted for -`#MODULE_VERSION#`. +If you have DKMS installed, you can install bbswitch in such a way that it +survives kernel upgrades. It is recommended to remove older versions of bbswitch +by running `dkms remove bbswitch/OLDVERSION --all` as root. To install the new +version, simply run: + + # make -f Makefile.dkms + +To uninstall it, run: + + # make -f Makefile.dkms uninstall Usage ----- From 11ddd3469fc9b20ce8fffec4e4062d2d831496ae Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Mon, 23 Jan 2012 23:52:27 +0100 Subject: [PATCH 03/16] Add support for WMMX method This should support at least the Lenovo Ideapad Y470 and Y570. It probes the WMMX method before calling the _DSM method directly since that method should work for all machines that support the NVIDIA DSM. --- bbswitch.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index 6c57406..6dc6601 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -41,6 +41,8 @@ static const char acpi_optimus_dsm_muid[16] = { 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, }; +#define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" +#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA DSM */ static const char acpi_nvidia_dsm_muid[16] = { 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4 @@ -56,10 +58,22 @@ It looks like something for Intel GPU: http://lxr.linux.no/#linux+v3.1.5/drivers/gpu/drm/i915/intel_acpi.c */ -#define DSM_TYPE_UNSUPPORTED 0 -#define DSM_TYPE_OPTIMUS 1 -#define DSM_TYPE_NVIDIA 2 -static int dsm_type = DSM_TYPE_UNSUPPORTED; +struct mxdsm_args { + u32 func; + char muid[16]; + u32 revid; + u32 sfnc; + char args[4]; +}; + +enum dsm_type { + DSM_TYPE_UNSUPPORTED, + DSM_TYPE_OPTIMUS, + DSM_TYPE_NVIDIA, + /* _DSM call through a WMI MX method */ + DSM_TYPE_NVIDIA_WMI, +}; +static enum dsm_type dsm_type = DSM_TYPE_UNSUPPORTED; static struct pci_dev *dis_dev; static acpi_handle dis_handle; @@ -78,6 +92,8 @@ static char *buffer_to_string(const char buffer[], char *target) { return target; } +static int parse_dsm_result(struct acpi_buffer *output, uint32_t *result); + // Returns 0 if the call succeeded and non-zero otherwise. If the call // succeeded, the result is stored in "result" providing that the result is an // integer or a buffer containing 4 values @@ -86,7 +102,6 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_object_list input; union acpi_object params[4]; - union acpi_object *obj; int err; input.count = 4; @@ -118,8 +133,12 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, buffer_to_string(args, tmp), acpi_format_exception(err)); return err; } + return parse_dsm_result(&output, result); +} - obj = (union acpi_object *)output.pointer; +// Processes the result of an ACPI _DSM call and deallocates the result memory +static int parse_dsm_result(struct acpi_buffer *output, uint32_t *result) { + union acpi_object *obj = output->pointer; if (obj->type == ACPI_TYPE_INTEGER && result) { *result = obj->integer.value; @@ -136,7 +155,7 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, " type: %X\n", obj->type); } - kfree(output.pointer); + kfree(output->pointer); return 0; } @@ -153,6 +172,51 @@ static int has_dsm_func(const char muid[16], int revid, int sfnc) { return result & 1 && result & (1 << sfnc); } +// Returns 0 if the call succeeded and non-zero otherwise. If the call +// succeeded, the result is stored in "result" providing that the result is an +// integer or a buffer containing 4 values +static int wmmx_call(const char muid[16], int revid, int func, char args[4], + uint32_t *result) { + struct mxdsm_args mx_args = { + .func = MXM_WMMX_FUNC_DSM, + .revid = revid, + .sfnc = func, + }; + struct acpi_buffer input = { (acpi_size)sizeof(mx_args), &mx_args }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + + memcpy(mx_args.muid, muid, sizeof(mx_args.muid)); + if (args) { + memcpy(mx_args.args, args, sizeof(mx_args.args)); + } else { + // some implementations do not like empty values + memset(mx_args.args, 0, sizeof(mx_args.args)); + } + + // 1, > 1 <-- required by at least Acer Travelmate 8472TG, seen on others + status = wmi_evaluate_method(MXM_WMMX_GUID, 1, 1, &input, &output); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING "bbswitch: failed to evaluate WMMX: %s\n", + acpi_format_exception(status)); + return status; + } + return parse_dsm_result(&output, result); +} + +// Returns 1 if a _DSM function and its function index exists and 0 otherwise +static int has_wmi_func(const char muid[16], int revid, int sfnc) { + u32 result = 0; + + // fail if the _DSM call failed + if (wmmx_call(muid, revid, 0, 0, &result)) + return 0; + + // ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. If + // the n-th bit is enabled, function n is supported + return result & 1 && result & (1 << sfnc); +} + static int bbswitch_optimus_dsm(void) { if (dsm_type == DSM_TYPE_OPTIMUS) { char args[] = {1, 0, 0, 3}; @@ -170,10 +234,9 @@ static int bbswitch_optimus_dsm(void) { } static int bbswitch_acpi_off(void) { + char args[] = {2, 0, 0, 0}; + u32 result = 0; if (dsm_type == DSM_TYPE_NVIDIA) { - char args[] = {2, 0, 0, 0}; - u32 result = 0; - if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { // failure @@ -181,15 +244,21 @@ static int bbswitch_acpi_off(void) { } printk(KERN_DEBUG "bbswitch: Result of _DSM call for OFF: %08X\n", result); + } else if (dsm_type == DSM_TYPE_NVIDIA_WMI) { + if (wmmx_call(acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { + // failure + return 1; + } + printk(KERN_DEBUG "bbswitch: Result of WMMX call for OFF: %08X\n", + result); } return 0; } static int bbswitch_acpi_on(void) { + char args[] = {1, 0, 0, 0}; + u32 result = 0; if (dsm_type == DSM_TYPE_NVIDIA) { - char args[] = {1, 0, 0, 0}; - u32 result = 0; - if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { // failure @@ -197,6 +266,13 @@ static int bbswitch_acpi_on(void) { } printk(KERN_DEBUG "bbswitch: Result of _DSM call for ON: %08X\n", result); + } else if (dsm_type == DSM_TYPE_NVIDIA_WMI) { + if (wmmx_call(acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { + // failure + return 1; + } + printk(KERN_DEBUG "bbswitch: Result of WMMX call for ON: %08X\n", + result); } return 0; } @@ -369,6 +445,9 @@ static int __init bbswitch_init(void) { if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { dsm_type = DSM_TYPE_OPTIMUS; printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); + } else if (has_wmi_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { + dsm_type = DSM_TYPE_NVIDIA_WMI; + printk(KERN_INFO "bbswitch: detected a nVidia WMMX function\n"); } else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { dsm_type = DSM_TYPE_NVIDIA; printk(KERN_INFO "bbswitch: detected a nVidia _DSM function\n"); From 7fd02221b2a29c1e6f96643b4d3e807177f1b8ef Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Tue, 24 Jan 2012 00:04:41 +0100 Subject: [PATCH 04/16] Print version on load --- Makefile.dkms | 2 +- bbswitch.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile.dkms b/Makefile.dkms index 48ec687..3a06762 100644 --- a/Makefile.dkms +++ b/Makefile.dkms @@ -1,6 +1,6 @@ modname := bbswitch DKMS := dkms -modver := $(shell awk -F'"' '/MODULE_VERSION/{print $$2}' < bbswitch.c) +modver := $(shell awk -F'"' '/define *BBSWITCH_VERSION/{print $$2}' < bbswitch.c) # directory in which generated files are stored DKMS_DEST := /usr/src/$(modname)-$(modver) diff --git a/bbswitch.c b/bbswitch.c index 6dc6601..5d2858a 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -16,10 +16,12 @@ #include #include +#define BBSWITCH_VERSION "0.4.1" + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Toggle the discrete graphics card"); MODULE_AUTHOR("Peter Lekensteyn "); -MODULE_VERSION("0.4.1"); +MODULE_VERSION(BBSWITCH_VERSION); enum { CARD_UNCHANGED = -1, @@ -406,6 +408,8 @@ static int __init bbswitch_init(void) { struct pci_dev *pdev = NULL; acpi_handle igd_handle = NULL; + printk(KERN_INFO "bbswitch: version %s\n", BBSWITCH_VERSION); + while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; From 0ec41fa2da2a56b11d7049ae48ddc355e3a6fceb Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Tue, 24 Jan 2012 14:30:53 +0100 Subject: [PATCH 05/16] Support Optimus machines through WMMX (GH-2) This should finally support machines like the Lenovo Y570. WMMX methods should be more reliable than direct DSM probing so let's try that before calling DSM directly. --- bbswitch.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index 5d2858a..1d7e893 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -44,7 +44,7 @@ static const char acpi_optimus_dsm_muid[16] = { }; #define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" -#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA DSM */ +#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA/Optimus DSM */ static const char acpi_nvidia_dsm_muid[16] = { 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4 @@ -73,6 +73,7 @@ enum dsm_type { DSM_TYPE_OPTIMUS, DSM_TYPE_NVIDIA, /* _DSM call through a WMI MX method */ + DSM_TYPE_OPTIMUS_WMI, DSM_TYPE_NVIDIA_WMI, }; static enum dsm_type dsm_type = DSM_TYPE_UNSUPPORTED; @@ -220,10 +221,9 @@ static int has_wmi_func(const char muid[16], int revid, int sfnc) { } static int bbswitch_optimus_dsm(void) { + char args[] = {1, 0, 0, 3}; + u32 result = 0; if (dsm_type == DSM_TYPE_OPTIMUS) { - char args[] = {1, 0, 0, 3}; - u32 result = 0; - if (acpi_call_dsm(dis_handle, acpi_optimus_dsm_muid, 0x100, 0x1A, args, &result)) { // failure @@ -231,6 +231,13 @@ static int bbswitch_optimus_dsm(void) { } printk(KERN_DEBUG "bbswitch: Result of Optimus _DSM call: %08X\n", result); + } else if (dsm_type == DSM_TYPE_OPTIMUS_WMI) { + if (wmmx_call(acpi_optimus_dsm_muid, 0x100, 0x1A, args, &result)) { + // failure + return 1; + } + printk(KERN_DEBUG "bbswitch: Result of WMMX call for Optimus: %08X\n", + result); } return 0; } @@ -446,13 +453,20 @@ static int __init bbswitch_init(void) { return -ENODEV; } - if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { - dsm_type = DSM_TYPE_OPTIMUS; - printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); + if (has_wmi_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { + dsm_type = DSM_TYPE_OPTIMUS_WMI; + printk(KERN_INFO "bbswitch: detected an Optimus WMMX function\n"); } else if (has_wmi_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { dsm_type = DSM_TYPE_NVIDIA_WMI; printk(KERN_INFO "bbswitch: detected a nVidia WMMX function\n"); + } else if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { + /* necessary for at least Lenovo Ideapad Z570 which does not have the + * WMMX method */ + dsm_type = DSM_TYPE_OPTIMUS; + printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); } else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { + /* necessary for at least Dell XPS L501X bios A08 which does not have + * the WMMX method */ dsm_type = DSM_TYPE_NVIDIA; printk(KERN_INFO "bbswitch: detected a nVidia _DSM function\n"); } else { From fd74ba5bd8e12761543b360619ceb3b9d8cb4adf Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Tue, 24 Jan 2012 14:33:21 +0100 Subject: [PATCH 06/16] Load module dependency on make load: wmi --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index d2c88af..9ce4c15 100644 --- a/Makefile +++ b/Makefile @@ -13,4 +13,5 @@ clean: load: -/sbin/rmmod $(modname) + /sbin/modprobe wmi /sbin/insmod $(modname).ko From f4c2fadaf5ea851f30a8306dd3f59b59d2223a3e Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 28 Jan 2012 10:56:58 +0100 Subject: [PATCH 07/16] Revert "Load module dependency on make load: wmi" This reverts commit fd74ba5bd8e12761543b360619ceb3b9d8cb4adf. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 9ce4c15..d2c88af 100644 --- a/Makefile +++ b/Makefile @@ -13,5 +13,4 @@ clean: load: -/sbin/rmmod $(modname) - /sbin/modprobe wmi /sbin/insmod $(modname).ko From e321debb5bd1dff5fd725188f1b47b6a47868e7b Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 28 Jan 2012 10:57:03 +0100 Subject: [PATCH 08/16] Revert "Support Optimus machines through WMMX (GH-2)" This reverts commit 0ec41fa2da2a56b11d7049ae48ddc355e3a6fceb. --- bbswitch.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index 1d7e893..5d2858a 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -44,7 +44,7 @@ static const char acpi_optimus_dsm_muid[16] = { }; #define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" -#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA/Optimus DSM */ +#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA DSM */ static const char acpi_nvidia_dsm_muid[16] = { 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4 @@ -73,7 +73,6 @@ enum dsm_type { DSM_TYPE_OPTIMUS, DSM_TYPE_NVIDIA, /* _DSM call through a WMI MX method */ - DSM_TYPE_OPTIMUS_WMI, DSM_TYPE_NVIDIA_WMI, }; static enum dsm_type dsm_type = DSM_TYPE_UNSUPPORTED; @@ -221,9 +220,10 @@ static int has_wmi_func(const char muid[16], int revid, int sfnc) { } static int bbswitch_optimus_dsm(void) { - char args[] = {1, 0, 0, 3}; - u32 result = 0; if (dsm_type == DSM_TYPE_OPTIMUS) { + char args[] = {1, 0, 0, 3}; + u32 result = 0; + if (acpi_call_dsm(dis_handle, acpi_optimus_dsm_muid, 0x100, 0x1A, args, &result)) { // failure @@ -231,13 +231,6 @@ static int bbswitch_optimus_dsm(void) { } printk(KERN_DEBUG "bbswitch: Result of Optimus _DSM call: %08X\n", result); - } else if (dsm_type == DSM_TYPE_OPTIMUS_WMI) { - if (wmmx_call(acpi_optimus_dsm_muid, 0x100, 0x1A, args, &result)) { - // failure - return 1; - } - printk(KERN_DEBUG "bbswitch: Result of WMMX call for Optimus: %08X\n", - result); } return 0; } @@ -453,20 +446,13 @@ static int __init bbswitch_init(void) { return -ENODEV; } - if (has_wmi_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { - dsm_type = DSM_TYPE_OPTIMUS_WMI; - printk(KERN_INFO "bbswitch: detected an Optimus WMMX function\n"); + if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { + dsm_type = DSM_TYPE_OPTIMUS; + printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); } else if (has_wmi_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { dsm_type = DSM_TYPE_NVIDIA_WMI; printk(KERN_INFO "bbswitch: detected a nVidia WMMX function\n"); - } else if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { - /* necessary for at least Lenovo Ideapad Z570 which does not have the - * WMMX method */ - dsm_type = DSM_TYPE_OPTIMUS; - printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); } else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { - /* necessary for at least Dell XPS L501X bios A08 which does not have - * the WMMX method */ dsm_type = DSM_TYPE_NVIDIA; printk(KERN_INFO "bbswitch: detected a nVidia _DSM function\n"); } else { From 7c30e38bd1393fdc29f807ee1628c977896524d3 Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 28 Jan 2012 10:57:04 +0100 Subject: [PATCH 09/16] Revert "Add support for WMMX method" This reverts commit 11ddd3469fc9b20ce8fffec4e4062d2d831496ae. --- bbswitch.c | 105 +++++++---------------------------------------------- 1 file changed, 13 insertions(+), 92 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index 5d2858a..aa0f2a5 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -43,8 +43,6 @@ static const char acpi_optimus_dsm_muid[16] = { 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, }; -#define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" -#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA DSM */ static const char acpi_nvidia_dsm_muid[16] = { 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4 @@ -60,22 +58,10 @@ It looks like something for Intel GPU: http://lxr.linux.no/#linux+v3.1.5/drivers/gpu/drm/i915/intel_acpi.c */ -struct mxdsm_args { - u32 func; - char muid[16]; - u32 revid; - u32 sfnc; - char args[4]; -}; - -enum dsm_type { - DSM_TYPE_UNSUPPORTED, - DSM_TYPE_OPTIMUS, - DSM_TYPE_NVIDIA, - /* _DSM call through a WMI MX method */ - DSM_TYPE_NVIDIA_WMI, -}; -static enum dsm_type dsm_type = DSM_TYPE_UNSUPPORTED; +#define DSM_TYPE_UNSUPPORTED 0 +#define DSM_TYPE_OPTIMUS 1 +#define DSM_TYPE_NVIDIA 2 +static int dsm_type = DSM_TYPE_UNSUPPORTED; static struct pci_dev *dis_dev; static acpi_handle dis_handle; @@ -94,8 +80,6 @@ static char *buffer_to_string(const char buffer[], char *target) { return target; } -static int parse_dsm_result(struct acpi_buffer *output, uint32_t *result); - // Returns 0 if the call succeeded and non-zero otherwise. If the call // succeeded, the result is stored in "result" providing that the result is an // integer or a buffer containing 4 values @@ -104,6 +88,7 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_object_list input; union acpi_object params[4]; + union acpi_object *obj; int err; input.count = 4; @@ -135,12 +120,8 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, buffer_to_string(args, tmp), acpi_format_exception(err)); return err; } - return parse_dsm_result(&output, result); -} -// Processes the result of an ACPI _DSM call and deallocates the result memory -static int parse_dsm_result(struct acpi_buffer *output, uint32_t *result) { - union acpi_object *obj = output->pointer; + obj = (union acpi_object *)output.pointer; if (obj->type == ACPI_TYPE_INTEGER && result) { *result = obj->integer.value; @@ -157,7 +138,7 @@ static int parse_dsm_result(struct acpi_buffer *output, uint32_t *result) { " type: %X\n", obj->type); } - kfree(output->pointer); + kfree(output.pointer); return 0; } @@ -174,51 +155,6 @@ static int has_dsm_func(const char muid[16], int revid, int sfnc) { return result & 1 && result & (1 << sfnc); } -// Returns 0 if the call succeeded and non-zero otherwise. If the call -// succeeded, the result is stored in "result" providing that the result is an -// integer or a buffer containing 4 values -static int wmmx_call(const char muid[16], int revid, int func, char args[4], - uint32_t *result) { - struct mxdsm_args mx_args = { - .func = MXM_WMMX_FUNC_DSM, - .revid = revid, - .sfnc = func, - }; - struct acpi_buffer input = { (acpi_size)sizeof(mx_args), &mx_args }; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_status status; - - memcpy(mx_args.muid, muid, sizeof(mx_args.muid)); - if (args) { - memcpy(mx_args.args, args, sizeof(mx_args.args)); - } else { - // some implementations do not like empty values - memset(mx_args.args, 0, sizeof(mx_args.args)); - } - - // 1, > 1 <-- required by at least Acer Travelmate 8472TG, seen on others - status = wmi_evaluate_method(MXM_WMMX_GUID, 1, 1, &input, &output); - if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "bbswitch: failed to evaluate WMMX: %s\n", - acpi_format_exception(status)); - return status; - } - return parse_dsm_result(&output, result); -} - -// Returns 1 if a _DSM function and its function index exists and 0 otherwise -static int has_wmi_func(const char muid[16], int revid, int sfnc) { - u32 result = 0; - - // fail if the _DSM call failed - if (wmmx_call(muid, revid, 0, 0, &result)) - return 0; - - // ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. If - // the n-th bit is enabled, function n is supported - return result & 1 && result & (1 << sfnc); -} - static int bbswitch_optimus_dsm(void) { if (dsm_type == DSM_TYPE_OPTIMUS) { char args[] = {1, 0, 0, 3}; @@ -236,9 +172,10 @@ static int bbswitch_optimus_dsm(void) { } static int bbswitch_acpi_off(void) { - char args[] = {2, 0, 0, 0}; - u32 result = 0; if (dsm_type == DSM_TYPE_NVIDIA) { + char args[] = {2, 0, 0, 0}; + u32 result = 0; + if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { // failure @@ -246,21 +183,15 @@ static int bbswitch_acpi_off(void) { } printk(KERN_DEBUG "bbswitch: Result of _DSM call for OFF: %08X\n", result); - } else if (dsm_type == DSM_TYPE_NVIDIA_WMI) { - if (wmmx_call(acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { - // failure - return 1; - } - printk(KERN_DEBUG "bbswitch: Result of WMMX call for OFF: %08X\n", - result); } return 0; } static int bbswitch_acpi_on(void) { - char args[] = {1, 0, 0, 0}; - u32 result = 0; if (dsm_type == DSM_TYPE_NVIDIA) { + char args[] = {1, 0, 0, 0}; + u32 result = 0; + if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { // failure @@ -268,13 +199,6 @@ static int bbswitch_acpi_on(void) { } printk(KERN_DEBUG "bbswitch: Result of _DSM call for ON: %08X\n", result); - } else if (dsm_type == DSM_TYPE_NVIDIA_WMI) { - if (wmmx_call(acpi_nvidia_dsm_muid, 0x102, 0x3, args, &result)) { - // failure - return 1; - } - printk(KERN_DEBUG "bbswitch: Result of WMMX call for ON: %08X\n", - result); } return 0; } @@ -449,9 +373,6 @@ static int __init bbswitch_init(void) { if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { dsm_type = DSM_TYPE_OPTIMUS; printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); - } else if (has_wmi_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { - dsm_type = DSM_TYPE_NVIDIA_WMI; - printk(KERN_INFO "bbswitch: detected a nVidia WMMX function\n"); } else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { dsm_type = DSM_TYPE_NVIDIA; printk(KERN_INFO "bbswitch: detected a nVidia _DSM function\n"); From 804773eb22691c9f7675bb924eb358946f6f5e75 Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Wed, 29 Feb 2012 00:10:32 +0100 Subject: [PATCH 10/16] Fix misuse of sizeof which trims arguments/muid (GH-12) --- bbswitch.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index aa0f2a5..c1984f9 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -71,12 +71,12 @@ static struct notifier_block nb; /* whether the card was off before suspend or not; on: 0, off: 1 */ int dis_before_suspend_disabled; -static char *buffer_to_string(const char buffer[], char *target) { +static char *buffer_to_string(const char *buffer, size_t n, char *target) { int i; - for (i=0; i Date: Wed, 29 Feb 2012 00:30:14 +0100 Subject: [PATCH 11/16] Buffer overflow by one byte --- bbswitch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index c1984f9..d17c05b 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -74,9 +74,8 @@ int dis_before_suspend_disabled; static char *buffer_to_string(const char *buffer, size_t n, char *target) { int i; for (i=0; i Date: Wed, 29 Feb 2012 00:49:34 +0100 Subject: [PATCH 12/16] Don't share buffer for muid/args, it'll be overwritten --- bbswitch.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index d17c05b..f0cd673 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -111,12 +111,13 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, err = acpi_evaluate_object(handle, "_DSM", &input, &output); if (err) { - char tmp[5 * 16]; /* enough space for muid or args with separators */ + char muid_str[5 * 16]; + char args_str[5 * 4]; printk(KERN_WARNING "bbswitch: failed to evaluate _DSM {%s} %X %X" " {%s}: %s\n", - buffer_to_string(muid, 16, tmp), revid, func, - buffer_to_string(args, 4, tmp), acpi_format_exception(err)); + buffer_to_string(muid, 16, muid_str), revid, func, + buffer_to_string(args, 4, args_str), acpi_format_exception(err)); return err; } From e13e6ffc5468066ac780bfb098540ceffb8cf201 Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Thu, 1 Mar 2012 13:16:48 +0100 Subject: [PATCH 13/16] Fix null deref when ACPI method call failed (GH-12) --- bbswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbswitch.c b/bbswitch.c index f0cd673..f1eb431 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -74,7 +74,7 @@ int dis_before_suspend_disabled; static char *buffer_to_string(const char *buffer, size_t n, char *target) { int i; for (i=0; i Date: Thu, 1 Mar 2012 21:53:52 +0100 Subject: [PATCH 14/16] Fix formatting of arguments on case of ACPI failure (GH-12) --- bbswitch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bbswitch.c b/bbswitch.c index f1eb431..0c55d98 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -74,7 +74,7 @@ int dis_before_suspend_disabled; static char *buffer_to_string(const char *buffer, size_t n, char *target) { int i; for (i=0; i Date: Thu, 26 Apr 2012 12:49:14 +0200 Subject: [PATCH 15/16] Fix documentation error (Closes GH-16) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72e3d20..17bef18 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ The module has some options that control the behavior on loading and unloading: `load_state` and `unload_state`. Valid values are `-1`, `0` and `1` meaning "do not change the card state", "turn the card off" and "turn the card on" respectively. For example, if you want to have `bbswitch` disable the card -immediately when loading the module while disabling the card on unload, load the +immediately when loading the module while enabling the card on unload, load the module with: # modprobe bbswitch load_state=0 unload_state=1 From d12ce73a0d042fce3738aea800f55f8a23874e8c Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Thu, 26 Apr 2012 12:56:47 +0200 Subject: [PATCH 16/16] Update to version 0.4.2, update NEWS --- NEWS | 8 ++++++++ bbswitch.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 2f08c50..335e37e 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +Version 0.4.2 - 26 April 2012 + +* Fixed a documentation error on unload_state. +* Added Makefile.dkms and documentation for easier installation using DKMS. +* Make /proc/acpi/bbswitch world-writable +* Fix NULL pointer dereference when reporting a failure during ACPI method + evaluation. + Version 0.4.1 - 16 January 2012 * Corrected a small error that yielded an confusing error message "The discrete diff --git a/bbswitch.c b/bbswitch.c index 0c55d98..ca79e0d 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -16,7 +16,7 @@ #include #include -#define BBSWITCH_VERSION "0.4.1" +#define BBSWITCH_VERSION "0.4.2" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Toggle the discrete graphics card");