From 5f2db6c26a2d9a98ff16f027b6e4fd285298188d Mon Sep 17 00:00:00 2001 From: Maxim Burgerhout Date: Sun, 26 Aug 2012 15:40:05 +0200 Subject: [PATCH 1/8] Add install and uninstall targets to Makefile For if you do not want to or are unable to use dkms --- Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Makefile b/Makefile index d2c88af..ae14e28 100644 --- a/Makefile +++ b/Makefile @@ -14,3 +14,14 @@ clean: load: -/sbin/rmmod $(modname) /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 From dbf8aaf8e27b0d78be8ef71521a6bd0dcae36e55 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 14 Oct 2012 11:29:06 +0200 Subject: [PATCH 2/8] Use D3cold instead of D3hot to reflect real device state --- bbswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbswitch.c b/bbswitch.c index ca79e0d..0d163ce 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -237,7 +237,7 @@ static void bbswitch_off(void) { pci_save_state(dis_dev); pci_clear_master(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()) printk(KERN_WARNING "bbswitch: The discrete card could not be disabled" From aeff5965241d617d0086e19951f7f0131e6ecd2c Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 14 Oct 2012 11:34:52 +0200 Subject: [PATCH 3/8] 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. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index ae14e28..f9cc1e9 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,11 @@ default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: - $(MAKE) -C $(KDIR) M=$(PWD) clean + $(MAKE) O=$(PWD) -C $(KDIR) M=$(PWD) clean load: - -/sbin/rmmod $(modname) - /sbin/insmod $(modname).ko + -rmmod $(modname) + insmod $(modname).ko install: mkdir -p /lib/modules/$(KVERSION)/misc/$(modname) From b8a602969287a0da1d845dabdc6f06285283ff81 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 19 Oct 2012 16:58:43 +0200 Subject: [PATCH 4/8] Convert printk to pr_... macros For pr_debug outputs on _DSM results, use "make DEBUG=1" --- Makefile | 4 ++++ bbswitch.c | 64 +++++++++++++++++++++++++----------------------------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index f9cc1e9..9073c91 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,10 @@ KVERSION := $(shell uname -r) KDIR := /lib/modules/$(KVERSION)/build PWD := "$$(pwd)" +ifdef DEBUG +CFLAGS_$(obj-m) := -DDEBUG +endif + default: $(MAKE) -C $(KDIR) M=$(PWD) modules diff --git a/bbswitch.c b/bbswitch.c index 0d163ce..4d61875 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -9,6 +9,8 @@ * Get status * # cat /proc/acpi/bbswitch */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -114,8 +116,7 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, char muid_str[5 * 16]; char args_str[5 * 4]; - printk(KERN_WARNING "bbswitch: failed to evaluate _DSM {%s} 0x%X 0x%X" - " {%s}: %s\n", + pr_warn("failed to evaluate _DSM {%s} 0x%X 0x%X {%s}: %s\n", buffer_to_string(muid, 16, muid_str), revid, func, buffer_to_string(args, 4, args_str), acpi_format_exception(err)); return err; @@ -134,8 +135,8 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid, *result |= (obj->buffer.pointer[3] << 24); } } else { - printk(KERN_WARNING "bbswitch: _DSM call yields an unsupported result" - " type: %X\n", obj->type); + pr_warn("_DSM call yields an unsupported result type: %#x\n", + obj->type); } kfree(output.pointer); @@ -165,8 +166,7 @@ static int bbswitch_optimus_dsm(void) { // failure return 1; } - printk(KERN_DEBUG "bbswitch: Result of Optimus _DSM call: %08X\n", - result); + pr_debug("Result of Optimus _DSM call: %08X\n", result); } return 0; } @@ -181,8 +181,7 @@ static int bbswitch_acpi_off(void) { // failure return 1; } - printk(KERN_DEBUG "bbswitch: Result of _DSM call for OFF: %08X\n", - result); + pr_debug("Result of _DSM call for OFF: %08X\n", result); } return 0; } @@ -197,8 +196,7 @@ static int bbswitch_acpi_on(void) { // failure return 1; } - printk(KERN_DEBUG "bbswitch: Result of _DSM call for ON: %08X\n", - result); + pr_debug("Result of _DSM call for ON: %08X\n", result); } return 0; } @@ -221,16 +219,15 @@ static void bbswitch_off(void) { // 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 (dis_dev->driver) { - printk(KERN_WARNING "bbswitch: device %s is in use by driver '%s', " - "refusing OFF\n", dev_name(&dis_dev->dev), dis_dev->driver->name); + pr_warn("device %s is in use by driver '%s', refusing OFF\n", + dev_name(&dis_dev->dev), dis_dev->driver->name); return; } - printk(KERN_INFO "bbswitch: disabling discrete graphics\n"); + pr_info("disabling discrete graphics\n"); if (bbswitch_optimus_dsm()) { - printk(KERN_WARNING "bbswitch: Optimus ACPI call failed, the device is" - " not disabled\n"); + pr_warn("Optimus ACPI call failed, the device is not disabled\n"); return; } @@ -240,25 +237,22 @@ static void bbswitch_off(void) { pci_set_power_state(dis_dev, PCI_D3cold); if (bbswitch_acpi_off()) - printk(KERN_WARNING "bbswitch: The discrete card could not be disabled" - " by a _DSM call\n"); + pr_warn("The discrete card could not be disabled by a _DSM call\n"); } static void bbswitch_on(void) { if (!is_card_disabled()) return; - printk(KERN_INFO "bbswitch: enabling discrete graphics\n"); + pr_info("enabling discrete graphics\n"); if (bbswitch_acpi_on()) - printk(KERN_WARNING "bbswitch: The discrete card could not be enabled" - " by a _DSM call\n"); + pr_warn("The discrete card could not be enabled by a _DSM call\n"); pci_set_power_state(dis_dev, PCI_D0); pci_restore_state(dis_dev); if (pci_enable_device(dis_dev)) - printk(KERN_WARNING "bbswitch: failed to enable %s\n", - dev_name(&dis_dev->dev)); + pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev)); pci_set_master(dis_dev); } @@ -332,7 +326,7 @@ 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); + pr_info("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 }; @@ -345,8 +339,8 @@ static int __init bbswitch_init(void) { handle = DEVICE_ACPI_HANDLE(&pdev->dev); if (!handle) { - printk(KERN_WARNING "bbswitch: cannot find ACPI handle for VGA" - " device %s\n", dev_name(&pdev->dev)); + pr_warn("cannot find ACPI handle for VGA device %s\n", + dev_name(&pdev->dev)); continue; } @@ -354,45 +348,45 @@ static int __init bbswitch_init(void) { if (pdev->vendor == PCI_VENDOR_ID_INTEL) { 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); } else { dis_dev = pdev; 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); } kfree(buf.pointer); } if (dis_dev == NULL) { - printk(KERN_ERR "bbswitch: No discrete VGA device found\n"); + pr_err("No discrete VGA device found\n"); 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"); + pr_info("detected an Optimus _DSM 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"); + pr_info("detected a nVidia _DSM function\n"); } else { /* At least two Acer machines are known to use the intel ACPI handle * with the legacy nvidia DSM */ dis_handle = igd_handle; if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) { 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"); } else { - printk(KERN_ERR "bbswitch: No suitable _DSM call found.\n"); + pr_err("No suitable _DSM call found.\n"); return -ENODEV; } } 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"); + pr_err("Couldn't create proc entry\n"); return -ENOMEM; } @@ -401,7 +395,7 @@ static int __init bbswitch_init(void) { else if (load_state == CARD_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"); nb.notifier_call = &bbswitch_pm_handler; @@ -418,7 +412,7 @@ static void __exit bbswitch_exit(void) { else if (unload_state == CARD_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"); if (nb.notifier_call) From 7f9fb7adf900eb260f06e6ce0d89e80c268e563a Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 19 Oct 2012 17:28:59 +0200 Subject: [PATCH 5/8] 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. --- bbswitch.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/bbswitch.c b/bbswitch.c index 4d61875..62c911b 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -17,6 +17,7 @@ #include #include #include +#include #define BBSWITCH_VERSION "0.4.2" @@ -256,6 +257,17 @@ static void bbswitch_on(void) { 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, size_t len, loff_t *off) { char cmd[8]; @@ -266,19 +278,25 @@ static ssize_t bbswitch_proc_write(struct file *fp, const char __user *buff, if (copy_from_user(cmd, buff, len)) return -EFAULT; + dis_dev_get(); + if (strncmp(cmd, "OFF", 3) == 0) bbswitch_off(); if (strncmp(cmd, "ON", 2) == 0) bbswitch_on(); + dis_dev_put(); + return len; } static int bbswitch_proc_show(struct seq_file *seqfp, void *p) { // 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), is_card_disabled() ? "OFF" : "ON"); + dis_dev_put(); return 0; } static int bbswitch_proc_open(struct inode *inode, struct file *file) { @@ -290,19 +308,24 @@ static int bbswitch_pm_handler(struct notifier_block *nbp, switch (event_type) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: + dis_dev_get(); dis_before_suspend_disabled = is_card_disabled(); // enable the device before suspend to avoid the PCI config space from // being saved incorrectly if (dis_before_suspend_disabled) bbswitch_on(); + dis_dev_put(); break; case PM_POST_HIBERNATION: case PM_POST_SUSPEND: case PM_POST_RESTORE: // after suspend, the card is on, but if it was off before suspend, // disable it again - if (dis_before_suspend_disabled) + if (dis_before_suspend_disabled) { + dis_dev_get(); bbswitch_off(); + dis_dev_put(); + } break; case PM_RESTORE_PREPARE: // deliberately don't do anything as it does not occur before suspend @@ -390,6 +413,8 @@ static int __init bbswitch_init(void) { return -ENOMEM; } + dis_dev_get(); + if (load_state == CARD_ON) bbswitch_on(); else if (load_state == CARD_OFF) @@ -398,6 +423,8 @@ static int __init bbswitch_init(void) { pr_info("Succesfully loaded. Discrete card %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on"); + dis_dev_put(); + nb.notifier_call = &bbswitch_pm_handler; register_pm_notifier(&nb); @@ -407,6 +434,8 @@ static int __init bbswitch_init(void) { static void __exit bbswitch_exit(void) { remove_proc_entry("bbswitch", acpi_root_dir); + dis_dev_get(); + if (unload_state == CARD_ON) bbswitch_on(); else if (unload_state == CARD_OFF) @@ -415,6 +444,8 @@ static void __exit bbswitch_exit(void) { pr_info("Unloaded. Discrete card %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on"); + dis_dev_put(); + if (nb.notifier_call) unregister_pm_notifier(&nb); } From da55a7033110d3ac0f3cb08b46d2f393f1e69091 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 19 Oct 2012 17:32:16 +0200 Subject: [PATCH 6/8] Add modeline for vim --- bbswitch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bbswitch.c b/bbswitch.c index 62c911b..f69c1ab 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -452,3 +452,5 @@ static void __exit bbswitch_exit(void) { module_init(bbswitch_init); module_exit(bbswitch_exit); + +/* vim: set sw=4 ts=4 et: */ From 8beb9e3f8810661a68c20276a1fa7f04fd111fc6 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 22 Oct 2012 16:43:17 +0200 Subject: [PATCH 7/8] Add missing 'static' keyword --- bbswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbswitch.c b/bbswitch.c index f69c1ab..3e7e713 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -72,7 +72,7 @@ static acpi_handle dis_handle; /* used for keeping the PM event handler */ static struct notifier_block nb; /* 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, size_t n, char *target) { int i; From b12b017f3af463fd59e81395ae8b47dc539f0caf Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 22 Oct 2012 16:23:35 +0200 Subject: [PATCH 8/8] Update to version 0.5, update NEWS --- NEWS | 5 +++++ bbswitch.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 71252fd..6bdf725 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,9 @@ +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 diff --git a/bbswitch.c b/bbswitch.c index 3e7e713..5420d44 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -19,7 +19,7 @@ #include #include -#define BBSWITCH_VERSION "0.4.2" +#define BBSWITCH_VERSION "0.5" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Toggle the discrete graphics card");