Compare commits

...

45 Commits

Author SHA1 Message Date
Peter Wu 0c38f97c3a Update to version 0.8, update NEWS 11 years ago
Peter Wu 417a592331 Linux 3.13 compatibility
Reported by ArchangeGabriel, original patch by tuxoko (Issue
Bumblebee-Project/bbswitch#75)
11 years ago
Peter Wu 9eb2e0d3fd Merge pull request #71 from TimothyGu/master
README.md: fix indentation of upstart script for enabling graphics on shutdown
11 years ago
Timothy Gu 4a029bc0ce README.md: Fix Markdown 11 years ago
Peter Wu ee0591b9b8 Clarify why Buffer is used instead of Package 11 years ago
Peter Wu c166b27e68 Change license to GPLv2+
The Linux kernel is licensed under GPLv2. As GPLv3 and GPLv2 are incompatible in
some way, just license bbswitch as GPL 2.0 or (at your choice) any latter
version. Note: I wrote all code of bbswith.c, so I am entitled to change the
license and do not need permission from other authors (as there are not any).

Request made by Ludwig Nussel.
12 years ago
Peter Wu c7dbd6babb Update to version 0.7, update NEWS 12 years ago
Peter Wu 2822d1630c README: mention kmod, workaround for T410 (Fixes GH-58) 12 years ago
Peter Wu c665a74def Fix WARNING on Linux 3.9 (Closes GH-55)
Patch by amonakov. This stops the following error:

    bbswitch: disabling discrete graphics
    ------------[ cut here ]------------
    WARNING: at drivers/pci/pci.c:1393 pci_disable_device+0x84/0x90()
    Hardware name: B7130
    [44B blob data]
    Modules linked in:
     bbswitch(O+) ip_set_hash_ip xt_set ip_set nfnetlink ipt_REJECT xt_recent xt_tcpudp xt_owner nf_conntrack_ipv6 nf_defrag
    Pid: 2653, comm: modprobe Tainted: G           O 3.9.1-1-custom #1
    Call Trace:
     [<ffffffff8103fe60>] warn_slowpath_common+0x70/0xa0
     [<ffffffff8103fedc>] warn_slowpath_fmt+0x4c/0x50
     [<ffffffff8130712c>] ? pci_bus_write_config_word+0x1c/0x20
     [<ffffffff8130d794>] pci_disable_device+0x84/0x90
     [<ffffffffa05b21f6>] bbswitch_off+0xa6/0x1d0 [bbswitch]
     [<ffffffffa0104248>] bbswitch_init+0x248/0x1000 [bbswitch]
     [<ffffffffa0104000>] ? 0xffffffffa0103fff
     [<ffffffff810002da>] do_one_initcall+0x10a/0x160
     [<ffffffff810a8720>] load_module+0x1d40/0x24a0
     [<ffffffff810a4410>] ? store_uevent+0x40/0x40
     [<ffffffff810a8fce>] sys_finit_module+0x7e/0x80
     [<ffffffff815afcc6>] system_call_fastpath+0x1a/0x1f
    ---[ end trace 1b312a60aa1b0ed6 ]---
    pci 0000:01:00.0: Refused to change power state, currently in D0
12 years ago
Bruno Pagani e21cdd7b3f Update REAMDE.md
Closes GH-28
12 years ago
Peter Wu f982a09546 Print path to DSM method on execution failure 12 years ago
Peter Wu 3e50c7b118 Do not check for nvidia DSM on igd handle
Just in case the integrated video ACPI handle cannot be found. This is a safety
net for those who try bbswitch on an incompatible machine. I do not know any of
such machinees, but it looks good.
12 years ago
Peter Wu 91814c63f1 Add copyright header (GH-50) 12 years ago
Peter Wu 7361046b24 Update to version 0.6, update NEWS 12 years ago
Peter Wu 2808cf520e README: update module options help 12 years ago
Peter Wu d90350ced1 Clarify license (GPLv3+) 12 years ago
Peter Wu a36a216439 grsec compatibility (GH-48)
The notifier_call member of struct notifier_block cannot be modified on
grsec-patched kernels. Set the pointer on compile-time instead.
12 years ago
Peter Wu 5593d95193 Linux 3.8 compatibility hack
Since the following commit, the device would not turn off because the power
state is unknown. Until a fix is released in Linux (or until I know what
better to do), I just assume the power state D0.

commit ddc150f7a33ae0c9cb16eaac3641abc00f56316f
Author: Lv Zheng <lv.zheng@intel.com>
Date:   Fri Nov 16 02:46:28 2012 +0100

    ACPI / PM: Add check preventing transitioning to non-D0 state from D3.

    No power transitioning from D3 state up to a non-D0 state is allowed
    so make acpi_device_set_power() fail and complain if such a transition
    is attempted.

    Signed-off-by: Lv Zheng <lv.zheng@intel.com>
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
12 years ago
Peter Wu fc2f2aa9c7 Add option to disable Optimus DSM (GH-14)
Necessary for one (some?) older laptops like the Lenovo T410s. Usage: put
"options bbswitch skip_optimus_dsm=1" in your modprobe.conf
12 years ago
Peter Wu cdb79257f2 .gitignore: ignore tar.gz and vim 12 years ago
Peter Wu b12b017f3a Update to version 0.5, update NEWS 12 years ago
Peter Wu 2adcb24207 Merge branch 'master' into develop 12 years ago
Peter Wu 8beb9e3f88 Add missing 'static' keyword 12 years ago
Peter Wu da55a70331 Add modeline for vim 12 years ago
Peter Wu 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.
12 years ago
Peter Wu b8a6029692 Convert printk to pr_... macros
For pr_debug outputs on _DSM results, use "make DEBUG=1"
12 years ago
Peter Wu 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.
12 years ago
Peter Wu dbf8aaf8e2 Use D3cold instead of D3hot to reflect real device state 12 years ago
Peter d54dd454b6 Merge pull request #31 from wzzrd/develop
Add install and uninstall targets to Makefile
12 years ago
Maxim Burgerhout 5f2db6c26a Add install and uninstall targets to Makefile
For if you do not want to or are unable to use dkms
12 years ago
Peter Wu 8ada8849f2 Compatibility with older DKMS versions (GH-25) 13 years ago
Lekensteyn 979fbcf552 Merge branch 'develop' 13 years ago
Lekensteyn d12ce73a0d Update to version 0.4.2, update NEWS 13 years ago
Lekensteyn 0d1429e8bb Fix documentation error (Closes GH-16) 13 years ago
Lekensteyn af91008deb Fix formatting of arguments on case of ACPI failure (GH-12) 13 years ago
Lekensteyn e13e6ffc54 Fix null deref when ACPI method call failed (GH-12) 13 years ago
Lekensteyn f034252a13 Don't share buffer for muid/args, it'll be overwritten 13 years ago
Lekensteyn 647e3dddea Buffer overflow by one byte 13 years ago
Lekensteyn 804773eb22 Fix misuse of sizeof which trims arguments/muid (GH-12) 13 years ago
Lekensteyn 7c30e38bd1 Revert "Add support for WMMX method"
This reverts commit 11ddd3469f.
13 years ago
Lekensteyn e321debb5b Revert "Support Optimus machines through WMMX (GH-2)"
This reverts commit 0ec41fa2da.
13 years ago
Lekensteyn f4c2fadaf5 Revert "Load module dependency on make load: wmi"
This reverts commit fd74ba5bd8.
13 years ago
Lekensteyn d5ff9a1fb9 Merge branch 'develop' 13 years ago
Lekensteyn c1dd7140df Merge branch 'develop' 13 years ago
Lekensteyn fd4dabf37e Merge branch 'develop' 13 years ago

2
.gitignore vendored

@ -5,3 +5,5 @@ Module.symvers
*.mod.c *.mod.c
*.o *.o
modules.order modules.order
bbswitch-*.tar.gz
.*.sw*

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

@ -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

@ -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

35
NEWS

@ -1,3 +1,38 @@
Version 0.8 - 5 December 2013
* Compatibility with Linux 3.13.
Version 0.7 - 28 May 2013
* Fixes a scary WARNING on Linux 3.9.
* Improved debug information when an ACPI method cannot be executed.
* Added copyright information, update my name.
Version 0.6 - 19 March 2013
* Add workaround for Linux 3.8 regression (not present in earlier or latter
versions) that prevented the video card from turned off unless a driver was
loaded before.
* Compatibility with grsec patches.
* Add 'skip_optimus_dsm' module option so users of Lenovo T410s can use bbswitch
by setting this option to true.
* Clarify license (GPLv3).
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

@ -22,6 +22,16 @@ detect the correct handle preceding _DSM and has some built-in safeguards:
disabled again if that was the case before suspending. Hibernation should disabled again if that was the case before suspending. Hibernation should
work, but it not tested. work, but it not tested.
Precautionary measure :
- On some machines, turning off the card is permanent and the card does not
reappear on subsequents reboots, which can result into the screen staying
black all the time, including the BIOS screen.
If it occurs, first try to shutdown, unplug power cord, remove battery, wait
30s, then put everything back in and boot. If it's not solved, then the
solution is to reset the BIOS to factory settings. Before executing bbswitch
for the first time, it is therefore recommended to take note of the full key
sequence in the BIOS to do a reset.
Build Build
----- -----
@ -40,8 +50,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 +90,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
@ -94,6 +104,10 @@ same behavior as:
If not explictly set, the default behavior is not to change the power state of If not explictly set, the default behavior is not to change the power state of
the discrete video card which equals to `load_state=-1 unload_state=-1`. the discrete video card which equals to `load_state=-1 unload_state=-1`.
The Lenovo T410 and Lenovo T410s laptops need the module option
`skip_optimus_dsm=1`, otherwise it will detect the wrong methods which result in
the card not being disabled.
### Disable card on boot ### Disable card on boot
These options can be useful to disable the card on boot time. Depending on your These options can be useful to disable the card on boot time. Depending on your
@ -103,10 +117,27 @@ get disabled on boot:
bbswitch load_state=0 bbswitch load_state=0
Users of `kmod` should create `/etc/modprobe.d/bbswitch.conf` containing
`options bbswitch load_state=0` to set the default options. To load the module,
systemd users should create `/etc/modules-load.d/bbswitch.conf` containing
`bbswitch`.
You have to update your initial ramdisk (initrd) for the changes propagate to You have to update your initial ramdisk (initrd) for the changes propagate to
the boot process. On Debian and Ubuntu, this can performed by running the boot process. On Debian and Ubuntu, this can performed by running
`update-initramfs -u` as root. `update-initramfs -u` as root.
### Enable card on shutdown
Some machines do not like the card being disabled at shutdown.
Add the next initscript (`/etc/init/bbswitch.conf`) :
description "Save power by disabling nvidia on Optimus"
author "Lekensteyn <lekensteyn@gmail.com>"
start on runlevel [2345]
stop on runlevel [016]
pre-start exec /sbin/modprobe bbswitch load_state=0 unload_state=1
pre-stop exec /sbin/rmmod bbswitch
Reporting bugs Reporting bugs
-------------- --------------

@ -9,18 +9,38 @@
* Get status * Get status
* # cat /proc/acpi/bbswitch * # cat /proc/acpi/bbswitch
*/ */
/*
* Copyright (C) 2011-2013 Bumblebee Project
* Author: Peter Wu <lekensteyn@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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.8"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Toggle the discrete graphics card"); MODULE_DESCRIPTION("Toggle the discrete graphics card");
MODULE_AUTHOR("Peter Lekensteyn <lekensteyn@gmail.com>"); MODULE_AUTHOR("Peter Wu <lekensteyn@gmail.com>");
MODULE_VERSION(BBSWITCH_VERSION); MODULE_VERSION(BBSWITCH_VERSION);
enum { enum {
@ -35,6 +55,9 @@ module_param(load_state, int, 0400);
static int unload_state = CARD_UNCHANGED; static int unload_state = CARD_UNCHANGED;
MODULE_PARM_DESC(unload_state, "Card state on unload (0 = off, 1 = on, -1 = unchanged)"); MODULE_PARM_DESC(unload_state, "Card state on unload (0 = off, 1 = on, -1 = unchanged)");
module_param(unload_state, int, 0600); module_param(unload_state, int, 0600);
static bool skip_optimus_dsm = false;
MODULE_PARM_DESC(skip_optimus_dsm, "Skip probe of Optimus discrete DSM (default = false)");
module_param(skip_optimus_dsm, bool, 0400);
extern struct proc_dir_entry *acpi_root_dir; extern struct proc_dir_entry *acpi_root_dir;
@ -43,8 +66,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,43 +81,25 @@ 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;
/* 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 */ /* 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 +108,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;
@ -116,6 +120,9 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid,
params[1].integer.value = revid; params[1].integer.value = revid;
params[2].type = ACPI_TYPE_INTEGER; params[2].type = ACPI_TYPE_INTEGER;
params[2].integer.value = func; params[2].integer.value = func;
/* Although the ACPI spec defines Arg3 as a Package, in practise
* implementations expect a Buffer (CreateWordField and Index functions are
* applied to it). */
params[3].type = ACPI_TYPE_BUFFER; params[3].type = ACPI_TYPE_BUFFER;
params[3].buffer.length = 4; params[3].buffer.length = 4;
if (args) { if (args) {
@ -128,20 +135,20 @@ 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))]; struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
char muid_str[5 * 16];
char args_str[5 * 4];
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buf);
printk(KERN_WARNING "bbswitch: failed to evaluate _DSM {%s} %X %X" pr_warn("failed to evaluate %s._DSM {%s} 0x%X 0x%X {%s}: %s\n",
" {%s}: %s\n", (char *)buf.pointer,
buffer_to_string(muid, tmp), revid, func, buffer_to_string(muid, 16, muid_str), revid, func,
buffer_to_string(args, tmp), acpi_format_exception(err)); buffer_to_string(args, 4, args_str), 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 +161,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 +182,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 +245,68 @@ 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); do {
struct acpi_device *ad = NULL;
int r;
r = acpi_bus_get_device(dis_handle, &ad);
if (r || !ad) {
pr_warn("Cannot get ACPI device for PCI device\n");
break;
}
if (ad->power.state == ACPI_STATE_UNKNOWN) {
pr_debug("ACPI power state is unknown, forcing D0\n");
ad->power.state = ACPI_STATE_D0;
}
} while (0);
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 +317,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 +347,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
@ -410,12 +383,16 @@ static struct file_operations bbswitch_fops = {
.release= single_release .release= single_release
}; };
static struct notifier_block nb = {
.notifier_call = &bbswitch_pm_handler
};
static int __init bbswitch_init(void) { static int __init bbswitch_init(void) {
struct proc_dir_entry *acpi_entry; struct proc_dir_entry *acpi_entry;
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 };
@ -426,10 +403,16 @@ static int __init bbswitch_init(void) {
pci_class != PCI_CLASS_DISPLAY_3D) pci_class != PCI_CLASS_DISPLAY_3D)
continue; continue;
#ifdef ACPI_HANDLE
/* since Linux 3.8 */
handle = ACPI_HANDLE(&pdev->dev);
#else
/* removed since Linux 3.13 */
handle = DEVICE_ACPI_HANDLE(&pdev->dev); handle = DEVICE_ACPI_HANDLE(&pdev->dev);
#endif
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,67 +420,67 @@ 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 (!skip_optimus_dsm &&
dsm_type = DSM_TYPE_OPTIMUS_WMI; has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) {
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 (dis_handle && 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 (!is_card_disabled()) {
/* We think the card is enabled, so ensure the kernel does as well */
if (pci_enable_device(dis_dev))
pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev));
}
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");
nb.notifier_call = &bbswitch_pm_handler; dis_dev_put();
register_pm_notifier(&nb); register_pm_notifier(&nb);
return 0; return 0;
@ -506,17 +489,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: */

Loading…
Cancel
Save