25 Commits

Author SHA1 Message Date
b12b017f3a Update to version 0.5, update NEWS 2012-10-22 17:03:33 +02:00
2adcb24207 Merge branch 'master' into develop 2012-10-22 17:03:19 +02:00
8beb9e3f88 Add missing 'static' keyword 2012-10-22 16:43:40 +02:00
da55a70331 Add modeline for vim 2012-10-19 17:32:16 +02:00
7f9fb7adf9 Power on the PCIe port before accessing PCI config space (#35)
Fixes regression in Linux 3.6 when run-time power management is enabled. See
https://bugzilla.kernel.org/show_bug.cgi?id=48981 for a discussion.
2012-10-19 17:28:59 +02:00
b8a6029692 Convert printk to pr_... macros
For pr_debug outputs on _DSM results, use "make DEBUG=1"
2012-10-19 16:58:43 +02:00
aeff596524 Makefile: fix clean and install target
clean: do not try to wipe /usr/src/linux-...

load: rmmod and insmod do not have to be in /sbin (for example, /usr/bin
on Arch Linux). Assume that the user has a sane PATH.
2012-10-14 11:34:52 +02:00
dbf8aaf8e2 Use D3cold instead of D3hot to reflect real device state 2012-10-14 11:31:47 +02:00
d54dd454b6 Merge pull request #31 from wzzrd/develop
Add install and uninstall targets to Makefile
2012-08-26 06:52:27 -07:00
5f2db6c26a Add install and uninstall targets to Makefile
For if you do not want to or are unable to use dkms
2012-08-26 15:40:05 +02:00
8ada8849f2 Compatibility with older DKMS versions (GH-25) 2012-07-07 20:01:33 +02:00
979fbcf552 Merge branch 'develop' 2012-04-26 12:59:41 +02:00
d12ce73a0d Update to version 0.4.2, update NEWS 2012-04-26 12:56:47 +02:00
0d1429e8bb Fix documentation error (Closes GH-16) 2012-04-26 12:49:14 +02:00
af91008deb Fix formatting of arguments on case of ACPI failure (GH-12) 2012-03-01 21:53:52 +01:00
e13e6ffc54 Fix null deref when ACPI method call failed (GH-12) 2012-03-01 13:18:24 +01:00
f034252a13 Don't share buffer for muid/args, it'll be overwritten 2012-02-29 00:49:34 +01:00
647e3dddea Buffer overflow by one byte 2012-02-29 00:30:14 +01:00
804773eb22 Fix misuse of sizeof which trims arguments/muid (GH-12) 2012-02-29 00:10:32 +01:00
7c30e38bd1 Revert "Add support for WMMX method"
This reverts commit 11ddd3469f.
2012-01-28 10:57:04 +01:00
e321debb5b Revert "Support Optimus machines through WMMX (GH-2)"
This reverts commit 0ec41fa2da.
2012-01-28 10:57:03 +01:00
f4c2fadaf5 Revert "Load module dependency on make load: wmi"
This reverts commit fd74ba5bd8.
2012-01-28 10:56:58 +01:00
d5ff9a1fb9 Merge branch 'develop' 2012-01-16 17:24:47 +01:00
c1dd7140df Merge branch 'develop' 2012-01-15 17:18:45 +01:00
fd4dabf37e Merge branch 'develop' 2012-01-14 23:18:54 +01:00
5 changed files with 129 additions and 166 deletions

View File

@ -5,13 +5,27 @@ KVERSION := $(shell uname -r)
KDIR := /lib/modules/$(KVERSION)/build KDIR := /lib/modules/$(KVERSION)/build
PWD := "$$(pwd)" PWD := "$$(pwd)"
ifdef DEBUG
CFLAGS_$(obj-m) := -DDEBUG
endif
default: default:
$(MAKE) -C $(KDIR) M=$(PWD) modules $(MAKE) -C $(KDIR) M=$(PWD) modules
clean: clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean $(MAKE) O=$(PWD) -C $(KDIR) M=$(PWD) clean
load: load:
-/sbin/rmmod $(modname) -rmmod $(modname)
/sbin/modprobe wmi insmod $(modname).ko
/sbin/insmod $(modname).ko
install:
mkdir -p /lib/modules/$(KVERSION)/misc/$(modname)
install -m 0755 -o root -g root $(modname).ko /lib/modules/$(KVERSION)/misc/$(modname)
depmod -a
uninstall:
rm /lib/modules/$(KVERSION)/misc/$(modname)/$(modname).ko
rmdir /lib/modules/$(KVERSION)/misc/$(modname)
rmdir /lib/modules/$(KVERSION)/misc
depmod -a

View File

@ -13,12 +13,12 @@ src_install:
sed 's/#MODULE_VERSION#/$(modver)/' dkms/dkms.conf > '$(DKMS_DEST)/dkms.conf' sed 's/#MODULE_VERSION#/$(modver)/' dkms/dkms.conf > '$(DKMS_DEST)/dkms.conf'
build: src_install build: src_install
$(DKMS) build 'bbswitch/$(modver)' $(DKMS) build -m bbswitch -v $(modver)
install: build install: build
$(DKMS) install 'bbswitch/$(modver)' $(DKMS) install -m bbswitch -v $(modver)
uninstall: uninstall:
$(DKMS) remove bbswitch/$(modver) --all $(DKMS) remove -m bbswitch -v $(modver) --all
.PHONY: all src_install build install uninstall .PHONY: all src_install build install uninstall

15
NEWS
View File

@ -1,3 +1,18 @@
Version 0.5 - 22 October 2012
* Improved compatibility with older DKMS versions.
* Set device state to D3cold instead of D3hot.
* Only print DSM method call results when debugging is enabled.
* Fixed runtime power management regression in Linux 3.6.
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 Version 0.4.1 - 16 January 2012
* Corrected a small error that yielded an confusing error message "The discrete * Corrected a small error that yielded an confusing error message "The discrete

View File

@ -40,8 +40,8 @@ DKMS support
If you have DKMS installed, you can install bbswitch in such a way that it 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 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 by running `dkms remove -m bbswitch -v OLDVERSION --all` as root. To install
version, simply run: the new version, simply run:
# make -f Makefile.dkms # make -f Makefile.dkms
@ -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 `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" 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 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: module with:
# modprobe bbswitch load_state=0 unload_state=1 # modprobe bbswitch load_state=0 unload_state=1

View File

@ -9,14 +9,17 @@
* Get status * Get status
* # cat /proc/acpi/bbswitch * # cat /proc/acpi/bbswitch
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/pm_runtime.h>
#define BBSWITCH_VERSION "0.4.1" #define BBSWITCH_VERSION "0.5"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Toggle the discrete graphics card"); MODULE_DESCRIPTION("Toggle the discrete graphics card");
@ -43,8 +46,6 @@ static const char acpi_optimus_dsm_muid[16] = {
0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
}; };
#define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
#define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA/Optimus DSM */
static const char acpi_nvidia_dsm_muid[16] = { static const char acpi_nvidia_dsm_muid[16] = {
0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4
@ -60,23 +61,10 @@ It looks like something for Intel GPU:
http://lxr.linux.no/#linux+v3.1.5/drivers/gpu/drm/i915/intel_acpi.c http://lxr.linux.no/#linux+v3.1.5/drivers/gpu/drm/i915/intel_acpi.c
*/ */
struct mxdsm_args { #define DSM_TYPE_UNSUPPORTED 0
u32 func; #define DSM_TYPE_OPTIMUS 1
char muid[16]; #define DSM_TYPE_NVIDIA 2
u32 revid; static int dsm_type = DSM_TYPE_UNSUPPORTED;
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_OPTIMUS_WMI,
DSM_TYPE_NVIDIA_WMI,
};
static enum dsm_type dsm_type = DSM_TYPE_UNSUPPORTED;
static struct pci_dev *dis_dev; static struct pci_dev *dis_dev;
static acpi_handle dis_handle; static acpi_handle dis_handle;
@ -84,19 +72,16 @@ static acpi_handle dis_handle;
/* used for keeping the PM event handler */ /* used for keeping the PM event handler */
static struct notifier_block nb; static struct notifier_block nb;
/* whether the card was off before suspend or not; on: 0, off: 1 */ /* whether the card was off before suspend or not; on: 0, off: 1 */
int dis_before_suspend_disabled; static 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; int i;
for (i=0; i<sizeof(buffer); i++) { for (i=0; i<n; i++) {
sprintf(target + i * 5, "%02X,", buffer[i]); snprintf(target + i * 5, 5 * (n - i), "0x%02X,", buffer ? buffer[i] & 0xFF : 0);
} }
target[sizeof(buffer) * 5] = '\0';
return 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 // 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 // succeeded, the result is stored in "result" providing that the result is an
// integer or a buffer containing 4 values // integer or a buffer containing 4 values
@ -105,6 +90,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_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_object_list input; struct acpi_object_list input;
union acpi_object params[4]; union acpi_object params[4];
union acpi_object *obj;
int err; int err;
input.count = 4; input.count = 4;
@ -128,20 +114,16 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid,
err = acpi_evaluate_object(handle, "_DSM", &input, &output); err = acpi_evaluate_object(handle, "_DSM", &input, &output);
if (err) { if (err) {
char tmp[5 * max(sizeof(muid), sizeof(args))]; char muid_str[5 * 16];
char args_str[5 * 4];
printk(KERN_WARNING "bbswitch: failed to evaluate _DSM {%s} %X %X" pr_warn("failed to evaluate _DSM {%s} 0x%X 0x%X {%s}: %s\n",
" {%s}: %s\n", buffer_to_string(muid, 16, muid_str), revid, func,
buffer_to_string(muid, tmp), revid, func, buffer_to_string(args, 4, args_str), acpi_format_exception(err));
buffer_to_string(args, tmp), acpi_format_exception(err));
return err; return err;
} }
return parse_dsm_result(&output, result);
}
// Processes the result of an ACPI _DSM call and deallocates the result memory obj = (union acpi_object *)output.pointer;
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) { if (obj->type == ACPI_TYPE_INTEGER && result) {
*result = obj->integer.value; *result = obj->integer.value;
@ -154,11 +136,11 @@ static int parse_dsm_result(struct acpi_buffer *output, uint32_t *result) {
*result |= (obj->buffer.pointer[3] << 24); *result |= (obj->buffer.pointer[3] << 24);
} }
} else { } else {
printk(KERN_WARNING "bbswitch: _DSM call yields an unsupported result" pr_warn("_DSM call yields an unsupported result type: %#x\n",
" type: %X\n", obj->type); obj->type);
} }
kfree(output->pointer); kfree(output.pointer);
return 0; return 0;
} }
@ -175,113 +157,47 @@ static int has_dsm_func(const char muid[16], int revid, int sfnc) {
return result & 1 && result & (1 << 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) { static int bbswitch_optimus_dsm(void) {
if (dsm_type == DSM_TYPE_OPTIMUS) {
char args[] = {1, 0, 0, 3}; char args[] = {1, 0, 0, 3};
u32 result = 0; u32 result = 0;
if (dsm_type == DSM_TYPE_OPTIMUS) {
if (acpi_call_dsm(dis_handle, acpi_optimus_dsm_muid, 0x100, 0x1A, args, if (acpi_call_dsm(dis_handle, acpi_optimus_dsm_muid, 0x100, 0x1A, args,
&result)) { &result)) {
// failure // failure
return 1; return 1;
} }
printk(KERN_DEBUG "bbswitch: Result of Optimus _DSM call: %08X\n", pr_debug("Result of Optimus _DSM call: %08X\n", result);
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; return 0;
} }
static int bbswitch_acpi_off(void) { static int bbswitch_acpi_off(void) {
if (dsm_type == DSM_TYPE_NVIDIA) {
char args[] = {2, 0, 0, 0}; char args[] = {2, 0, 0, 0};
u32 result = 0; u32 result = 0;
if (dsm_type == DSM_TYPE_NVIDIA) {
if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args, if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args,
&result)) { &result)) {
// failure // failure
return 1; return 1;
} }
printk(KERN_DEBUG "bbswitch: Result of _DSM call for OFF: %08X\n", pr_debug("Result of _DSM call for OFF: %08X\n", result);
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; return 0;
} }
static int bbswitch_acpi_on(void) { static int bbswitch_acpi_on(void) {
if (dsm_type == DSM_TYPE_NVIDIA) {
char args[] = {1, 0, 0, 0}; char args[] = {1, 0, 0, 0};
u32 result = 0; u32 result = 0;
if (dsm_type == DSM_TYPE_NVIDIA) {
if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args, if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args,
&result)) { &result)) {
// failure // failure
return 1; return 1;
} }
printk(KERN_DEBUG "bbswitch: Result of _DSM call for ON: %08X\n", pr_debug("Result of _DSM call for ON: %08X\n", result);
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; return 0;
} }
@ -304,47 +220,54 @@ static void bbswitch_off(void) {
// to prevent the system from possibly locking up, don't disable the device // to prevent the system from possibly locking up, don't disable the device
// if it's still in use by a driver (i.e. nouveau or nvidia) // if it's still in use by a driver (i.e. nouveau or nvidia)
if (dis_dev->driver) { if (dis_dev->driver) {
printk(KERN_WARNING "bbswitch: device %s is in use by driver '%s', " pr_warn("device %s is in use by driver '%s', refusing OFF\n",
"refusing OFF\n", dev_name(&dis_dev->dev), dis_dev->driver->name); dev_name(&dis_dev->dev), dis_dev->driver->name);
return; return;
} }
printk(KERN_INFO "bbswitch: disabling discrete graphics\n"); pr_info("disabling discrete graphics\n");
if (bbswitch_optimus_dsm()) { if (bbswitch_optimus_dsm()) {
printk(KERN_WARNING "bbswitch: Optimus ACPI call failed, the device is" pr_warn("Optimus ACPI call failed, the device is not disabled\n");
" not disabled\n");
return; return;
} }
pci_save_state(dis_dev); pci_save_state(dis_dev);
pci_clear_master(dis_dev); pci_clear_master(dis_dev);
pci_disable_device(dis_dev); pci_disable_device(dis_dev);
pci_set_power_state(dis_dev, PCI_D3hot); pci_set_power_state(dis_dev, PCI_D3cold);
if (bbswitch_acpi_off()) if (bbswitch_acpi_off())
printk(KERN_WARNING "bbswitch: The discrete card could not be disabled" pr_warn("The discrete card could not be disabled by a _DSM call\n");
" by a _DSM call\n");
} }
static void bbswitch_on(void) { static void bbswitch_on(void) {
if (!is_card_disabled()) if (!is_card_disabled())
return; return;
printk(KERN_INFO "bbswitch: enabling discrete graphics\n"); pr_info("enabling discrete graphics\n");
if (bbswitch_acpi_on()) if (bbswitch_acpi_on())
printk(KERN_WARNING "bbswitch: The discrete card could not be enabled" pr_warn("The discrete card could not be enabled by a _DSM call\n");
" by a _DSM call\n");
pci_set_power_state(dis_dev, PCI_D0); pci_set_power_state(dis_dev, PCI_D0);
pci_restore_state(dis_dev); pci_restore_state(dis_dev);
if (pci_enable_device(dis_dev)) if (pci_enable_device(dis_dev))
printk(KERN_WARNING "bbswitch: failed to enable %s\n", pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev));
dev_name(&dis_dev->dev));
pci_set_master(dis_dev); pci_set_master(dis_dev);
} }
/* power bus so we can read PCI configuration space */
static void dis_dev_get(void) {
if (dis_dev->bus && dis_dev->bus->self)
pm_runtime_get_sync(&dis_dev->bus->self->dev);
}
static void dis_dev_put(void) {
if (dis_dev->bus && dis_dev->bus->self)
pm_runtime_put_sync(&dis_dev->bus->self->dev);
}
static ssize_t bbswitch_proc_write(struct file *fp, const char __user *buff, static ssize_t bbswitch_proc_write(struct file *fp, const char __user *buff,
size_t len, loff_t *off) { size_t len, loff_t *off) {
char cmd[8]; char cmd[8];
@ -355,19 +278,25 @@ static ssize_t bbswitch_proc_write(struct file *fp, const char __user *buff,
if (copy_from_user(cmd, buff, len)) if (copy_from_user(cmd, buff, len))
return -EFAULT; return -EFAULT;
dis_dev_get();
if (strncmp(cmd, "OFF", 3) == 0) if (strncmp(cmd, "OFF", 3) == 0)
bbswitch_off(); bbswitch_off();
if (strncmp(cmd, "ON", 2) == 0) if (strncmp(cmd, "ON", 2) == 0)
bbswitch_on(); bbswitch_on();
dis_dev_put();
return len; return len;
} }
static int bbswitch_proc_show(struct seq_file *seqfp, void *p) { static int bbswitch_proc_show(struct seq_file *seqfp, void *p) {
// show the card state. Example output: 0000:01:00:00 ON // show the card state. Example output: 0000:01:00:00 ON
dis_dev_get();
seq_printf(seqfp, "%s %s\n", dev_name(&dis_dev->dev), seq_printf(seqfp, "%s %s\n", dev_name(&dis_dev->dev),
is_card_disabled() ? "OFF" : "ON"); is_card_disabled() ? "OFF" : "ON");
dis_dev_put();
return 0; return 0;
} }
static int bbswitch_proc_open(struct inode *inode, struct file *file) { static int bbswitch_proc_open(struct inode *inode, struct file *file) {
@ -379,19 +308,24 @@ static int bbswitch_pm_handler(struct notifier_block *nbp,
switch (event_type) { switch (event_type) {
case PM_HIBERNATION_PREPARE: case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE: case PM_SUSPEND_PREPARE:
dis_dev_get();
dis_before_suspend_disabled = is_card_disabled(); dis_before_suspend_disabled = is_card_disabled();
// enable the device before suspend to avoid the PCI config space from // enable the device before suspend to avoid the PCI config space from
// being saved incorrectly // being saved incorrectly
if (dis_before_suspend_disabled) if (dis_before_suspend_disabled)
bbswitch_on(); bbswitch_on();
dis_dev_put();
break; break;
case PM_POST_HIBERNATION: case PM_POST_HIBERNATION:
case PM_POST_SUSPEND: case PM_POST_SUSPEND:
case PM_POST_RESTORE: case PM_POST_RESTORE:
// after suspend, the card is on, but if it was off before suspend, // after suspend, the card is on, but if it was off before suspend,
// disable it again // disable it again
if (dis_before_suspend_disabled) if (dis_before_suspend_disabled) {
dis_dev_get();
bbswitch_off(); bbswitch_off();
dis_dev_put();
}
break; break;
case PM_RESTORE_PREPARE: case PM_RESTORE_PREPARE:
// deliberately don't do anything as it does not occur before suspend // deliberately don't do anything as it does not occur before suspend
@ -415,7 +349,7 @@ static int __init bbswitch_init(void) {
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
acpi_handle igd_handle = NULL; acpi_handle igd_handle = NULL;
printk(KERN_INFO "bbswitch: version %s\n", BBSWITCH_VERSION); pr_info("version %s\n", BBSWITCH_VERSION);
while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
@ -428,8 +362,8 @@ static int __init bbswitch_init(void) {
handle = DEVICE_ACPI_HANDLE(&pdev->dev); handle = DEVICE_ACPI_HANDLE(&pdev->dev);
if (!handle) { if (!handle) {
printk(KERN_WARNING "bbswitch: cannot find ACPI handle for VGA" pr_warn("cannot find ACPI handle for VGA device %s\n",
" device %s\n", dev_name(&pdev->dev)); dev_name(&pdev->dev));
continue; continue;
} }
@ -437,66 +371,60 @@ static int __init bbswitch_init(void) {
if (pdev->vendor == PCI_VENDOR_ID_INTEL) { if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
igd_handle = handle; igd_handle = handle;
printk(KERN_INFO "bbswitch: Found integrated VGA device %s: %s\n", pr_info("Found integrated VGA device %s: %s\n",
dev_name(&pdev->dev), (char *)buf.pointer); dev_name(&pdev->dev), (char *)buf.pointer);
} else { } else {
dis_dev = pdev; dis_dev = pdev;
dis_handle = handle; dis_handle = handle;
printk(KERN_INFO "bbswitch: Found discrete VGA device %s: %s\n", pr_info("Found discrete VGA device %s: %s\n",
dev_name(&pdev->dev), (char *)buf.pointer); dev_name(&pdev->dev), (char *)buf.pointer);
} }
kfree(buf.pointer); kfree(buf.pointer);
} }
if (dis_dev == NULL) { if (dis_dev == NULL) {
printk(KERN_ERR "bbswitch: No discrete VGA device found\n"); pr_err("No discrete VGA device found\n");
return -ENODEV; return -ENODEV;
} }
if (has_wmi_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) { if (has_dsm_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; dsm_type = DSM_TYPE_OPTIMUS;
printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n"); pr_info("detected an Optimus _DSM function\n");
} else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { } 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; dsm_type = DSM_TYPE_NVIDIA;
printk(KERN_INFO "bbswitch: detected a nVidia _DSM function\n"); pr_info("detected a nVidia _DSM function\n");
} else { } else {
/* At least two Acer machines are known to use the intel ACPI handle /* At least two Acer machines are known to use the intel ACPI handle
* with the legacy nvidia DSM */ * with the legacy nvidia DSM */
dis_handle = igd_handle; dis_handle = igd_handle;
if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) {
dsm_type = DSM_TYPE_NVIDIA; dsm_type = DSM_TYPE_NVIDIA;
printk(KERN_INFO "bbswitch: detected a nVidia _DSM function on the" pr_info("detected a nVidia _DSM function on the"
" integrated video card\n"); " integrated video card\n");
} else { } else {
printk(KERN_ERR "bbswitch: No suitable _DSM call found.\n"); pr_err("No suitable _DSM call found.\n");
return -ENODEV; return -ENODEV;
} }
} }
acpi_entry = proc_create("bbswitch", 0664, acpi_root_dir, &bbswitch_fops); acpi_entry = proc_create("bbswitch", 0664, acpi_root_dir, &bbswitch_fops);
if (acpi_entry == NULL) { if (acpi_entry == NULL) {
printk(KERN_ERR "bbswitch: Couldn't create proc entry\n"); pr_err("Couldn't create proc entry\n");
return -ENOMEM; return -ENOMEM;
} }
dis_dev_get();
if (load_state == CARD_ON) if (load_state == CARD_ON)
bbswitch_on(); bbswitch_on();
else if (load_state == CARD_OFF) else if (load_state == CARD_OFF)
bbswitch_off(); bbswitch_off();
printk(KERN_INFO "bbswitch: Succesfully loaded. Discrete card %s is %s\n", pr_info("Succesfully loaded. Discrete card %s is %s\n",
dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on"); dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
dis_dev_put();
nb.notifier_call = &bbswitch_pm_handler; nb.notifier_call = &bbswitch_pm_handler;
register_pm_notifier(&nb); register_pm_notifier(&nb);
@ -506,17 +434,23 @@ static int __init bbswitch_init(void) {
static void __exit bbswitch_exit(void) { static void __exit bbswitch_exit(void) {
remove_proc_entry("bbswitch", acpi_root_dir); remove_proc_entry("bbswitch", acpi_root_dir);
dis_dev_get();
if (unload_state == CARD_ON) if (unload_state == CARD_ON)
bbswitch_on(); bbswitch_on();
else if (unload_state == CARD_OFF) else if (unload_state == CARD_OFF)
bbswitch_off(); bbswitch_off();
printk(KERN_INFO "bbswitch: Unloaded. Discrete card %s is %s\n", pr_info("Unloaded. Discrete card %s is %s\n",
dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on"); dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
dis_dev_put();
if (nb.notifier_call) if (nb.notifier_call)
unregister_pm_notifier(&nb); unregister_pm_notifier(&nb);
} }
module_init(bbswitch_init); module_init(bbswitch_init);
module_exit(bbswitch_exit); module_exit(bbswitch_exit);
/* vim: set sw=4 ts=4 et: */