[
  {
    "path": ".gitignore",
    "content": ".cproject\n.project\n.settings\nModule.symvers\nmodules.order\n*.o\n*~\ncore\n.depend\n.*.cmd\n*.ko\n*.mod\n*.mod.c\n.tmp_versions\n.vscode\n"
  },
  {
    "path": "COPYING",
    "content": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "Makefile",
    "content": "obj-m\t:= src/faustus.o\n\nKERNELDIR ?= /lib/modules/$(shell uname -r)/build\nPWD       := $(shell pwd)\n\nall: default\n\ndefault:\n\t$(MAKE) -C $(KERNELDIR) M=$(PWD) modules\n\ninstall:\n\t$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install\n\nclean:\n\trm -rf src/*.o src/*~ src/.*.cmd src/*.ko src/*.mod.c \\\n\t\t.tmp_versions modules.order Module.symvers\n\ndkmsclean:\n\t@dkms remove faustus/0.1 --all || true\n\t@dkms remove faustus/0.2 --all || true\n\ndkms: dkmsclean\n\tdkms add .\n\tdkms install -m faustus -v 0.2\n\nonboot:\n\techo \"faustus\" > /etc/modules-load.d/faustus.conf\n\nnoboot:\n\trm -f /etc/modules-load.d/faustus.conf"
  },
  {
    "path": "README.md",
    "content": "# Faustus Project\n\nExperimental unofficial Linux platform driver module for ASUS TUF Gaming series laptops. \n\nIt is a backport of the asus-wmi / asus-nb-wmi drivers from the mainline + RGB backlight crudely cut-down to be useful for these laptops and packed as a DKMS module for 4.x / 5.x kernels.\n\n**How to decide if I should use it?**\n\n- If your machine does expose keyboard backlight as USB device (you see any devices with the ASUS vendor in output of `lsusb`) then this driver is not for you, check out [CalcProgrammer1/OpenRGB](https://gitlab.com/CalcProgrammer1/OpenRGB), [flukejones/rog-core](https://github.com/flukejones/rog-core), [MidhunSureshR/openauranb](https://github.com/MidhunSureshR/openauranb) or [wroberts/rogauracore](https://github.com/wroberts/rogauracore).\n- If your laptop does not have RGB keyboard backlight and your kernel version is >= 5.3 - everything should work out of the box and there is no reason for you to be using this driver.\n- If you either have RGB keyboard backlight that is managed via WMI, LTS kernel or you want to use features from mainline, you might want to consider trying this out.\n\n## Fair warning\n\n**This is highly experimental and controls the ACPI / WMI responsible for dangerous low-level hardware features (for instance thermal management). So the possibility exists that you could erase data, lock up the system, disable thermal management and set your laptop on fire or worse. So use at your own risk if you know what you are doing.**\n\n## Systems\n\n|Model                   |BIOS        |OS                     |Kernel version              |Notes\n|-                       |-           |-                      |-                           |-\n|FX505GM                 |FX505GM.301 |Ubuntu 18.04.2 LTS     |4.18.0-25-generic           |\n|FX505DD (not tested)    |?           |?                      |                            |\n|FX505DY                 |FX505DY.308 |Arch Linux             |5.1.15-arch1-1-ARCH         |\n|FX705GE                 |?           |?                      |?                           |\n|FX705DY                 |FX705DY.304 |openSUSE Tumbleweed    |5.1.16-1-default            |\n|FX505GD                 |FX505GD.304 |?                      |?                           |\n|FX505DT                 |FX505DT.310 |Arch Linux             |Linux 5.7.5-arch1-1         |\n|                        |FX505DT.304 |Ubuntu 18.04.4/20.04.1 |5.4.0-42-generic            |\n|                        |FX505DT.310 |openSUSE Leap 15.2     |5.3.18-lp152.57-default     |\n|                        |FX505DT.310 |Manjaro                |5.6.16-1-MANJARO            |\n|                        |FX505DT.310 |EndeavourOS            |5.10.7-arch1-1              |\n|FX705DT                 |FX705DT.308 |Arch Linux             |5.6.13-arch1-1              |\n|FX505DU                 |FX505DU.308 |Manjaro 18.1.5         |5.4.13-3-MANJARO            |\n|FX705DU                 |FX705DU.308 |Arch Linux             |5.4.13-zen1-1-zen           |\n|FX705GM                 |FX705GM.305 |Manjaro                |5.3.18-1-MANJARO            |\n|FX505DV                 |FX505DV.303 |Ubuntu 19.10           |5.3.0-29-generic            |\n|FX505GE                 |FX505GE.302 |Manjaro Linux          |4.19.107-1-MANJARO          |\n|FX505GT                 |FX505GT.305 |Ubuntu 20.04           |5.4.0-42-generic            |\n|                        |FX505GT.304 |Manjaro 20.0.3         |5.4.52-1-MANJARO            |\n|                        |FX505GT.305 |Manjaro                |5.4.64-1-MANJARO            |\n|                        |FX505GT.303 |Mint Tricia 19.3       |5.4.0-58-generic            |\n|FA706II                 |FA706II.304 |?                      |?                           |1 (#62)\n|FA706IU                 |FA706IU.315 |?                      |5.11.8-051108-generic       |1, 2 (#62)\n|FX506LI                 |FX506LI.304 |Ubuntu 20.04           |5.4.0-70-generic            |1, 2 (#63)\n\nNotes:\n\n1. Fan mode control is not supported.\n2. RGB hot keys (Fn-Left, Fn-Right) are not functional.\n\nSee \"Contributing\" section for other versions.\n\nTo check your exact model run\n\n```default\nsudo dmidecode | less\n```\n\nand scroll down to check BIOS Information / Version (2nd column) and Base Board Information / Product name (1st column).\n\n## Features\n\n- Additional Fn-X Hotkeys\n- Keyboard backlight intensity\n- Color and mode control for RGB keyboard backlight\n- Fan boost mode switching\n\n## UI\n\n[icodelifee/TUF-Control](https://github.com/icodelifee/TUF-Control)  \nA Keyboard Lighting And Fan Mode Controller GUI App - awesome Electron-based frontend for this driver (WIP).\n\n[CalcProgrammer1/OpenRGB](https://gitlab.com/CalcProgrammer1/OpenRGB)  \nOpen source RGB lighting control that doesn't depend on manufacturer software - supports multiple RGB controllers, including this driver.\n\n[cromer/tuf-manager](https://git.cromer.cl/cromer/tuf-manager)  \nThe software includes 2 different user interfaces, CLI and GUI. It is written in Vala and uses GTK3 for the GUI.\n\n## Installation\n\nHow to: first disable old drivers, then proceed using make to test that it works at all, then install via DKMS permanently and enable on boot.\n\n### Disable original modules\n\nCreate file /etc/modprobe.d/faustus.conf with the following contents and reboot the system.\n\n```\nblacklist asus_wmi\nblacklist asus_nb_wmi\n```\n\nYou could also try unloading the modules instead of reboot before proceeding by issuing:\n\n```\nsudo rmmod asus_nb_wmi\nsudo rmmod asus_wmi\n```\n\nSome reports may suggest that you need to reboot after blacklisting, as the modules fail cleaning up on errors (do if you see AE_ALREADY_ACQUIRED in dmesg).\n\n### Install build dependencies and DKMS\n\n```\n$ sudo apt-get install dkms\n```\n\n### Using make\n\nCompile and load the driver temporarily\n\n```\n$ make\n$ sudo modprobe sparse-keymap\n$ sudo modprobe wmi\n$ sudo modprobe video\n$ sudo insmod src/faustus.ko\n```\n\nand check `dmesg | tail` to verify the driver is loaded and no errors are present.\n\n```\n[ 8295.475755] faustus: DMI checK: FX505GM\n[ 8295.476475] faustus: Initialization: 0x1\n[ 8295.477057] faustus: BIOS WMI version: 8.1\n[ 8295.477680] faustus: SFUN value: 0x4a0061\n[ 8295.477687] faustus faustus: Use DSTS\n[ 8295.477691] faustus faustus: Enable event queue\n[ 8295.490603] input: Asus WMI hotkeys as /devices/platform/faustus/input/input34\n[ 8295.492695] faustus: Number of fans: 1\n```\n\nIf you see:\n\n```\nERROR: could not insert module src/faustus.ko: No such device\n```\n\nit most likely means that your system is not in the \"Systems\" list above and not in the DMI table. This is not a bug and does not necessarily mean that the module does not work, but as there is no evidence that it does work on your system, it will fail fast with the above error message (see \"Contributing\" section below for bypassing the check if you feel adventurous).\n\nCheck that everything works, the system is stable. Also try unloading the driver with\n\n```\n$ sudo rmmod faustus\n```\n\nand inserting it back.\n\n### Using DKMS\n\n```\n$ make dkms\n```\n\nThe source code will probably be installed in `/usr/src/faustus-<version>/` and the module itself will be compiled and installed in the current kernel module directory `/lib/modules/...`. It should also be automatically rebuilt when the kernel is upgraded.\n\nNext, try to load the module\n```\n$ sudo modprobe faustus\n```\n\nTo uninstall the DKMS module execute\n```\n$ sudo make dkmsclean\n```\nor\n```\n$ sudo dkms remove faustus/<version> --all\n```\n\nNOTE: The DKMS install does work with secure boot on Ubuntu 18.04.\n\n### Load on boot\nOn Ubuntu execute \n```\n$ sudo make onboot\n```\nor add it to your config files in the other way. Revert with\n```\n$ sudo make noboot\n```\n\nThis is OS dependent, check the internet on how to do it right.\n\n## Usage\n\n### Keyboard backlight intensity\nIs exposed via ledclass device `/sys/class/leds/asus::kbd_backlight` takes values 0 to 3. The driver changes brightness by itself when hotkeys are pressed.\n\n### RGB backlight\n\nTLDR: Run the `./set_rgb.sh` script as root.\n\nNOTE: The interface will most definitely switch to LED subsystem when submitted to mainline. This here is sort of hack.\n\nDriver exposes sysfs attributes in `/sys/devices/platform/faustus/kbbl/`. You have to write all the parameters and then write 1 to `kbbl_set` to write them permanently or 2 to write them temporarily (the settings will reset on restart or hibernation). \n\nThe list of settings is:\n* `kbbl_red` - red component in hex [00 - ff]\n* `kbbl_green` - green component in hex [00 - ff]\n* `kbbl_blue` - blue component in hex [00 - ff]\n* `kbbl_mode` - mode: \n  - 0 - static color\n  - 1 - breathing\n  - 2 - color cycle (the color component parameters have no effect)\n  - 3 - strobe (epileptic mode, speed parameter has no effect)\n* `kbbl_speed` - speed for modes 1 and 2:\n  - 0 - slow\n  - 1 - medium\n  - 2 - fast\n* `kbbl_flags` - enable flags (must be ORed to get the value), use 2a or ff to set all\n  - 02 - on boot (before module load) \n  - 08 - awake \n  - 20 - sleep \n  - 80? - should be logically shutdown, but I have genuinely no idea what it does\n\n### Fan mode\n\nIs controlled by default by the driver itself when `Fn-F5` is pressed switching three modes:\n\n- 0 - normal\n- 1 - overboost\n- 2 - silent\n\nThere are two mode files available depending on the laptop model:\n\n- `/sys/devices/platform/faustus/fan_boost_mode`\n- `/sys/devices/platform/faustus/throttle_thermal_policy`.\n\nIn case if the `throttle_thermal_policy` is present, it has always all 3 modes available, whereas individual modes of `fan_boost_mode` may or may not be available. The mode will not be preserved on reboot or hibernation.\n\n## Contributing\n\nIf you own a machine of this series from the table above it would be much appreciated if you test the driver and write your feedback (successful and otherwise) in an issue on GitHub.\n\nIf you machine of this series is not in the list, likely it is supported too. You can check the DSDT yourself and try loading the driver without DMI verification by passing `let_it_burn=1`:\n```\nsudo insmod ./src/faustus.ko let_it_burn=1\n```\nand send feedback if it works.\n\n### Information to include in feedback\nAlways OS / kernel version and\n```\n$ sudo dmidecode | grep \"BIOS Inf\\|Board Inf\" -A 3 \nBIOS Information\n\tVendor: American Megatrends Inc.\n\tVersion: FX505GM.301\n\tRelease Date: 09/21/2018\n--\nBase Board Information\n\tManufacturer: ASUSTeK COMPUTER INC.\n\tProduct Name: FX505GM\n\tVersion: 1.0\n```\n\nand additionally for a new model\n```\n$ sudo cat /sys/firmware/acpi/tables/DSDT > dsdt.aml\n```\n\n## Roadmap\nThe patches are in fornext branch except for RGB backlight. This repository will provide usable DKMS version and will be maintained at least until it reaches stable Ubuntu version.\n\n## Disclaimers\n### Trademarks\nASUS Trademark is either a US registered trademark or trademark of ASUSTeK Computer Inc. in the United States and/or other countries. Reference to any ASUS products, services, processes, or other information and/or use of ASUS Trademarks does not constitute or imply endorsement, sponsorship, or recommendation thereof by ASUS.\n\nAll other trademarks are the property of their respective owners.\n\n### Affiliation\nMoreover, ASUS does not participate, authorize, approve, sponsor, support or is affiliated with this project in any way, neither this project with ASUS.\n\n### Epilepsy warning\nThe driver can turn on blinking lights on the laptop that might cause seizures.\n"
  },
  {
    "path": "dkms.conf",
    "content": "PACKAGE_VERSION=\"0.2\"\n\nPACKAGE_NAME=\"faustus\"\nMAKE[0]=\"make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules\"\nCLEAN=\"make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean\"\n\nBUILT_MODULE_NAME[0]=\"faustus\"\nBUILT_MODULE_LOCATION[0]=\"src/\"\nDEST_MODULE_LOCATION[0]=\"/extra\"\n\nREMAKE_INITRD=\"yes\"\nAUTOINSTALL=yes\n"
  },
  {
    "path": "set_rgb.sh",
    "content": "#!/bin/bash\n\n# Red [00 - ff]\necho 33 > /sys/devices/platform/faustus/kbbl/kbbl_red\n# Green [00 - ff]\necho ff > /sys/devices/platform/faustus/kbbl/kbbl_green\n# Blue [00 - ff]\necho 00 > /sys/devices/platform/faustus/kbbl/kbbl_blue\n# Mode: 0 - static color, 1 - breathe, 2 - color cycle, 3 - strobe\necho 0 > /sys/devices/platform/faustus/kbbl/kbbl_mode\n# Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast\necho 0 > /sys/devices/platform/faustus/kbbl/kbbl_speed\n# Enable: 02 - on boot (before module load) | 08 - awake | 20 - sleep (2a or ff to set all)\necho 2a > /sys/devices/platform/faustus/kbbl/kbbl_flags\n# Save: 1 - permanently, 2 - temporarily (reset after reboot)\necho 1 > /sys/devices/platform/faustus/kbbl/kbbl_set\n"
  },
  {
    "path": "src/faustus.c",
    "content": "/*\n * Asus PC WMI hotkey driver\n *\n * Copyright(C) 2010 Intel Corporation.\n * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>\n *\n * Portions based on wistron_btns.c:\n * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>\n * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>\n * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>\n *\n *  This program is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program; if not, write to the Free Software\n *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n */\n\n#define pr_fmt(fmt) KBUILD_MODNAME \": \" fmt\n\n#include <linux/kernel.h>\n#include <linux/module.h>\n#include <linux/init.h>\n#include <linux/types.h>\n#include <linux/slab.h>\n#include <linux/input.h>\n#include <linux/input/sparse-keymap.h>\n#include <linux/fb.h>\n#include <linux/backlight.h>\n#include <linux/leds.h>\n#include <linux/rfkill.h>\n#include <linux/pci.h>\n#include <linux/pci_hotplug.h>\n#include <linux/power_supply.h>\n#include <linux/hwmon.h>\n#include <linux/hwmon-sysfs.h>\n#include <linux/debugfs.h>\n#include <linux/seq_file.h>\n#include <linux/platform_device.h>\n#include <linux/acpi.h>\n#include <linux/dmi.h>\n\n#include <linux/version.h>\n#if LINUX_VERSION_CODE > KERNEL_VERSION(5,6,0)\n#include <linux/units.h>\n#else\n#define ABSOLUTE_ZERO_MILLICELSIUS -273150\n#define MILLIDEGREE_PER_DECIDEGREE 100\n\nstatic inline long milli_kelvin_to_millicelsius(long t)\n{\n\treturn t + ABSOLUTE_ZERO_MILLICELSIUS;\n}\n\nstatic inline long deci_kelvin_to_millicelsius(long t)\n{\n\treturn milli_kelvin_to_millicelsius(t * MILLIDEGREE_PER_DECIDEGREE);\n}\n#endif\n\n#include <acpi/battery.h>\n#include <acpi/video.h>\n\n#include \"faustus.h\"\n\nMODULE_AUTHOR(\"Corentin Chary <corentin.chary@gmail.com>, \"\n\t      \"Yong Wang <yong.y.wang@intel.com>\");\nMODULE_DESCRIPTION(\"Backport of Asus Generic WMI Driver\");\nMODULE_LICENSE(\"GPL\");\n\nstatic bool let_it_burn = 0;\nmodule_param(let_it_burn, bool, 0);\nMODULE_PARM_DESC(let_it_burn, \"Disable DMI check, force load\");\n\nstatic bool report_key_events = 0;\nmodule_param(report_key_events, bool, 0644);\nMODULE_PARM_DESC(report_key_events, \"Forward fan mode key events\");\n\n#define ASUS_WMI_MGMT_GUID\t\"97845ED0-4E6D-11DE-8A39-0800200C9A66\"\n\n#define NOTIFY_BRNUP_MIN\t\t0x11\n#define NOTIFY_BRNUP_MAX\t\t0x1f\n#define NOTIFY_BRNDOWN_MIN\t\t0x20\n#define NOTIFY_BRNDOWN_MAX\t\t0x2e\n#define NOTIFY_FNLOCK_TOGGLE\t\t0x4e\n#define NOTIFY_KBD_DOCK_CHANGE\t\t0x75\n#define NOTIFY_KBD_BRTUP\t\t0xc4\n#define NOTIFY_KBD_BRTDWN\t\t0xc5\n#define NOTIFY_KBD_BRTTOGGLE\t\t0xc7\n#define NOTIFY_KBD_FBM\t\t\t0x99\n#define NOTIFY_KBD_TTP\t\t\t0xae\n#define NOTIFY_LID_FLIP\t\t\t0xfa\n\n#define ASUS_WMI_FNLOCK_BIOS_DISABLED\tBIT(0)\n\n#define ASUS_FAN_DESC\t\t\t\"cpu_fan\"\n#define ASUS_FAN_MFUN\t\t\t0x13\n#define ASUS_FAN_SFUN_READ\t\t0x06\n#define ASUS_FAN_SFUN_WRITE\t\t0x07\n\n/* Based on standard hwmon pwmX_enable values */\n#define ASUS_FAN_CTRL_FULLSPEED\t\t0\n#define ASUS_FAN_CTRL_MANUAL\t\t1\n#define ASUS_FAN_CTRL_AUTO\t\t2\n\n#define ASUS_FAN_BOOST_MODE_NORMAL\t\t0\n#define ASUS_FAN_BOOST_MODE_OVERBOOST\t\t1\n#define ASUS_FAN_BOOST_MODE_OVERBOOST_MASK\t0x01\n#define ASUS_FAN_BOOST_MODE_SILENT\t\t2\n#define ASUS_FAN_BOOST_MODE_SILENT_MASK\t\t0x02\n#define ASUS_FAN_BOOST_MODES_MASK\t\t0x03\n\n#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT\t0\n#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST\t1\n#define ASUS_THROTTLE_THERMAL_POLICY_SILENT\t2\n\n#define USB_INTEL_XUSB2PR\t\t0xD0\n#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI\t0x9c31\n\n#define ASUS_ACPI_UID_ASUSWMI\t\t\"ASUSWMI\"\n#define ASUS_ACPI_UID_ATK\t\t\"ATK\"\n\n#define WMI_EVENT_QUEUE_SIZE\t\t0x10\n#define WMI_EVENT_QUEUE_END\t\t0x1\n#define WMI_EVENT_MASK\t\t\t0xFFFF\n/* The WMI hotkey event value is always the same. */\n#define WMI_EVENT_VALUE_ATK\t\t0xFF\n\n#define WMI_EVENT_MASK\t\t\t0xFFFF\n\nstatic const char * const ashs_ids[] = { \"ATK4001\", \"ATK4002\", NULL };\n\nstatic bool ashs_present(void)\n{\n\tint i = 0;\n\twhile (ashs_ids[i]) {\n\t\tif (acpi_dev_found(ashs_ids[i++]))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstruct bios_args {\n\tu32 arg0;\n\tu32 arg1;\n\tu32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */\n\tu32 arg4;\n\tu32 arg5;\n} __packed;\n\n/*\n * Struct that's used for all methods called via AGFN. Naming is\n * identically to the AML code.\n */\nstruct agfn_args {\n\tu16 mfun; /* probably \"Multi-function\" to be called */\n\tu16 sfun; /* probably \"Sub-function\" to be called */\n\tu16 len;  /* size of the hole struct, including subfunction fields */\n\tu8 stas;  /* not used by now */\n\tu8 err;   /* zero on success */\n} __packed;\n\n/* struct used for calling fan read and write methods */\nstruct agfn_fan_args {\n\tstruct agfn_args agfn;\t/* common fields */\n\tu8 fan;\t\t\t/* fan number: 0: set auto mode 1: 1st fan */\n\tu32 speed;\t\t/* read: RPM/100 - write: 0-255 */\n} __packed;\n\n/*\n * <platform>/    - debugfs root directory\n *   dev_id      - current dev_id\n *   ctrl_param  - current ctrl_param\n *   method_id   - current method_id\n *   devs        - call DEVS(dev_id, ctrl_param) and print result\n *   dsts        - call DSTS(dev_id)  and print result\n *   call        - call method_id(dev_id, ctrl_param) and print result\n */\nstruct asus_wmi_debug {\n\tstruct dentry *root;\n\tu32 method_id;\n\tu32 dev_id;\n\tu32 ctrl_param;\n};\n\nstruct asus_rfkill {\n\tstruct asus_wmi *asus;\n\tstruct rfkill *rfkill;\n\tu32 dev_id;\n};\n\nstruct asus_kbbl_rgb {\n\tu8 kbbl_red;\n\tu8 kbbl_green;\n\tu8 kbbl_blue;\n\tu8 kbbl_mode;\n\tu8 kbbl_speed;\n\n\tu8 kbbl_set_red;\n\tu8 kbbl_set_green;\n\tu8 kbbl_set_blue;\n\tu8 kbbl_set_mode;\n\tu8 kbbl_set_speed;\n\tu8 kbbl_set_flags;\n};\n\nenum fan_type {\n\tFAN_TYPE_NONE = 0,\n\tFAN_TYPE_AGFN,\t\t/* deprecated on newer platforms */\n\tFAN_TYPE_SPEC83,\t/* starting in Spec 8.3, use CPU_FAN_CTRL */\n};\n\nstruct asus_wmi {\n\tint dsts_id;\n\tint spec;\n\tint sfun;\n\tbool wmi_event_queue;\n\n\tstruct input_dev *inputdev;\n\tstruct backlight_device *backlight_device;\n\tstruct platform_device *platform_device;\n\n\tstruct led_classdev wlan_led;\n\tint wlan_led_wk;\n\tstruct led_classdev tpd_led;\n\tint tpd_led_wk;\n\tstruct led_classdev kbd_led;\n\tint kbd_led_wk;\n\tstruct led_classdev lightbar_led;\n\tint lightbar_led_wk;\n\tstruct workqueue_struct *led_workqueue;\n\tstruct work_struct tpd_led_work;\n\tstruct work_struct wlan_led_work;\n\tstruct work_struct lightbar_led_work;\n\n\tstruct asus_rfkill wlan;\n\tstruct asus_rfkill bluetooth;\n\tstruct asus_rfkill wimax;\n\tstruct asus_rfkill wwan3g;\n\tstruct asus_rfkill gps;\n\tstruct asus_rfkill uwb;\n\n\tenum fan_type fan_type;\n\tint fan_pwm_mode;\n\tint agfn_pwm;\n\n\tbool fan_boost_mode_available;\n\tu8 fan_boost_mode_mask;\n\tu8 fan_boost_mode;\n\n\tbool throttle_thermal_policy_available;\n\tu8 throttle_thermal_policy_mode;\n\n\t// The RSOC controls the maximum charging percentage.\n\tbool battery_rsoc_available;\n\n\tbool kbbl_rgb_available;\n\tstruct asus_kbbl_rgb kbbl_rgb;\n\n\tstruct hotplug_slot hotplug_slot;\n\tstruct mutex hotplug_lock;\n\tstruct mutex wmi_lock;\n\tstruct workqueue_struct *hotplug_workqueue;\n\tstruct work_struct hotplug_work;\n\n\tbool fnlock_locked;\n\n\tstruct asus_wmi_debug debug;\n\n\tstruct asus_wmi_driver *driver;\n};\n\n/* WMI ************************************************************************/\n\nstatic int asus_wmi_evaluate_method3(u32 method_id,\n\t\tu32 arg0, u32 arg1, u32 arg2, u32 *retval)\n{\n\tstruct bios_args args = {\n\t\t.arg0 = arg0,\n\t\t.arg1 = arg1,\n\t\t.arg2 = arg2,\n\t};\n\tstruct acpi_buffer input = { (acpi_size) sizeof(args), &args };\n\tstruct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };\n\tacpi_status status;\n\tunion acpi_object *obj;\n\tu32 tmp = 0;\n\n\tstatus = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,\n\t\t\t\t     &input, &output);\n\n\tif (ACPI_FAILURE(status))\n\t\treturn -EIO;\n\n\tobj = (union acpi_object *)output.pointer;\n\tif (obj && obj->type == ACPI_TYPE_INTEGER)\n\t\ttmp = (u32) obj->integer.value;\n\n\tif (retval)\n\t\t*retval = tmp;\n\n\tkfree(obj);\n\n\tif (tmp == ASUS_WMI_UNSUPPORTED_METHOD)\n\t\treturn -ENODEV;\n\n\treturn 0;\n}\n\nstatic int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)\n{\n\treturn asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);\n}\n\nstatic int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)\n{\n\tstruct acpi_buffer input;\n\tu64 phys_addr;\n\tu32 retval;\n\tu32 status;\n\n\t/*\n\t * Copy to dma capable address otherwise memory corruption occurs as\n\t * bios has to be able to access it.\n\t */\n\tinput.pointer = kmemdup(args.pointer, args.length, GFP_DMA | GFP_KERNEL);\n\tinput.length = args.length;\n\tif (!input.pointer)\n\t\treturn -ENOMEM;\n\tphys_addr = virt_to_phys(input.pointer);\n\n\tstatus = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,\n\t\t\t\t\tphys_addr, 0, &retval);\n\tif (!status)\n\t\tmemcpy(args.pointer, input.pointer, args.length);\n\n\tkfree(input.pointer);\n\tif (status)\n\t\treturn -ENXIO;\n\n\treturn retval;\n}\n\nstatic int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)\n{\n\treturn asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);\n}\n\nstatic int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,\n\t\t\t\t u32 *retval)\n{\n\treturn asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,\n\t\t\t\t\tctrl_param, retval);\n}\n\n/* Helper for special devices with magic return codes */\nstatic int asus_wmi_get_devstate_bits(struct asus_wmi *asus,\n\t\t\t\t      u32 dev_id, u32 mask)\n{\n\tu32 retval = 0;\n\tint err;\n\n\terr = asus_wmi_get_devstate(asus, dev_id, &retval);\n\tif (err < 0)\n\t\treturn err;\n\n\tif (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))\n\t\treturn -ENODEV;\n\n\tif (mask == ASUS_WMI_DSTS_STATUS_BIT) {\n\t\tif (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)\n\t\t\treturn -ENODEV;\n\t}\n\n\treturn retval & mask;\n}\n\nstatic int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)\n{\n\treturn asus_wmi_get_devstate_bits(asus, dev_id,\n\t\t\t\t\t  ASUS_WMI_DSTS_STATUS_BIT);\n}\n\nstatic bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)\n{\n\tu32 retval;\n\tint status = asus_wmi_get_devstate(asus, dev_id, &retval);\n\n\treturn status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);\n}\n\n/* Input **********************************************************************/\n\nstatic int asus_wmi_input_init(struct asus_wmi *asus)\n{\n\tint err, result;\n\n\tasus->inputdev = input_allocate_device();\n\tif (!asus->inputdev)\n\t\treturn -ENOMEM;\n\n\tasus->inputdev->name = asus->driver->input_name;\n\tasus->inputdev->phys = asus->driver->input_phys;\n\tasus->inputdev->id.bustype = BUS_HOST;\n\tasus->inputdev->dev.parent = &asus->platform_device->dev;\n\tset_bit(EV_REP, asus->inputdev->evbit);\n\n\terr = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);\n\tif (err)\n\t\tgoto err_free_dev;\n\n\tif (asus->driver->quirks->use_kbd_dock_devid) {\n\t\tresult = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK);\n\t\tif (result >= 0) {\n\t\t\tinput_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);\n\t\t\tinput_report_switch(asus->inputdev, SW_TABLET_MODE, !result);\n\t\t} else if (result != -ENODEV) {\n\t\t\tpr_err(\"Error checking for keyboard-dock: %d\\n\", result);\n\t\t}\n\t}\n\n\tif (asus->driver->quirks->use_lid_flip_devid) {\n\t\tresult = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP);\n\t\tif (result < 0)\n\t\t\tasus->driver->quirks->use_lid_flip_devid = 0;\n\t\tif (result >= 0) {\n\t\t\tinput_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);\n\t\t\tinput_report_switch(asus->inputdev, SW_TABLET_MODE, result);\n\t\t} else if (result == -ENODEV) {\n\t\t\tpr_err(\"This device has lid_flip quirk but got ENODEV checking it. This is a bug.\");\n\t\t} else {\n\t\t\tpr_err(\"Error checking for lid-flip: %d\\n\", result);\n\t\t}\n\t}\n\n\terr = input_register_device(asus->inputdev);\n\tif (err)\n\t\tgoto err_free_dev;\n\n\treturn 0;\n\nerr_free_dev:\n\tinput_free_device(asus->inputdev);\n\treturn err;\n}\n\nstatic void asus_wmi_input_exit(struct asus_wmi *asus)\n{\n\tif (asus->inputdev)\n\t\tinput_unregister_device(asus->inputdev);\n\n\tasus->inputdev = NULL;\n}\n\n/* Tablet mode ****************************************************************/\n\nstatic void lid_flip_tablet_mode_get_state(struct asus_wmi *asus)\n{\n\tint result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP);\n\n\tif (result >= 0) {\n\t\tinput_report_switch(asus->inputdev, SW_TABLET_MODE, result);\n\t\tinput_sync(asus->inputdev);\n\t}\n}\n\n/* Battery ********************************************************************/\n\n/* The battery maximum charging percentage */\nstatic int charge_end_threshold;\n\nstatic ssize_t charge_control_end_threshold_store(struct device *dev,\n\t\t\t\t\t\t  struct device_attribute *attr,\n\t\t\t\t\t\t  const char *buf, size_t count)\n{\n\tint value, ret, rv;\n\n\tret = kstrtouint(buf, 10, &value);\n\tif (ret)\n\t\treturn ret;\n\n\tif (value < 0 || value > 100)\n\t\treturn -EINVAL;\n\n\tret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv);\n\tif (ret)\n\t\treturn ret;\n\n\tif (rv != 1)\n\t\treturn -EIO;\n\n\t/* There isn't any method in the DSDT to read the threshold, so we\n\t * save the threshold.\n\t */\n\tcharge_end_threshold = value;\n\treturn count;\n}\n\nstatic ssize_t charge_control_end_threshold_show(struct device *device,\n\t\t\t\t\t\t struct device_attribute *attr,\n\t\t\t\t\t\t char *buf)\n{\n\treturn sprintf(buf, \"%d\\n\", charge_end_threshold);\n}\n\nstatic DEVICE_ATTR_RW(charge_control_end_threshold);\n\nstatic int asus_wmi_battery_add(struct power_supply *battery)\n{\n\t/* The WMI method does not provide a way to specific a battery, so we\n\t * just assume it is the first battery.\n\t * Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first\n\t * battery is named BATT.\n\t */\n\tif (strcmp(battery->desc->name, \"BAT0\") != 0 &&\n\t    strcmp(battery->desc->name, \"BAT1\") != 0 &&\n\t    strcmp(battery->desc->name, \"BATC\") != 0 &&\n\t    strcmp(battery->desc->name, \"BATT\") != 0)\n\t\treturn -ENODEV;\n\n\tif (device_create_file(&battery->dev,\n\t    &dev_attr_charge_control_end_threshold))\n\t\treturn -ENODEV;\n\n\t/* The charge threshold is only reset when the system is power cycled,\n\t * and we can't get the current threshold so let set it to 100% when\n\t * a battery is added.\n\t */\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL);\n\tcharge_end_threshold = 100;\n\n\treturn 0;\n}\n\nstatic int asus_wmi_battery_remove(struct power_supply *battery)\n{\n\tdevice_remove_file(&battery->dev,\n\t\t\t   &dev_attr_charge_control_end_threshold);\n\treturn 0;\n}\n\nstatic struct acpi_battery_hook battery_hook = {\n\t.add_battery = asus_wmi_battery_add,\n\t.remove_battery = asus_wmi_battery_remove,\n\t.name = \"ASUS Battery Extension\",\n};\n\nstatic void asus_wmi_battery_init(struct asus_wmi *asus)\n{\n\tasus->battery_rsoc_available = false;\n\tif (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_RSOC)) {\n\t\tasus->battery_rsoc_available = true;\n\t\tbattery_hook_register(&battery_hook);\n\t}\n}\n\nstatic void asus_wmi_battery_exit(struct asus_wmi *asus)\n{\n\tif (asus->battery_rsoc_available)\n\t\tbattery_hook_unregister(&battery_hook);\n}\n\n/* LEDs ***********************************************************************/\n\n/*\n * These functions actually update the LED's, and are called from a\n * workqueue. By doing this as separate work rather than when the LED\n * subsystem asks, we avoid messing with the Asus ACPI stuff during a\n * potentially bad time, such as a timer interrupt.\n */\nstatic void tpd_led_update(struct work_struct *work)\n{\n\tint ctrl_param;\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(work, struct asus_wmi, tpd_led_work);\n\n\tctrl_param = asus->tpd_led_wk;\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);\n}\n\nstatic void tpd_led_set(struct led_classdev *led_cdev,\n\t\t\tenum led_brightness value)\n{\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(led_cdev, struct asus_wmi, tpd_led);\n\n\tasus->tpd_led_wk = !!value;\n\tqueue_work(asus->led_workqueue, &asus->tpd_led_work);\n}\n\nstatic int read_tpd_led_state(struct asus_wmi *asus)\n{\n\treturn asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);\n}\n\nstatic enum led_brightness tpd_led_get(struct led_classdev *led_cdev)\n{\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(led_cdev, struct asus_wmi, tpd_led);\n\n\treturn read_tpd_led_state(asus);\n}\n\nstatic void kbd_led_update(struct asus_wmi *asus)\n{\n\tint ctrl_param = 0;\n\n\tctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);\n}\n\nstatic int kbd_led_read(struct asus_wmi *asus, int *level, int *env)\n{\n\tint retval;\n\n\t/*\n\t * bits 0-2: level\n\t * bit 7: light on/off\n\t * bit 8-10: environment (0: dark, 1: normal, 2: light)\n\t * bit 17: status unknown\n\t */\n\tretval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,\n\t\t\t\t\t    0xFFFF);\n\n\t/* Unknown status is considered as off */\n\tif (retval == 0x8000)\n\t\tretval = 0;\n\n\tif (retval < 0)\n\t\treturn retval;\n\n\tif (level)\n\t\t*level = retval & 0x7F;\n\tif (env)\n\t\t*env = (retval >> 8) & 0x7F;\n\treturn 0;\n}\n\nstatic void do_kbd_led_set(struct led_classdev *led_cdev, int value)\n{\n\tstruct asus_wmi *asus;\n\tint max_level;\n\n\tasus = container_of(led_cdev, struct asus_wmi, kbd_led);\n\tmax_level = asus->kbd_led.max_brightness;\n\n\tasus->kbd_led_wk = clamp_val(value, 0, max_level);\n\tkbd_led_update(asus);\n}\n\nstatic void kbd_led_set(struct led_classdev *led_cdev,\n\t\t\tenum led_brightness value)\n{\n\t/* Prevent disabling keyboard backlight on module unregister */\n\tif (led_cdev->flags & LED_UNREGISTERING)\n\t\treturn;\n\n\tdo_kbd_led_set(led_cdev, value);\n}\n\nstatic void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)\n{\n\tstruct led_classdev *led_cdev = &asus->kbd_led;\n\n\tdo_kbd_led_set(led_cdev, value);\n\tled_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);\n}\n\nstatic enum led_brightness kbd_led_get(struct led_classdev *led_cdev)\n{\n\tstruct asus_wmi *asus;\n\tint retval, value;\n\n\tasus = container_of(led_cdev, struct asus_wmi, kbd_led);\n\n\tretval = kbd_led_read(asus, &value, NULL);\n\tif (retval < 0)\n\t\treturn retval;\n\n\treturn value;\n}\n\nstatic int wlan_led_unknown_state(struct asus_wmi *asus)\n{\n\tu32 result;\n\n\tasus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);\n\n\treturn result & ASUS_WMI_DSTS_UNKNOWN_BIT;\n}\n\nstatic void wlan_led_update(struct work_struct *work)\n{\n\tint ctrl_param;\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(work, struct asus_wmi, wlan_led_work);\n\n\tctrl_param = asus->wlan_led_wk;\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);\n}\n\nstatic void wlan_led_set(struct led_classdev *led_cdev,\n\t\t\t enum led_brightness value)\n{\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(led_cdev, struct asus_wmi, wlan_led);\n\n\tasus->wlan_led_wk = !!value;\n\tqueue_work(asus->led_workqueue, &asus->wlan_led_work);\n}\n\nstatic enum led_brightness wlan_led_get(struct led_classdev *led_cdev)\n{\n\tstruct asus_wmi *asus;\n\tu32 result;\n\n\tasus = container_of(led_cdev, struct asus_wmi, wlan_led);\n\tasus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);\n\n\treturn result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;\n}\n\nstatic void lightbar_led_update(struct work_struct *work)\n{\n\tstruct asus_wmi *asus;\n\tint ctrl_param;\n\n\tasus = container_of(work, struct asus_wmi, lightbar_led_work);\n\n\tctrl_param = asus->lightbar_led_wk;\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);\n}\n\nstatic void lightbar_led_set(struct led_classdev *led_cdev,\n\t\t\t     enum led_brightness value)\n{\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(led_cdev, struct asus_wmi, lightbar_led);\n\n\tasus->lightbar_led_wk = !!value;\n\tqueue_work(asus->led_workqueue, &asus->lightbar_led_work);\n}\n\nstatic enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)\n{\n\tstruct asus_wmi *asus;\n\tu32 result;\n\n\tasus = container_of(led_cdev, struct asus_wmi, lightbar_led);\n\tasus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);\n\n\treturn result & ASUS_WMI_DSTS_LIGHTBAR_MASK;\n}\n\nstatic void asus_wmi_led_exit(struct asus_wmi *asus)\n{\n\tled_classdev_unregister(&asus->kbd_led);\n\tled_classdev_unregister(&asus->tpd_led);\n\tled_classdev_unregister(&asus->wlan_led);\n\tled_classdev_unregister(&asus->lightbar_led);\n\n\tif (asus->led_workqueue)\n\t\tdestroy_workqueue(asus->led_workqueue);\n}\n\nstatic int asus_wmi_led_init(struct asus_wmi *asus)\n{\n\tint rv = 0, led_val;\n\n\tasus->led_workqueue = create_singlethread_workqueue(\"led_workqueue\");\n\tif (!asus->led_workqueue)\n\t\treturn -ENOMEM;\n\n\tif (read_tpd_led_state(asus) >= 0) {\n\t\tINIT_WORK(&asus->tpd_led_work, tpd_led_update);\n\n\t\tasus->tpd_led.name = \"asus::touchpad\";\n\t\tasus->tpd_led.brightness_set = tpd_led_set;\n\t\tasus->tpd_led.brightness_get = tpd_led_get;\n\t\tasus->tpd_led.max_brightness = 1;\n\n\t\trv = led_classdev_register(&asus->platform_device->dev,\n\t\t\t\t\t   &asus->tpd_led);\n\t\tif (rv)\n\t\t\tgoto error;\n\t}\n\n\tif (!kbd_led_read(asus, &led_val, NULL)) {\n\t\tasus->kbd_led_wk = led_val;\n\t\tasus->kbd_led.name = \"asus::kbd_backlight\";\n\t\tasus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;\n\t\tasus->kbd_led.brightness_set = kbd_led_set;\n\t\tasus->kbd_led.brightness_get = kbd_led_get;\n\t\tasus->kbd_led.max_brightness = 3;\n\n\t\trv = led_classdev_register(&asus->platform_device->dev,\n\t\t\t\t\t   &asus->kbd_led);\n\t\tif (rv)\n\t\t\tgoto error;\n\t}\n\n\tif (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)\n\t\t\t&& (asus->driver->quirks->wapf > 0)) {\n\t\tINIT_WORK(&asus->wlan_led_work, wlan_led_update);\n\n\t\tasus->wlan_led.name = \"asus::wlan\";\n\t\tasus->wlan_led.brightness_set = wlan_led_set;\n\t\tif (!wlan_led_unknown_state(asus))\n\t\t\tasus->wlan_led.brightness_get = wlan_led_get;\n\t\tasus->wlan_led.flags = LED_CORE_SUSPENDRESUME;\n\t\tasus->wlan_led.max_brightness = 1;\n\t\tasus->wlan_led.default_trigger = \"asus-wlan\";\n\n\t\trv = led_classdev_register(&asus->platform_device->dev,\n\t\t\t\t\t   &asus->wlan_led);\n\t\tif (rv)\n\t\t\tgoto error;\n\t}\n\n\tif (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_LIGHTBAR)) {\n\t\tINIT_WORK(&asus->lightbar_led_work, lightbar_led_update);\n\n\t\tasus->lightbar_led.name = \"asus::lightbar\";\n\t\tasus->lightbar_led.brightness_set = lightbar_led_set;\n\t\tasus->lightbar_led.brightness_get = lightbar_led_get;\n\t\tasus->lightbar_led.max_brightness = 1;\n\n\t\trv = led_classdev_register(&asus->platform_device->dev,\n\t\t\t\t\t   &asus->lightbar_led);\n\t}\n\nerror:\n\tif (rv)\n\t\tasus_wmi_led_exit(asus);\n\n\treturn rv;\n}\n\n/* RGB keyboard backlight *****************************************************/\n\nstatic ssize_t show_u8(u8 value, char *buf)\n{\n\treturn scnprintf(buf, PAGE_SIZE, \"%02x\\n\", value);\n}\n\nstatic ssize_t store_u8(u8 *value, const char *buf, int count)\n{\n\tint err;\n\tu8 result;\n\n\terr = kstrtou8(buf, 16, &result);\n\tif (err < 0) {\n\t\tpr_warn(\"Trying to store invalid value\\n\");\n\t\treturn err;\n\t}\n\n\t*value = result;\n\n\treturn count;\n}\n\nstatic ssize_t kbbl_red_show(struct device *dev, struct device_attribute *attr,\n\t\tchar *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn show_u8(asus->kbbl_rgb.kbbl_red, buf);\n}\n\nstatic ssize_t kbbl_red_store(struct device *dev, struct device_attribute *attr,\n\t\tconst char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn store_u8(&asus->kbbl_rgb.kbbl_set_red, buf, count);\n}\n\nstatic ssize_t kbbl_green_show(struct device *dev,\n\t\tstruct device_attribute *attr, char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn show_u8(asus->kbbl_rgb.kbbl_green, buf);\n}\n\nstatic ssize_t kbbl_green_store(struct device *dev,\n\t\tstruct device_attribute *attr, const char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn store_u8(&asus->kbbl_rgb.kbbl_set_green, buf, count);\n}\n\nstatic ssize_t kbbl_blue_show(struct device *dev, struct device_attribute *attr,\n\t\tchar *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn show_u8(asus->kbbl_rgb.kbbl_blue, buf);\n}\n\nstatic ssize_t kbbl_blue_store(struct device *dev,\n\t\tstruct device_attribute *attr, const char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn store_u8(&asus->kbbl_rgb.kbbl_set_blue, buf, count);\n}\n\nstatic ssize_t kbbl_mode_show(struct device *dev, struct device_attribute *attr,\n\t\tchar *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn show_u8(asus->kbbl_rgb.kbbl_mode, buf);\n}\n\nstatic ssize_t kbbl_mode_store(struct device *dev,\n\t\tstruct device_attribute *attr, const char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn store_u8(&asus->kbbl_rgb.kbbl_set_mode, buf, count);\n}\n\nstatic ssize_t kbbl_speed_show(struct device *dev,\n\t\tstruct device_attribute *attr, char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn show_u8(asus->kbbl_rgb.kbbl_speed, buf);\n}\n\nstatic ssize_t kbbl_speed_store(struct device *dev,\n\t\tstruct device_attribute *attr, const char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn store_u8(&asus->kbbl_rgb.kbbl_set_speed, buf, count);\n}\n\nstatic ssize_t kbbl_flags_show(struct device *dev,\n\t\tstruct device_attribute *attr, char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn show_u8(asus->kbbl_rgb.kbbl_set_flags, buf);\n}\n\nstatic ssize_t kbbl_flags_store(struct device *dev,\n\t\tstruct device_attribute *attr, const char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn store_u8(&asus->kbbl_rgb.kbbl_set_flags, buf, count);\n}\n\nstatic ssize_t kbbl_set_show(struct device *dev,\n\t\tstruct device_attribute *attr, char *buf)\n{\n\treturn scnprintf(buf, PAGE_SIZE,\n\t\t\t\"Write to configure RGB keyboard backlight\\n\");\n}\n\nstatic int kbbl_rgb_write(struct asus_wmi *asus, int persistent)\n{\n\tint err;\n\tu32 retval;\n\tu8 speed_byte;\n\tu8 mode_byte;\n\tu8 speed;\n\tu8 mode;\n\n\tspeed = asus->kbbl_rgb.kbbl_set_speed;\n\tswitch (speed) {\n\tcase 0:\n\tdefault:\n\t\tspeed_byte = 0xe1; // slow\n\t\tspeed = 0;\n\t\tbreak;\n\tcase 1:\n\t\tspeed_byte = 0xeb; // medium\n\t\tbreak;\n\tcase 2:\n\t\tspeed_byte = 0xf5; // fast\n\t\tbreak;\n\t}\n\n\tmode = asus->kbbl_rgb.kbbl_set_mode;\n\tswitch (mode) {\n\tcase 0:\n\tdefault:\n\t\tmode_byte = 0x00; // static color\n\t\tmode = 0;\n\t\tbreak;\n\tcase 1:\n\t\tmode_byte = 0x01; // breathing\n\t\tbreak;\n\tcase 2:\n\t\tmode_byte = 0x02; // color cycle\n\t\tbreak;\n\tcase 3:\n\t\tmode_byte = 0x0a; // strobing\n\t\tbreak;\n\t}\n\n\terr = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS,\n\t\tASUS_WMI_DEVID_KBD_RGB,\n\t\t(persistent ? 0xb4 : 0xb3) |\n\t\t(mode_byte << 8) |\n\t\t(asus->kbbl_rgb.kbbl_set_red << 16) |\n\t\t(asus->kbbl_rgb.kbbl_set_green << 24),\n\t\t(asus->kbbl_rgb.kbbl_set_blue) |\n\t\t(speed_byte << 8), &retval);\n\tif (err) {\n\t\tpr_warn(\"RGB keyboard device 1, write error: %d\\n\", err);\n\t\treturn err;\n\t}\n\n\tif (retval != 1) {\n\t\tpr_warn(\"RGB keyboard device 1, write error (retval): %x\\n\",\n\t\t\t\tretval);\n\t\treturn -EIO;\n\t}\n\n\terr = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS,\n\t\tASUS_WMI_DEVID_KBD_RGB2,\n\t\t(0xbd) |\n\t\t(asus->kbbl_rgb.kbbl_set_flags << 16) |\n\t\t(persistent ? 0x0100 : 0x0000), 0, &retval);\n\tif (err) {\n\t\tpr_warn(\"RGB keyboard device 2, write error: %d\\n\", err);\n\t\treturn err;\n\t}\n\n\tif (retval != 1) {\n\t\tpr_warn(\"RGB keyboard device 2, write error (retval): %x\\n\",\n\t\t\t\tretval);\n\t\treturn -EIO;\n\t}\n\n\tasus->kbbl_rgb.kbbl_red = asus->kbbl_rgb.kbbl_set_red;\n\tasus->kbbl_rgb.kbbl_green = asus->kbbl_rgb.kbbl_set_green;\n\tasus->kbbl_rgb.kbbl_blue = asus->kbbl_rgb.kbbl_set_blue;\n\tasus->kbbl_rgb.kbbl_mode = mode;\n\tasus->kbbl_rgb.kbbl_speed = speed;\n\n\treturn 0;\n}\n\nstatic ssize_t kbbl_set_store(struct device *dev,\n\t\tstruct device_attribute *attr, const char *buf, size_t count)\n{\n\tu8 value;\n\tstruct asus_wmi *asus;\n\tint result;\n\n\tasus = dev_get_drvdata(dev);\n\tresult = store_u8(&value, buf, count);\n\tif (result < 0)\n\t\treturn result;\n\n\tif (value == 1)\n\t\tkbbl_rgb_write(asus, 1);\n\telse if (value == 2)\n\t\tkbbl_rgb_write(asus, 0);\n\n\treturn count;\n}\n\n/* RGB values: 00 .. ff */\nstatic DEVICE_ATTR_RW(kbbl_red);\nstatic DEVICE_ATTR_RW(kbbl_green);\nstatic DEVICE_ATTR_RW(kbbl_blue);\n\n/*\n * Color modes: 0 - static color, 1 - breathing, 2 - color cycle, 3 - strobing\n */\nstatic DEVICE_ATTR_RW(kbbl_mode);\n\n/* Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast */\nstatic DEVICE_ATTR_RW(kbbl_speed);\n\n/*\n * Enable: 02 - on boot (until module load) | 08 - awake | 20 - sleep\n * (2a or ff to enable everything)\n *\n * Logically 80 would be shutdown, but no visible effects of this option\n * were observed so far\n */\nstatic DEVICE_ATTR_RW(kbbl_flags);\n\n/* Write data: 1 - permanently, 2 - temporarily (reset after reboot) */\nstatic DEVICE_ATTR_RW(kbbl_set);\n\nstatic struct attribute *rgbkb_sysfs_attributes[] = {\n\t&dev_attr_kbbl_red.attr,\n\t&dev_attr_kbbl_green.attr,\n\t&dev_attr_kbbl_blue.attr,\n\t&dev_attr_kbbl_mode.attr,\n\t&dev_attr_kbbl_speed.attr,\n\t&dev_attr_kbbl_flags.attr,\n\t&dev_attr_kbbl_set.attr,\n\tNULL,\n};\n\nstatic const struct attribute_group kbbl_attribute_group = {\n\t.name = \"kbbl\",\n\t.attrs = rgbkb_sysfs_attributes\n};\n\nstatic int kbbl_rgb_init(struct asus_wmi *asus)\n{\n\tint err;\n\n\terr = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB);\n\tif (err) {\n\t\tif (err == -ENODEV)\n\t\t\treturn 0;\n\t\telse\n\t\t\treturn err;\n\t}\n\n\terr = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB2);\n\tif (err) {\n\t\tif (err == -ENODEV)\n\t\t\treturn 0;\n\t\telse\n\t\t\treturn err;\n\t}\n\n\tasus->kbbl_rgb_available = true;\n\treturn sysfs_create_group(&asus->platform_device->dev.kobj,\n\t\t\t&kbbl_attribute_group);\n}\n\nstatic void kbbl_rgb_exit(struct asus_wmi *asus)\n{\n\tif (asus->kbbl_rgb_available) {\n\t\tsysfs_remove_group(&asus->platform_device->dev.kobj,\n\t\t\t\t&kbbl_attribute_group);\n\t}\n}\n\n/* RF *************************************************************************/\n\n/*\n * PCI hotplug (for wlan rfkill)\n */\nstatic bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)\n{\n\tint result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);\n\n\tif (result < 0)\n\t\treturn false;\n\treturn !result;\n}\n\nstatic void asus_rfkill_hotplug(struct asus_wmi *asus)\n{\n\tstruct pci_dev *dev;\n\tstruct pci_bus *bus;\n\tbool blocked;\n\tbool absent;\n\tu32 l;\n\n\tmutex_lock(&asus->wmi_lock);\n\tblocked = asus_wlan_rfkill_blocked(asus);\n\tmutex_unlock(&asus->wmi_lock);\n\n\tmutex_lock(&asus->hotplug_lock);\n\tpci_lock_rescan_remove();\n\n\tif (asus->wlan.rfkill)\n\t\trfkill_set_sw_state(asus->wlan.rfkill, blocked);\n\n\tif (asus->hotplug_slot.ops) {\n\t\tbus = pci_find_bus(0, 1);\n\t\tif (!bus) {\n\t\t\tpr_warn(\"Unable to find PCI bus 1?\\n\");\n\t\t\tgoto out_unlock;\n\t\t}\n\n\t\tif (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {\n\t\t\tpr_err(\"Unable to read PCI config space?\\n\");\n\t\t\tgoto out_unlock;\n\t\t}\n\t\tabsent = (l == 0xffffffff);\n\n\t\tif (blocked != absent) {\n\t\t\tpr_warn(\"BIOS says wireless lan is %s, \"\n\t\t\t\t\"but the pci device is %s\\n\",\n\t\t\t\tblocked ? \"blocked\" : \"unblocked\",\n\t\t\t\tabsent ? \"absent\" : \"present\");\n\t\t\tpr_warn(\"skipped wireless hotplug as probably \"\n\t\t\t\t\"inappropriate for this model\\n\");\n\t\t\tgoto out_unlock;\n\t\t}\n\n\t\tif (!blocked) {\n\t\t\tdev = pci_get_slot(bus, 0);\n\t\t\tif (dev) {\n\t\t\t\t/* Device already present */\n\t\t\t\tpci_dev_put(dev);\n\t\t\t\tgoto out_unlock;\n\t\t\t}\n\t\t\tdev = pci_scan_single_device(bus, 0);\n\t\t\tif (dev) {\n\t\t\t\tpci_bus_assign_resources(bus);\n\t\t\t\tpci_bus_add_device(dev);\n\t\t\t}\n\t\t} else {\n\t\t\tdev = pci_get_slot(bus, 0);\n\t\t\tif (dev) {\n\t\t\t\tpci_stop_and_remove_bus_device(dev);\n\t\t\t\tpci_dev_put(dev);\n\t\t\t}\n\t\t}\n\t}\n\nout_unlock:\n\tpci_unlock_rescan_remove();\n\tmutex_unlock(&asus->hotplug_lock);\n}\n\nstatic void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)\n{\n\tstruct asus_wmi *asus = data;\n\n\tif (event != ACPI_NOTIFY_BUS_CHECK)\n\t\treturn;\n\n\t/*\n\t * We can't call directly asus_rfkill_hotplug because most\n\t * of the time WMBC is still being executed and not reetrant.\n\t * There is currently no way to tell ACPICA that  we want this\n\t * method to be serialized, we schedule a asus_rfkill_hotplug\n\t * call later, in a safer context.\n\t */\n\tqueue_work(asus->hotplug_workqueue, &asus->hotplug_work);\n}\n\nstatic int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)\n{\n\tacpi_status status;\n\tacpi_handle handle;\n\n\tstatus = acpi_get_handle(NULL, node, &handle);\n\tif (ACPI_FAILURE(status))\n\t\treturn -ENODEV;\n\n\tstatus = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,\n\t\t\t\t\t     asus_rfkill_notify, asus);\n\tif (ACPI_FAILURE(status))\n\t\tpr_warn(\"Failed to register notify on %s\\n\", node);\n\n\treturn 0;\n}\n\nstatic void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)\n{\n\tacpi_status status = AE_OK;\n\tacpi_handle handle;\n\n\tstatus = acpi_get_handle(NULL, node, &handle);\n\tif (ACPI_FAILURE(status))\n\t\treturn;\n\n\tstatus = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,\n\t\t\t\t\t    asus_rfkill_notify);\n\tif (ACPI_FAILURE(status))\n\t\tpr_err(\"Error removing rfkill notify handler %s\\n\", node);\n}\n\nstatic int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,\n\t\t\t\t   u8 *value)\n{\n\tstruct asus_wmi *asus = container_of(hotplug_slot,\n\t\t\t\t\t     struct asus_wmi, hotplug_slot);\n\tint result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);\n\n\tif (result < 0)\n\t\treturn result;\n\n\t*value = !!result;\n\treturn 0;\n}\n\nstatic const struct hotplug_slot_ops asus_hotplug_slot_ops = {\n\t.get_adapter_status = asus_get_adapter_status,\n\t.get_power_status = asus_get_adapter_status,\n};\n\nstatic void asus_hotplug_work(struct work_struct *work)\n{\n\tstruct asus_wmi *asus;\n\n\tasus = container_of(work, struct asus_wmi, hotplug_work);\n\tasus_rfkill_hotplug(asus);\n}\n\nstatic int asus_setup_pci_hotplug(struct asus_wmi *asus)\n{\n\tint ret = -ENOMEM;\n\tstruct pci_bus *bus = pci_find_bus(0, 1);\n\n\tif (!bus) {\n\t\tpr_err(\"Unable to find wifi PCI bus\\n\");\n\t\treturn -ENODEV;\n\t}\n\n\tasus->hotplug_workqueue =\n\t    create_singlethread_workqueue(\"hotplug_workqueue\");\n\tif (!asus->hotplug_workqueue)\n\t\tgoto error_workqueue;\n\n\tINIT_WORK(&asus->hotplug_work, asus_hotplug_work);\n\n\tasus->hotplug_slot.ops = &asus_hotplug_slot_ops;\n\n\tret = pci_hp_register(&asus->hotplug_slot, bus, 0, \"asus-wifi\");\n\tif (ret) {\n\t\tpr_err(\"Unable to register hotplug slot - %d\\n\", ret);\n\t\tgoto error_register;\n\t}\n\n\treturn 0;\n\nerror_register:\n\tasus->hotplug_slot.ops = NULL;\n\tdestroy_workqueue(asus->hotplug_workqueue);\nerror_workqueue:\n\treturn ret;\n}\n\n/*\n * Rfkill devices\n */\nstatic int asus_rfkill_set(void *data, bool blocked)\n{\n\tstruct asus_rfkill *priv = data;\n\tu32 ctrl_param = !blocked;\n\tu32 dev_id = priv->dev_id;\n\n\t/*\n\t * If the user bit is set, BIOS can't set and record the wlan status,\n\t * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED\n\t * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).\n\t * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED\n\t * while setting the wlan status through WMI.\n\t * This is also the behavior that windows app will do.\n\t */\n\tif ((dev_id == ASUS_WMI_DEVID_WLAN) &&\n\t     priv->asus->driver->wlan_ctrl_by_user)\n\t\tdev_id = ASUS_WMI_DEVID_WLAN_LED;\n\n\treturn asus_wmi_set_devstate(dev_id, ctrl_param, NULL);\n}\n\nstatic void asus_rfkill_query(struct rfkill *rfkill, void *data)\n{\n\tstruct asus_rfkill *priv = data;\n\tint result;\n\n\tresult = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);\n\n\tif (result < 0)\n\t\treturn;\n\n\trfkill_set_sw_state(priv->rfkill, !result);\n}\n\nstatic int asus_rfkill_wlan_set(void *data, bool blocked)\n{\n\tstruct asus_rfkill *priv = data;\n\tstruct asus_wmi *asus = priv->asus;\n\tint ret;\n\n\t/*\n\t * This handler is enabled only if hotplug is enabled.\n\t * In this case, the asus_wmi_set_devstate() will\n\t * trigger a wmi notification and we need to wait\n\t * this call to finish before being able to call\n\t * any wmi method\n\t */\n\tmutex_lock(&asus->wmi_lock);\n\tret = asus_rfkill_set(data, blocked);\n\tmutex_unlock(&asus->wmi_lock);\n\treturn ret;\n}\n\nstatic const struct rfkill_ops asus_rfkill_wlan_ops = {\n\t.set_block = asus_rfkill_wlan_set,\n\t.query = asus_rfkill_query,\n};\n\nstatic const struct rfkill_ops asus_rfkill_ops = {\n\t.set_block = asus_rfkill_set,\n\t.query = asus_rfkill_query,\n};\n\nstatic int asus_new_rfkill(struct asus_wmi *asus,\n\t\t\t   struct asus_rfkill *arfkill,\n\t\t\t   const char *name, enum rfkill_type type, int dev_id)\n{\n\tint result = asus_wmi_get_devstate_simple(asus, dev_id);\n\tstruct rfkill **rfkill = &arfkill->rfkill;\n\n\tif (result < 0)\n\t\treturn result;\n\n\tarfkill->dev_id = dev_id;\n\tarfkill->asus = asus;\n\n\tif (dev_id == ASUS_WMI_DEVID_WLAN &&\n\t    asus->driver->quirks->hotplug_wireless)\n\t\t*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,\n\t\t\t\t       &asus_rfkill_wlan_ops, arfkill);\n\telse\n\t\t*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,\n\t\t\t\t       &asus_rfkill_ops, arfkill);\n\n\tif (!*rfkill)\n\t\treturn -EINVAL;\n\n\tif ((dev_id == ASUS_WMI_DEVID_WLAN) &&\n\t\t\t(asus->driver->quirks->wapf > 0))\n\t\trfkill_set_led_trigger_name(*rfkill, \"asus-wlan\");\n\n\trfkill_init_sw_state(*rfkill, !result);\n\tresult = rfkill_register(*rfkill);\n\tif (result) {\n\t\trfkill_destroy(*rfkill);\n\t\t*rfkill = NULL;\n\t\treturn result;\n\t}\n\treturn 0;\n}\n\nstatic void asus_wmi_rfkill_exit(struct asus_wmi *asus)\n{\n\tif (asus->driver->wlan_ctrl_by_user && ashs_present())\n\t\treturn;\n\n\tasus_unregister_rfkill_notifier(asus, \"\\\\_SB.PCI0.P0P5\");\n\tasus_unregister_rfkill_notifier(asus, \"\\\\_SB.PCI0.P0P6\");\n\tasus_unregister_rfkill_notifier(asus, \"\\\\_SB.PCI0.P0P7\");\n\tif (asus->wlan.rfkill) {\n\t\trfkill_unregister(asus->wlan.rfkill);\n\t\trfkill_destroy(asus->wlan.rfkill);\n\t\tasus->wlan.rfkill = NULL;\n\t}\n\t/*\n\t * Refresh pci hotplug in case the rfkill state was changed after\n\t * asus_unregister_rfkill_notifier()\n\t */\n\tasus_rfkill_hotplug(asus);\n\tif (asus->hotplug_slot.ops)\n\t\tpci_hp_deregister(&asus->hotplug_slot);\n\tif (asus->hotplug_workqueue)\n\t\tdestroy_workqueue(asus->hotplug_workqueue);\n\n\tif (asus->bluetooth.rfkill) {\n\t\trfkill_unregister(asus->bluetooth.rfkill);\n\t\trfkill_destroy(asus->bluetooth.rfkill);\n\t\tasus->bluetooth.rfkill = NULL;\n\t}\n\tif (asus->wimax.rfkill) {\n\t\trfkill_unregister(asus->wimax.rfkill);\n\t\trfkill_destroy(asus->wimax.rfkill);\n\t\tasus->wimax.rfkill = NULL;\n\t}\n\tif (asus->wwan3g.rfkill) {\n\t\trfkill_unregister(asus->wwan3g.rfkill);\n\t\trfkill_destroy(asus->wwan3g.rfkill);\n\t\tasus->wwan3g.rfkill = NULL;\n\t}\n\tif (asus->gps.rfkill) {\n\t\trfkill_unregister(asus->gps.rfkill);\n\t\trfkill_destroy(asus->gps.rfkill);\n\t\tasus->gps.rfkill = NULL;\n\t}\n\tif (asus->uwb.rfkill) {\n\t\trfkill_unregister(asus->uwb.rfkill);\n\t\trfkill_destroy(asus->uwb.rfkill);\n\t\tasus->uwb.rfkill = NULL;\n\t}\n}\n\nstatic int asus_wmi_rfkill_init(struct asus_wmi *asus)\n{\n\tint result = 0;\n\n\tmutex_init(&asus->hotplug_lock);\n\tmutex_init(&asus->wmi_lock);\n\n\tresult = asus_new_rfkill(asus, &asus->wlan, \"asus-wlan\",\n\t\t\t\t RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);\n\n\tif (result && result != -ENODEV)\n\t\tgoto exit;\n\n\tresult = asus_new_rfkill(asus, &asus->bluetooth,\n\t\t\t\t \"asus-bluetooth\", RFKILL_TYPE_BLUETOOTH,\n\t\t\t\t ASUS_WMI_DEVID_BLUETOOTH);\n\n\tif (result && result != -ENODEV)\n\t\tgoto exit;\n\n\tresult = asus_new_rfkill(asus, &asus->wimax, \"asus-wimax\",\n\t\t\t\t RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);\n\n\tif (result && result != -ENODEV)\n\t\tgoto exit;\n\n\tresult = asus_new_rfkill(asus, &asus->wwan3g, \"asus-wwan3g\",\n\t\t\t\t RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);\n\n\tif (result && result != -ENODEV)\n\t\tgoto exit;\n\n\tresult = asus_new_rfkill(asus, &asus->gps, \"asus-gps\",\n\t\t\t\t RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);\n\n\tif (result && result != -ENODEV)\n\t\tgoto exit;\n\n\tresult = asus_new_rfkill(asus, &asus->uwb, \"asus-uwb\",\n\t\t\t\t RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);\n\n\tif (result && result != -ENODEV)\n\t\tgoto exit;\n\n\tif (!asus->driver->quirks->hotplug_wireless)\n\t\tgoto exit;\n\n\tresult = asus_setup_pci_hotplug(asus);\n\t/*\n\t * If we get -EBUSY then something else is handling the PCI hotplug -\n\t * don't fail in this case\n\t */\n\tif (result == -EBUSY)\n\t\tresult = 0;\n\n\tasus_register_rfkill_notifier(asus, \"\\\\_SB.PCI0.P0P5\");\n\tasus_register_rfkill_notifier(asus, \"\\\\_SB.PCI0.P0P6\");\n\tasus_register_rfkill_notifier(asus, \"\\\\_SB.PCI0.P0P7\");\n\t/*\n\t * Refresh pci hotplug in case the rfkill state was changed during\n\t * setup.\n\t */\n\tasus_rfkill_hotplug(asus);\n\nexit:\n\tif (result && result != -ENODEV)\n\t\tasus_wmi_rfkill_exit(asus);\n\n\tif (result == -ENODEV)\n\t\tresult = 0;\n\n\treturn result;\n}\n\n/* Quirks *********************************************************************/\n\nstatic void asus_wmi_set_xusb2pr(struct asus_wmi *asus)\n{\n\tstruct pci_dev *xhci_pdev;\n\tu32 orig_ports_available;\n\tu32 ports_available = asus->driver->quirks->xusb2pr;\n\n\txhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,\n\t\t\tPCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,\n\t\t\tNULL);\n\n\tif (!xhci_pdev)\n\t\treturn;\n\n\tpci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,\n\t\t\t\t&orig_ports_available);\n\n\tpci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,\n\t\t\t\tcpu_to_le32(ports_available));\n\n\tpr_info(\"set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\\n\",\n\t\t\torig_ports_available, ports_available);\n}\n\n/*\n * Some devices dont support or have borcken get_als method\n * but still support set method.\n */\nstatic void asus_wmi_set_als(void)\n{\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);\n}\n\n/* Hwmon device ***************************************************************/\n\nstatic int asus_agfn_fan_speed_read(struct asus_wmi *asus, int fan,\n\t\t\t\t\t  int *speed)\n{\n\tstruct agfn_fan_args args = {\n\t\t.agfn.len = sizeof(args),\n\t\t.agfn.mfun = ASUS_FAN_MFUN,\n\t\t.agfn.sfun = ASUS_FAN_SFUN_READ,\n\t\t.fan = fan,\n\t\t.speed = 0,\n\t};\n\tstruct acpi_buffer input = { (acpi_size) sizeof(args), &args };\n\tint status;\n\n\tif (fan != 1)\n\t\treturn -EINVAL;\n\n\tstatus = asus_wmi_evaluate_method_agfn(input);\n\n\tif (status || args.agfn.err)\n\t\treturn -ENXIO;\n\n\tif (speed)\n\t\t*speed = args.speed;\n\n\treturn 0;\n}\n\nstatic int asus_agfn_fan_speed_write(struct asus_wmi *asus, int fan,\n\t\t\t\t     int *speed)\n{\n\tstruct agfn_fan_args args = {\n\t\t.agfn.len = sizeof(args),\n\t\t.agfn.mfun = ASUS_FAN_MFUN,\n\t\t.agfn.sfun = ASUS_FAN_SFUN_WRITE,\n\t\t.fan = fan,\n\t\t.speed = speed ?  *speed : 0,\n\t};\n\tstruct acpi_buffer input = { (acpi_size) sizeof(args), &args };\n\tint status;\n\n\t/* 1: for setting 1st fan's speed 0: setting auto mode */\n\tif (fan != 1 && fan != 0)\n\t\treturn -EINVAL;\n\n\tstatus = asus_wmi_evaluate_method_agfn(input);\n\n\tif (status || args.agfn.err)\n\t\treturn -ENXIO;\n\n\tif (speed && fan == 1)\n\t\tasus->agfn_pwm = *speed;\n\n\treturn 0;\n}\n\n/*\n * Check if we can read the speed of one fan. If true we assume we can also\n * control it.\n */\nstatic bool asus_wmi_has_agfn_fan(struct asus_wmi *asus)\n{\n\tint status;\n\tint speed;\n\tu32 value;\n\n\tstatus = asus_agfn_fan_speed_read(asus, 1, &speed);\n\tif (status != 0)\n\t\treturn false;\n\n\tstatus = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);\n\tif (status != 0)\n\t\treturn false;\n\n\t/*\n\t * We need to find a better way, probably using sfun,\n\t * bits or spec ...\n\t * Currently we disable it if:\n\t * - ASUS_WMI_UNSUPPORTED_METHOD is returned\n\t * - reverved bits are non-zero\n\t * - sfun and presence bit are not set\n\t */\n\treturn !(value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000\n\t\t || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)));\n}\n\nstatic int asus_fan_set_auto(struct asus_wmi *asus)\n{\n\tint status;\n\tu32 retval;\n\n\tswitch (asus->fan_type) {\n\tcase FAN_TYPE_SPEC83:\n\t\tstatus = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,\n\t\t\t\t\t       0, &retval);\n\t\tif (status)\n\t\t\treturn status;\n\n\t\tif (retval != 1)\n\t\t\treturn -EIO;\n\t\tbreak;\n\n\tcase FAN_TYPE_AGFN:\n\t\tstatus = asus_agfn_fan_speed_write(asus, 0, NULL);\n\t\tif (status)\n\t\t\treturn -ENXIO;\n\t\tbreak;\n\n\tdefault:\n\t\treturn -ENXIO;\n\t}\n\n\n\treturn 0;\n}\n\nstatic ssize_t pwm1_show(struct device *dev,\n\t\t\t       struct device_attribute *attr,\n\t\t\t       char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tint err;\n\tint value;\n\n\t/* If we already set a value then just return it */\n\tif (asus->agfn_pwm >= 0)\n\t\treturn sprintf(buf, \"%d\\n\", asus->agfn_pwm);\n\n\t/*\n\t * If we haven't set already set a value through the AGFN interface,\n\t * we read a current value through the (now-deprecated) FAN_CTRL device.\n\t */\n\terr = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);\n\tif (err < 0)\n\t\treturn err;\n\n\tvalue &= 0xFF;\n\n\tif (value == 1) /* Low Speed */\n\t\tvalue = 85;\n\telse if (value == 2)\n\t\tvalue = 170;\n\telse if (value == 3)\n\t\tvalue = 255;\n\telse if (value) {\n\t\tpr_err(\"Unknown fan speed %#x\\n\", value);\n\t\tvalue = -1;\n\t}\n\n\treturn sprintf(buf, \"%d\\n\", value);\n}\n\nstatic ssize_t pwm1_store(struct device *dev,\n\t\t\t\t     struct device_attribute *attr,\n\t\t\t\t     const char *buf, size_t count) {\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tint value;\n\tint state;\n\tint ret;\n\n\tret = kstrtouint(buf, 10, &value);\n\tif (ret)\n\t\treturn ret;\n\n\tvalue = clamp(value, 0, 255);\n\n\tstate = asus_agfn_fan_speed_write(asus, 1, &value);\n\tif (state)\n\t\tpr_warn(\"Setting fan speed failed: %d\\n\", state);\n\telse\n\t\tasus->fan_pwm_mode = ASUS_FAN_CTRL_MANUAL;\n\n\treturn count;\n}\n\nstatic ssize_t fan1_input_show(struct device *dev,\n\t\t\t\t\tstruct device_attribute *attr,\n\t\t\t\t\tchar *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tint value;\n\tint ret;\n\n\tswitch (asus->fan_type) {\n\tcase FAN_TYPE_SPEC83:\n\t\tret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL,\n\t\t\t\t\t    &value);\n\t\tif (ret < 0)\n\t\t\treturn ret;\n\n\t\tvalue &= 0xffff;\n\t\tbreak;\n\n\tcase FAN_TYPE_AGFN:\n\t\t/* no speed readable on manual mode */\n\t\tif (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL)\n\t\t\treturn -ENXIO;\n\n\t\tret = asus_agfn_fan_speed_read(asus, 1, &value);\n\t\tif (ret) {\n\t\t\tpr_warn(\"reading fan speed failed: %d\\n\", ret);\n\t\t\treturn -ENXIO;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\treturn -ENXIO;\n\t}\n\n\treturn sprintf(buf, \"%d\\n\", value < 0 ? -1 : value*100);\n}\n\nstatic ssize_t pwm1_enable_show(struct device *dev,\n\t\t\t\t\t\t struct device_attribute *attr,\n\t\t\t\t\t\t char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\t/*\n\t * Just read back the cached pwm mode.\n\t *\n\t * For the CPU_FAN device, the spec indicates that we should be\n\t * able to read the device status and consult bit 19 to see if we\n\t * are in Full On or Automatic mode. However, this does not work\n\t * in practice on X532FL at least (the bit is always 0) and there's\n\t * also nothing in the DSDT to indicate that this behaviour exists.\n\t */\n\treturn sprintf(buf, \"%d\\n\", asus->fan_pwm_mode);\n}\n\nstatic ssize_t pwm1_enable_store(struct device *dev,\n\t\t\t\t\t\t  struct device_attribute *attr,\n\t\t\t\t\t\t  const char *buf, size_t count)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tint status = 0;\n\tint state;\n\tint value;\n\tint ret;\n\tu32 retval;\n\n\tret = kstrtouint(buf, 10, &state);\n\tif (ret)\n\t\treturn ret;\n\n\tif (asus->fan_type == FAN_TYPE_SPEC83) {\n\t\tswitch (state) { /* standard documented hwmon values */\n\t\tcase ASUS_FAN_CTRL_FULLSPEED:\n\t\t\tvalue = 1;\n\t\t\tbreak;\n\t\tcase ASUS_FAN_CTRL_AUTO:\n\t\t\tvalue = 0;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn -EINVAL;\n\t\t}\n\n\t\tret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,\n\t\t\t\t\t    value, &retval);\n\t\tif (ret)\n\t\t\treturn ret;\n\n\t\tif (retval != 1)\n\t\t\treturn -EIO;\n\t} else if (asus->fan_type == FAN_TYPE_AGFN) {\n\t\tswitch (state) {\n\t\tcase ASUS_FAN_CTRL_MANUAL:\n\t\t\tbreak;\n\n\t\tcase ASUS_FAN_CTRL_AUTO:\n\t\t\tstatus = asus_fan_set_auto(asus);\n\t\t\tif (status)\n\t\t\t\treturn status;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn -EINVAL;\n\t\t}\n\t}\n\n\tasus->fan_pwm_mode = state;\n\treturn count;\n}\n\nstatic ssize_t fan1_label_show(struct device *dev,\n\t\t\t\t\t  struct device_attribute *attr,\n\t\t\t\t\t  char *buf)\n{\n\treturn sprintf(buf, \"%s\\n\", ASUS_FAN_DESC);\n}\n\nstatic ssize_t asus_hwmon_temp1(struct device *dev,\n\t\t\t\tstruct device_attribute *attr,\n\t\t\t\tchar *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tu32 value;\n\tint err;\n\n\terr = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);\n\tif (err < 0)\n\t\treturn err;\n\n\treturn sprintf(buf, \"%ld\\n\",\n\t\t       deci_kelvin_to_millicelsius(value & 0xFFFF));\n}\n\n/* Fan1 */\nstatic DEVICE_ATTR_RW(pwm1);\nstatic DEVICE_ATTR_RW(pwm1_enable);\nstatic DEVICE_ATTR_RO(fan1_input);\nstatic DEVICE_ATTR_RO(fan1_label);\n\n/* Temperature */\nstatic DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);\n\nstatic struct attribute *hwmon_attributes[] = {\n\t&dev_attr_pwm1.attr,\n\t&dev_attr_pwm1_enable.attr,\n\t&dev_attr_fan1_input.attr,\n\t&dev_attr_fan1_label.attr,\n\n\t&dev_attr_temp1_input.attr,\n\tNULL\n};\n\nstatic umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,\n\t\t\t\t\t  struct attribute *attr, int idx)\n{\n\tstruct device *dev = container_of(kobj, struct device, kobj);\n\tstruct asus_wmi *asus = dev_get_drvdata(dev->parent);\n\tu32 value = ASUS_WMI_UNSUPPORTED_METHOD;\n\n\tif (attr == &dev_attr_pwm1.attr) {\n\t\tif (asus->fan_type != FAN_TYPE_AGFN)\n\t\t\treturn 0;\n\t} else if (attr == &dev_attr_fan1_input.attr\n\t    || attr == &dev_attr_fan1_label.attr\n\t    || attr == &dev_attr_pwm1_enable.attr) {\n\t\tif (asus->fan_type == FAN_TYPE_NONE)\n\t\t\treturn 0;\n\t} else if (attr == &dev_attr_temp1_input.attr) {\n\t\tint err = asus_wmi_get_devstate(asus,\n\t\t\t\t\t\tASUS_WMI_DEVID_THERMAL_CTRL,\n\t\t\t\t\t\t&value);\n\n\t\tif (err < 0)\n\t\t\treturn 0; /* can't return negative here */\n\n\t\t/*\n\t\t * If the temperature value in deci-Kelvin is near the absolute\n\t\t * zero temperature, something is clearly wrong\n\t\t */\n\t\tif (value == 0 || value == 1)\n\t\t\treturn 0;\n\t}\n\n\treturn attr->mode;\n}\n\nstatic const struct attribute_group hwmon_attribute_group = {\n\t.is_visible = asus_hwmon_sysfs_is_visible,\n\t.attrs = hwmon_attributes\n};\n__ATTRIBUTE_GROUPS(hwmon_attribute);\n\nstatic int asus_wmi_hwmon_init(struct asus_wmi *asus)\n{\n\tstruct device *dev = &asus->platform_device->dev;\n\tstruct device *hwmon;\n\n\thwmon = devm_hwmon_device_register_with_groups(dev, \"asus\", asus,\n\t\t\thwmon_attribute_groups);\n\n\tif (IS_ERR(hwmon)) {\n\t\tpr_err(\"Could not register asus hwmon device\\n\");\n\t\treturn PTR_ERR(hwmon);\n\t}\n\treturn 0;\n}\n\nstatic int asus_wmi_fan_init(struct asus_wmi *asus)\n{\n\tasus->fan_type = FAN_TYPE_NONE;\n\tasus->agfn_pwm = -1;\n\n\tif (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL))\n\t\tasus->fan_type = FAN_TYPE_SPEC83;\n\telse if (asus_wmi_has_agfn_fan(asus))\n\t\tasus->fan_type = FAN_TYPE_AGFN;\n\n\tif (asus->fan_type == FAN_TYPE_NONE)\n\t\treturn -ENODEV;\n\n\tasus_fan_set_auto(asus);\n\tasus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO;\n\treturn 0;\n}\n\n/* Fan mode *******************************************************************/\n\nstatic int fan_boost_mode_check_present(struct asus_wmi *asus)\n{\n\tu32 result;\n\tint err;\n\n\tasus->fan_boost_mode_available = false;\n\n\terr = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_BOOST_MODE,\n\t\t\t\t    &result);\n\tif (err) {\n\t\tif (err == -ENODEV)\n\t\t\treturn 0;\n\t\telse\n\t\t\treturn err;\n\t}\n\n\tif ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&\n\t\t\t(result & ASUS_FAN_BOOST_MODES_MASK)) {\n\t\tasus->fan_boost_mode_available = true;\n\t\tasus->fan_boost_mode_mask = result & ASUS_FAN_BOOST_MODES_MASK;\n\t}\n\n\treturn 0;\n}\n\nstatic int fan_boost_mode_write(struct asus_wmi *asus)\n{\n\tint err;\n\tu8 value;\n\tu32 retval;\n\n\tvalue = asus->fan_boost_mode;\n\n\tpr_info(\"Set fan boost mode: %u\\n\", value);\n\terr = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value,\n\t\t\t\t    &retval);\n\n\tsysfs_notify(&asus->platform_device->dev.kobj, NULL,\n\t\t\t\"fan_boost_mode\");\n\n\tif (err) {\n\t\tpr_warn(\"Failed to set fan boost mode: %d\\n\", err);\n\t\treturn err;\n\t}\n\n\tif (retval != 1) {\n\t\tpr_warn(\"Failed to set fan boost mode (retval): 0x%x\\n\",\n\t\t\tretval);\n\t\treturn -EIO;\n\t}\n\n\treturn 0;\n}\n\nstatic int fan_boost_mode_switch_next(struct asus_wmi *asus)\n{\n\tu8 mask = asus->fan_boost_mode_mask;\n\n\tif (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_NORMAL) {\n\t\tif (mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK)\n\t\t\tasus->fan_boost_mode = ASUS_FAN_BOOST_MODE_OVERBOOST;\n\t\telse if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)\n\t\t\tasus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT;\n\t} else if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) {\n\t\tif (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)\n\t\t\tasus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT;\n\t\telse\n\t\t\tasus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL;\n\t} else {\n\t\tasus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL;\n\t}\n\n\treturn fan_boost_mode_write(asus);\n}\n\nstatic ssize_t fan_boost_mode_show(struct device *dev,\n\t\t\t\t   struct device_attribute *attr, char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\treturn scnprintf(buf, PAGE_SIZE, \"%d\\n\", asus->fan_boost_mode);\n}\n\nstatic ssize_t fan_boost_mode_store(struct device *dev,\n\t\t\t\t    struct device_attribute *attr,\n\t\t\t\t    const char *buf, size_t count)\n{\n\tu8 new_mode;\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tu8 mask = asus->fan_boost_mode_mask;\n\n\tint successfully_converted = kstrtou8(buf, 10, &new_mode);\n\tif (successfully_converted < 0) {\n\t\tpr_warn(\"Trying to store invalid value\\n\");\n\t\treturn -EINVAL;\n\t}\n\n\tif (new_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) {\n\t\tif (!(mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK))\n\t\t\treturn -EINVAL;\n\t} else if (new_mode == ASUS_FAN_BOOST_MODE_SILENT) {\n\t\tif (!(mask & ASUS_FAN_BOOST_MODE_SILENT_MASK))\n\t\t\treturn -EINVAL;\n\t} else if (new_mode != ASUS_FAN_BOOST_MODE_NORMAL) {\n\t\treturn -EINVAL;\n\t}\n\n\tasus->fan_boost_mode = new_mode;\n\tfan_boost_mode_write(asus);\n\n\treturn count;\n}\n\n// Fan boost mode: 0 - normal, 1 - overboost, 2 - silent\nstatic DEVICE_ATTR_RW(fan_boost_mode);\n\n/* Throttle thermal policy ****************************************************/\n\nstatic int throttle_thermal_policy_check_present(struct asus_wmi *asus)\n{\n\tu32 result;\n\tint err;\n\n\tasus->throttle_thermal_policy_available = false;\n\n\terr = asus_wmi_get_devstate(asus,\n\t\t\t\t    ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,\n\t\t\t\t    &result);\n\tif (err) {\n\t\tif (err == -ENODEV)\n\t\t\treturn 0;\n\t\treturn err;\n\t}\n\n\tif (result & ASUS_WMI_DSTS_PRESENCE_BIT)\n\t\tasus->throttle_thermal_policy_available = true;\n\n\treturn 0;\n}\n\nstatic int throttle_thermal_policy_write(struct asus_wmi *asus)\n{\n\tint err;\n\tu8 value;\n\tu32 retval;\n\n\tvalue = asus->throttle_thermal_policy_mode;\n\n\terr = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,\n\t\t\t\t    value, &retval);\n\n\tsysfs_notify(&asus->platform_device->dev.kobj, NULL,\n\t\t\t\"throttle_thermal_policy\");\n\n\tif (err) {\n\t\tpr_warn(\"Failed to set throttle thermal policy: %d\\n\", err);\n\t\treturn err;\n\t}\n\n\tif (retval != 1) {\n\t\tpr_warn(\"Failed to set throttle thermal policy (retval): 0x%x\\n\",\n\t\t\tretval);\n\t\treturn -EIO;\n\t}\n\n\treturn 0;\n}\n\nstatic int throttle_thermal_policy_set_default(struct asus_wmi *asus)\n{\n\tif (!asus->throttle_thermal_policy_available)\n\t\treturn 0;\n\n\tasus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;\n\treturn throttle_thermal_policy_write(asus);\n}\n\nstatic int throttle_thermal_policy_switch_next(struct asus_wmi *asus)\n{\n\tu8 new_mode = asus->throttle_thermal_policy_mode + 1;\n\n\tif (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)\n\t\tnew_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;\n\n\tasus->throttle_thermal_policy_mode = new_mode;\n\treturn throttle_thermal_policy_write(asus);\n}\n\nstatic ssize_t throttle_thermal_policy_show(struct device *dev,\n\t\t\t\t   struct device_attribute *attr, char *buf)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tu8 mode = asus->throttle_thermal_policy_mode;\n\n\treturn scnprintf(buf, PAGE_SIZE, \"%d\\n\", mode);\n}\n\nstatic ssize_t throttle_thermal_policy_store(struct device *dev,\n\t\t\t\t    struct device_attribute *attr,\n\t\t\t\t    const char *buf, size_t count)\n{\n\tint result;\n\tu8 new_mode;\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\n\tresult = kstrtou8(buf, 10, &new_mode);\n\tif (result < 0)\n\t\treturn result;\n\n\tif (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)\n\t\treturn -EINVAL;\n\n\tasus->throttle_thermal_policy_mode = new_mode;\n\tthrottle_thermal_policy_write(asus);\n\n\treturn count;\n}\n\n// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent\nstatic DEVICE_ATTR_RW(throttle_thermal_policy);\n\n/* Backlight ******************************************************************/\n\nstatic int read_backlight_power(struct asus_wmi *asus)\n{\n\tint ret;\n\n\tif (asus->driver->quirks->store_backlight_power)\n\t\tret = !asus->driver->panel_power;\n\telse\n\t\tret = asus_wmi_get_devstate_simple(asus,\n\t\t\t\t\t\t   ASUS_WMI_DEVID_BACKLIGHT);\n\n\tif (ret < 0)\n\t\treturn ret;\n\n\treturn ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;\n}\n\nstatic int read_brightness_max(struct asus_wmi *asus)\n{\n\tu32 retval;\n\tint err;\n\n\terr = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);\n\tif (err < 0)\n\t\treturn err;\n\n\tretval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;\n\tretval >>= 8;\n\n\tif (!retval)\n\t\treturn -ENODEV;\n\n\treturn retval;\n}\n\nstatic int read_brightness(struct backlight_device *bd)\n{\n\tstruct asus_wmi *asus = bl_get_data(bd);\n\tu32 retval;\n\tint err;\n\n\terr = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);\n\tif (err < 0)\n\t\treturn err;\n\n\treturn retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;\n}\n\nstatic u32 get_scalar_command(struct backlight_device *bd)\n{\n\tstruct asus_wmi *asus = bl_get_data(bd);\n\tu32 ctrl_param = 0;\n\n\tif ((asus->driver->brightness < bd->props.brightness) ||\n\t    bd->props.brightness == bd->props.max_brightness)\n\t\tctrl_param = 0x00008001;\n\telse if ((asus->driver->brightness > bd->props.brightness) ||\n\t\t bd->props.brightness == 0)\n\t\tctrl_param = 0x00008000;\n\n\tasus->driver->brightness = bd->props.brightness;\n\n\treturn ctrl_param;\n}\n\nstatic int update_bl_status(struct backlight_device *bd)\n{\n\tstruct asus_wmi *asus = bl_get_data(bd);\n\tu32 ctrl_param;\n\tint power, err = 0;\n\n\tpower = read_backlight_power(asus);\n\tif (power != -ENODEV && bd->props.power != power) {\n\t\tctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);\n\t\terr = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,\n\t\t\t\t\t    ctrl_param, NULL);\n\t\tif (asus->driver->quirks->store_backlight_power)\n\t\t\tasus->driver->panel_power = bd->props.power;\n\n\t\t/* When using scalar brightness, updating the brightness\n\t\t * will mess with the backlight power */\n\t\tif (asus->driver->quirks->scalar_panel_brightness)\n\t\t\treturn err;\n\t}\n\n\tif (asus->driver->quirks->scalar_panel_brightness)\n\t\tctrl_param = get_scalar_command(bd);\n\telse\n\t\tctrl_param = bd->props.brightness;\n\n\terr = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,\n\t\t\t\t    ctrl_param, NULL);\n\n\treturn err;\n}\n\nstatic const struct backlight_ops asus_wmi_bl_ops = {\n\t.get_brightness = read_brightness,\n\t.update_status = update_bl_status,\n};\n\nstatic int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)\n{\n\tstruct backlight_device *bd = asus->backlight_device;\n\tint old = bd->props.brightness;\n\tint new = old;\n\n\tif (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)\n\t\tnew = code - NOTIFY_BRNUP_MIN + 1;\n\telse if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)\n\t\tnew = code - NOTIFY_BRNDOWN_MIN;\n\n\tbd->props.brightness = new;\n\tbacklight_update_status(bd);\n\tbacklight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);\n\n\treturn old;\n}\n\nstatic int asus_wmi_backlight_init(struct asus_wmi *asus)\n{\n\tstruct backlight_device *bd;\n\tstruct backlight_properties props;\n\tint max;\n\tint power;\n\n\tmax = read_brightness_max(asus);\n\tif (max < 0)\n\t\treturn max;\n\n\tpower = read_backlight_power(asus);\n\tif (power == -ENODEV)\n\t\tpower = FB_BLANK_UNBLANK;\n\telse if (power < 0)\n\t\treturn power;\n\n\tmemset(&props, 0, sizeof(struct backlight_properties));\n\tprops.type = BACKLIGHT_PLATFORM;\n\tprops.max_brightness = max;\n\tbd = backlight_device_register(asus->driver->name,\n\t\t\t\t       &asus->platform_device->dev, asus,\n\t\t\t\t       &asus_wmi_bl_ops, &props);\n\tif (IS_ERR(bd)) {\n\t\tpr_err(\"Could not register backlight device\\n\");\n\t\treturn PTR_ERR(bd);\n\t}\n\n\tasus->backlight_device = bd;\n\n\tif (asus->driver->quirks->store_backlight_power)\n\t\tasus->driver->panel_power = power;\n\n\tbd->props.brightness = read_brightness(bd);\n\tbd->props.power = power;\n\tbacklight_update_status(bd);\n\n\tasus->driver->brightness = bd->props.brightness;\n\n\treturn 0;\n}\n\nstatic void asus_wmi_backlight_exit(struct asus_wmi *asus)\n{\n\tbacklight_device_unregister(asus->backlight_device);\n\n\tasus->backlight_device = NULL;\n}\n\nstatic int is_display_toggle(int code)\n{\n\t/* display toggle keys */\n\tif ((code >= 0x61 && code <= 0x67) ||\n\t    (code >= 0x8c && code <= 0x93) ||\n\t    (code >= 0xa0 && code <= 0xa7) ||\n\t    (code >= 0xd0 && code <= 0xd5))\n\t\treturn 1;\n\n\treturn 0;\n}\n\n/* Fn-lock ********************************************************************/\n\nstatic bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)\n{\n\tu32 result;\n\n\tasus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FNLOCK, &result);\n\n\treturn (result & ASUS_WMI_DSTS_PRESENCE_BIT) &&\n\t\t!(result & ASUS_WMI_FNLOCK_BIOS_DISABLED);\n}\n\nstatic void asus_wmi_fnlock_update(struct asus_wmi *asus)\n{\n\tint mode = asus->fnlock_locked;\n\n\tasus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);\n}\n\n/* WMI events *****************************************************************/\n\nstatic int asus_wmi_get_event_code(u32 value)\n{\n\tstruct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };\n\tunion acpi_object *obj;\n\tacpi_status status;\n\tint code;\n\n\tstatus = wmi_get_event_data(value, &response);\n\tif (ACPI_FAILURE(status)) {\n\t\tpr_warn(\"Failed to get WMI notify code: %s\\n\",\n\t\t\t\tacpi_format_exception(status));\n\t\treturn -EIO;\n\t}\n\n\tobj = (union acpi_object *)response.pointer;\n\n\tif (obj && obj->type == ACPI_TYPE_INTEGER)\n\t\tcode = (int)(obj->integer.value & WMI_EVENT_MASK);\n\telse\n\t\tcode = -EIO;\n\n\tkfree(obj);\n\treturn code;\n}\n\nstatic void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)\n{\n\tunsigned int key_value = 1;\n\tbool autorelease = 1;\n\tint result, orig_code;\n\n\torig_code = code;\n\n\tif (asus->driver->key_filter) {\n\t\tasus->driver->key_filter(asus->driver, &code, &key_value,\n\t\t\t\t\t &autorelease);\n\t\tif (code == ASUS_WMI_KEY_IGNORE)\n\t\t\treturn;\n\t}\n\n\tif (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)\n\t\tcode = ASUS_WMI_BRN_UP;\n\telse if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)\n\t\tcode = ASUS_WMI_BRN_DOWN;\n\n\tif (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {\n\t\tif (acpi_video_get_backlight_type() == acpi_backlight_vendor) {\n\t\t\tasus_wmi_backlight_notify(asus, orig_code);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (code == NOTIFY_KBD_BRTUP) {\n\t\tkbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);\n\t\treturn;\n\t}\n\tif (code == NOTIFY_KBD_BRTDWN) {\n\t\tkbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);\n\t\treturn;\n\t}\n\tif (code == NOTIFY_KBD_BRTTOGGLE) {\n\t\tif (asus->kbd_led_wk == asus->kbd_led.max_brightness)\n\t\t\tkbd_led_set_by_kbd(asus, 0);\n\t\telse\n\t\t\tkbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);\n\t\treturn;\n\t}\n\n\tif (code == NOTIFY_FNLOCK_TOGGLE) {\n\t\tasus->fnlock_locked = !asus->fnlock_locked;\n\t\tasus_wmi_fnlock_update(asus);\n\t\treturn;\n\t}\n\n\tif (asus->driver->quirks->use_kbd_dock_devid && code == NOTIFY_KBD_DOCK_CHANGE) {\n\t\tresult = asus_wmi_get_devstate_simple(asus,\n\t\t\t\t\t\t      ASUS_WMI_DEVID_KBD_DOCK);\n\t\tif (result >= 0) {\n\t\t\tinput_report_switch(asus->inputdev, SW_TABLET_MODE,\n\t\t\t\t\t    !result);\n\t\t\tinput_sync(asus->inputdev);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (asus->driver->quirks->use_lid_flip_devid && code == NOTIFY_LID_FLIP) {\n\t\tlid_flip_tablet_mode_get_state(asus);\n\t\treturn;\n\t}\n\n\tif (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) {\n\t\tfan_boost_mode_switch_next(asus);\n\t\tif (!report_key_events)\n\t\t\treturn;\n\t}\n\n\tif (asus->throttle_thermal_policy_available && code == NOTIFY_KBD_TTP) {\n\t\tthrottle_thermal_policy_switch_next(asus);\n\t\tif (!report_key_events)\n\t\t\treturn;\n\t}\n\n\tif (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)\n\t\treturn;\n\n\tif (!sparse_keymap_report_event(asus->inputdev, code,\n\t\t\t\t\tkey_value, autorelease))\n\t\tpr_info(\"Unknown key %x pressed\\n\", code);\n}\n\nstatic void asus_wmi_notify(u32 value, void *context)\n{\n\tstruct asus_wmi *asus = context;\n\tint code;\n\tint i;\n\n\tfor (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {\n\t\tcode = asus_wmi_get_event_code(value);\n\t\tif (code < 0) {\n\t\t\tpr_warn(\"Failed to get notify code: %d\\n\", code);\n\t\t\treturn;\n\t\t}\n\n\t\tif (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)\n\t\t\treturn;\n\n\t\tasus_wmi_handle_event_code(code, asus);\n\n\t\t/*\n\t\t * Double check that queue is present:\n\t\t * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.\n\t\t */\n\t\tif (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)\n\t\t\treturn;\n\t}\n\n\tpr_warn(\"Failed to process event queue, last code: 0x%x\\n\", code);\n}\n\nstatic int asus_wmi_notify_queue_flush(struct asus_wmi *asus)\n{\n\tint code;\n\tint i;\n\n\tfor (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {\n\t\tcode = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);\n\t\tif (code < 0) {\n\t\t\tpr_warn(\"Failed to get event during flush: %d\\n\", code);\n\t\t\treturn code;\n\t\t}\n\n\t\tif (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)\n\t\t\treturn 0;\n\t}\n\n\tpr_warn(\"Failed to flush event queue\\n\");\n\treturn -EIO;\n}\n\n/* Sysfs **********************************************************************/\n\nstatic ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,\n\t\t\t     const char *buf, size_t count)\n{\n\tu32 retval;\n\tint err, value;\n\n\tvalue = asus_wmi_get_devstate_simple(asus, devid);\n\tif (value < 0)\n\t\treturn value;\n\n\terr = kstrtoint(buf, 0, &value);\n\tif (err)\n\t\treturn err;\n\n\terr = asus_wmi_set_devstate(devid, value, &retval);\n\tif (err < 0)\n\t\treturn err;\n\n\treturn count;\n}\n\nstatic ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)\n{\n\tint value = asus_wmi_get_devstate_simple(asus, devid);\n\n\tif (value < 0)\n\t\treturn value;\n\n\treturn sprintf(buf, \"%d\\n\", value);\n}\n\n#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm)\t\t\t\\\n\tstatic ssize_t show_##_name(struct device *dev,\t\t\t\\\n\t\t\t\t    struct device_attribute *attr,\t\\\n\t\t\t\t    char *buf)\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\\\n\t\tstruct asus_wmi *asus = dev_get_drvdata(dev);\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\treturn show_sys_wmi(asus, _cm, buf);\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tstatic ssize_t store_##_name(struct device *dev,\t\t\\\n\t\t\t\t     struct device_attribute *attr,\t\\\n\t\t\t\t     const char *buf, size_t count)\t\\\n\t{\t\t\t\t\t\t\t\t\\\n\t\tstruct asus_wmi *asus = dev_get_drvdata(dev);\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\treturn store_sys_wmi(asus, _cm, buf, count);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tstatic struct device_attribute dev_attr_##_name = {\t\t\\\n\t\t.attr = {\t\t\t\t\t\t\\\n\t\t\t.name = __stringify(_name),\t\t\t\\\n\t\t\t.mode = _mode },\t\t\t\t\\\n\t\t.show   = show_##_name,\t\t\t\t\t\\\n\t\t.store  = store_##_name,\t\t\t\t\\\n\t}\n\nASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);\nASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);\nASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);\nASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);\nASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);\n\nstatic ssize_t cpufv_store(struct device *dev, struct device_attribute *attr,\n\t\t\t   const char *buf, size_t count)\n{\n\tint value, rv;\n\n\trv = kstrtoint(buf, 0, &value);\n\tif (rv)\n\t\treturn rv;\n\n\tif (value < 0 || value > 2)\n\t\treturn -EINVAL;\n\n\trv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);\n\tif (rv < 0)\n\t\treturn rv;\n\n\treturn count;\n}\n\nstatic DEVICE_ATTR_WO(cpufv);\n\nstatic struct attribute *platform_attributes[] = {\n\t&dev_attr_cpufv.attr,\n\t&dev_attr_camera.attr,\n\t&dev_attr_cardr.attr,\n\t&dev_attr_touchpad.attr,\n\t&dev_attr_lid_resume.attr,\n\t&dev_attr_als_enable.attr,\n\t&dev_attr_fan_boost_mode.attr,\n\t&dev_attr_throttle_thermal_policy.attr,\n\tNULL\n};\n\nstatic umode_t asus_sysfs_is_visible(struct kobject *kobj,\n\t\t\t\t    struct attribute *attr, int idx)\n{\n\tstruct device *dev = container_of(kobj, struct device, kobj);\n\tstruct asus_wmi *asus = dev_get_drvdata(dev);\n\tbool ok = true;\n\tint devid = -1;\n\n\tif (attr == &dev_attr_camera.attr)\n\t\tdevid = ASUS_WMI_DEVID_CAMERA;\n\telse if (attr == &dev_attr_cardr.attr)\n\t\tdevid = ASUS_WMI_DEVID_CARDREADER;\n\telse if (attr == &dev_attr_touchpad.attr)\n\t\tdevid = ASUS_WMI_DEVID_TOUCHPAD;\n\telse if (attr == &dev_attr_lid_resume.attr)\n\t\tdevid = ASUS_WMI_DEVID_LID_RESUME;\n\telse if (attr == &dev_attr_als_enable.attr)\n\t\tdevid = ASUS_WMI_DEVID_ALS_ENABLE;\n\telse if (attr == &dev_attr_fan_boost_mode.attr)\n\t\tok = asus->fan_boost_mode_available;\n\telse if (attr == &dev_attr_throttle_thermal_policy.attr)\n\t\tok = asus->throttle_thermal_policy_available;\n\n\tif (devid != -1)\n\t\tok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);\n\n\treturn ok ? attr->mode : 0;\n}\n\nstatic const struct attribute_group platform_attribute_group = {\n\t.is_visible = asus_sysfs_is_visible,\n\t.attrs = platform_attributes\n};\n\nstatic void asus_wmi_sysfs_exit(struct platform_device *device)\n{\n\tsysfs_remove_group(&device->dev.kobj, &platform_attribute_group);\n}\n\nstatic int asus_wmi_sysfs_init(struct platform_device *device)\n{\n\treturn sysfs_create_group(&device->dev.kobj, &platform_attribute_group);\n}\n\n/* Platform device ************************************************************/\n\nstatic int asus_wmi_platform_init(struct asus_wmi *asus)\n{\n\tstruct device *dev = &asus->platform_device->dev;\n\tint rv;\n\n\t/* INIT enable hotkeys on some models */\n\tif (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))\n\t\tpr_info(\"Initialization: %#x\\n\", rv);\n\n\t/* We don't know yet what to do with this version... */\n\tif (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {\n\t\tpr_info(\"BIOS WMI version: %d.%d\\n\", rv >> 16, rv & 0xFF);\n\t\tasus->spec = rv;\n\t}\n\n\t/*\n\t * The SFUN method probably allows the original driver to get the list\n\t * of features supported by a given model. For now, 0x0100 or 0x0800\n\t * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.\n\t * The significance of others is yet to be found.\n\t */\n\tif (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {\n\t\tpr_info(\"SFUN value: %#x\\n\", rv);\n\t\tasus->sfun = rv;\n\t}\n\n\t/*\n\t * Eee PC and Notebooks seems to have different method_id for DSTS,\n\t * but it may also be related to the BIOS's SPEC.\n\t * Note, on most Eeepc, there is no way to check if a method exist\n\t * or note, while on notebooks, they returns 0xFFFFFFFE on failure,\n\t * but once again, SPEC may probably be used for that kind of things.\n\t *\n\t * Additionally at least TUF Gaming series laptops return nothing for\n\t * unknown methods, so the detection in this way is not possible.\n\t *\n\t * There is strong indication that only ACPI WMI devices that have _UID\n\t * equal to \"ASUSWMI\" use DCTS whereas those with \"ATK\" use DSTS.\n\t */\n\t// NOTE[backport]: Always use DSTS\n\tdev_info(dev, \"Use DSTS\\n\");\n\tasus->dsts_id = ASUS_WMI_METHODID_DSTS;\n\n\t/*\n\t * Some devices can have multiple event codes stored in a queue before\n\t * the module load if it was unloaded intermittently after calling\n\t * the INIT method (enables event handling). The WMI notify handler is\n\t * expected to retrieve all event codes until a retrieved code equals\n\t * queue end marker (One or Ones). Old codes are flushed from the queue\n\t * upon module load. Not enabling this when it should be has minimal\n\t * visible impact so fall back if anything goes wrong.\n\t */\n\t// NOTE[backport]: Always enable event queue\n\tdev_info(dev, \"Enable event queue\\n\");\n\n\tif (!asus_wmi_notify_queue_flush(asus))\n\t\tasus->wmi_event_queue = true;\n\n\t/* CWAP allow to define the behavior of the Fn+F2 key,\n\t * this method doesn't seems to be present on Eee PCs */\n\tif (asus->driver->quirks->wapf >= 0)\n\t\tasus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,\n\t\t\t\t      asus->driver->quirks->wapf, NULL);\n\n\treturn 0;\n}\n\n/* debugfs ********************************************************************/\n\nstruct asus_wmi_debugfs_node {\n\tstruct asus_wmi *asus;\n\tchar *name;\n\tint (*show) (struct seq_file *m, void *data);\n};\n\nstatic int show_dsts(struct seq_file *m, void *data)\n{\n\tstruct asus_wmi *asus = m->private;\n\tint err;\n\tu32 retval = -1;\n\n\terr = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);\n\tif (err < 0)\n\t\treturn err;\n\n\tseq_printf(m, \"DSTS(%#x) = %#x\\n\", asus->debug.dev_id, retval);\n\n\treturn 0;\n}\n\nstatic int show_devs(struct seq_file *m, void *data)\n{\n\tstruct asus_wmi *asus = m->private;\n\tint err;\n\tu32 retval = -1;\n\n\terr = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,\n\t\t\t\t    &retval);\n\tif (err < 0)\n\t\treturn err;\n\n\tseq_printf(m, \"DEVS(%#x, %#x) = %#x\\n\", asus->debug.dev_id,\n\t\t   asus->debug.ctrl_param, retval);\n\n\treturn 0;\n}\n\nstatic int show_call(struct seq_file *m, void *data)\n{\n\tstruct asus_wmi *asus = m->private;\n\tstruct bios_args args = {\n\t\t.arg0 = asus->debug.dev_id,\n\t\t.arg1 = asus->debug.ctrl_param,\n\t};\n\tstruct acpi_buffer input = { (acpi_size) sizeof(args), &args };\n\tstruct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };\n\tunion acpi_object *obj;\n\tacpi_status status;\n\n\tstatus = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,\n\t\t\t\t     0, asus->debug.method_id,\n\t\t\t\t     &input, &output);\n\n\tif (ACPI_FAILURE(status))\n\t\treturn -EIO;\n\n\tobj = (union acpi_object *)output.pointer;\n\tif (obj && obj->type == ACPI_TYPE_INTEGER)\n\t\tseq_printf(m, \"%#x(%#x, %#x) = %#x\\n\", asus->debug.method_id,\n\t\t\t   asus->debug.dev_id, asus->debug.ctrl_param,\n\t\t\t   (u32) obj->integer.value);\n\telse\n\t\tseq_printf(m, \"%#x(%#x, %#x) = t:%d\\n\", asus->debug.method_id,\n\t\t\t   asus->debug.dev_id, asus->debug.ctrl_param,\n\t\t\t   obj ? obj->type : -1);\n\n\tkfree(obj);\n\n\treturn 0;\n}\n\nstatic struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {\n\t{NULL, \"devs\", show_devs},\n\t{NULL, \"dsts\", show_dsts},\n\t{NULL, \"call\", show_call},\n};\n\nstatic int asus_wmi_debugfs_open(struct inode *inode, struct file *file)\n{\n\tstruct asus_wmi_debugfs_node *node = inode->i_private;\n\n\treturn single_open(file, node->show, node->asus);\n}\n\nstatic const struct file_operations asus_wmi_debugfs_io_ops = {\n\t.owner = THIS_MODULE,\n\t.open = asus_wmi_debugfs_open,\n\t.read = seq_read,\n\t.llseek = seq_lseek,\n\t.release = single_release,\n};\n\nstatic void asus_wmi_debugfs_exit(struct asus_wmi *asus)\n{\n\tdebugfs_remove_recursive(asus->debug.root);\n}\n\nstatic void asus_wmi_debugfs_init(struct asus_wmi *asus)\n{\n\tint i;\n\n\tasus->debug.root = debugfs_create_dir(asus->driver->name, NULL);\n\n\tdebugfs_create_x32(\"method_id\", S_IRUGO | S_IWUSR, asus->debug.root,\n\t\t\t   &asus->debug.method_id);\n\n\tdebugfs_create_x32(\"dev_id\", S_IRUGO | S_IWUSR, asus->debug.root,\n\t\t\t   &asus->debug.dev_id);\n\n\tdebugfs_create_x32(\"ctrl_param\", S_IRUGO | S_IWUSR, asus->debug.root,\n\t\t\t   &asus->debug.ctrl_param);\n\n\tfor (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {\n\t\tstruct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];\n\n\t\tnode->asus = asus;\n\t\tdebugfs_create_file(node->name, S_IFREG | S_IRUGO,\n\t\t\t\t    asus->debug.root, node,\n\t\t\t\t    &asus_wmi_debugfs_io_ops);\n\t}\n}\n\n/* Init / exit ****************************************************************/\n\n/// asus-nb-wmi\n/*\n * Asus Notebooks WMI hotkey driver\n *\n * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>\n *\n *  This program is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program; if not, write to the Free Software\n *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n */\n\n#define ASUS_NB_WMI_EVENT_GUID\t\"0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C\"\n\nstatic const struct key_entry asus_nb_wmi_keymap[] = {\n\t{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },\n\t{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },\n\t{ KE_KEY, 0x30, { KEY_VOLUMEUP } },\n\t{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },\n\t{ KE_KEY, 0x32, { KEY_MUTE } },\n\t{ KE_KEY, 0x35, { KEY_SCREENLOCK } },\n\t{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },\n\t{ KE_KEY, 0x41, { KEY_NEXTSONG } },\n\t{ KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */\n\t{ KE_KEY, 0x45, { KEY_PLAYPAUSE } },\n\t{ KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */\n\t{ KE_KEY, 0x50, { KEY_EMAIL } },\n\t{ KE_KEY, 0x51, { KEY_WWW } },\n\t{ KE_KEY, 0x55, { KEY_CALC } },\n\t{ KE_IGNORE, 0x57, },  /* Battery mode */\n\t{ KE_IGNORE, 0x58, },  /* AC mode */\n\t{ KE_KEY, 0x5C, { KEY_F15 } },  /* Power Gear key */\n\t{ KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */\n\t{ KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */\n\t{ KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */\n\t{ KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },\n\t{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */\n\t{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */\n\t{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */\n\t{ KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */\n\t{ KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */\n\t{ KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */\n\t{ KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */\n\t{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },\n\t{ KE_IGNORE, 0x6E, },  /* Low Battery notification */\n\t{ KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */\n\t{ KE_IGNORE, 0x79, },  /* Charger type dectection notification */\n\t{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */\n\t{ KE_KEY, 0x7c, { KEY_MICMUTE } },\n\t{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */\n\t{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */\n\t{ KE_KEY, 0x82, { KEY_CAMERA } },\n\t{ KE_KEY, 0x88, { KEY_RFKILL  } }, /* Radio Toggle Key */\n\t{ KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */\n\t{ KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */\n\t{ KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */\n\t{ KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */\n\t{ KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */\n\t{ KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */\n\t{ KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */\n\t{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */\n\t{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */\n\t{ KE_KEY, 0x95, { KEY_MEDIA } },\n\t{ KE_KEY, NOTIFY_KBD_FBM, { KEY_FN_F5 } },\n\t//{ KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */\n\t{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */\n\t{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */\n\t{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */\n\t{ KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */\n\t{ KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */\n\t{ KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */\n\t{ KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */\n\t{ KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */\n\t{ KE_KEY, NOTIFY_KBD_TTP, { KEY_FN_F5 } },\n\t{ KE_KEY, 0xB5, { KEY_CALC } },\n\t{ KE_KEY, 0xC4, { KEY_KBDILLUMUP } },\n\t{ KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },\n\t{ KE_IGNORE, 0xC6, },  /* Ambient Light Sensor notification */\n\t{ KE_KEY, 0xFA, { KEY_PROG2 } },           /* Lid flip action */\n\t{ KE_END, 0},\n};\n\nstatic struct quirk_entry quirk_asus_unknown = {\n\t.wapf = 0,\n\t.wmi_backlight_set_devstate = true,\n};\n\nstatic struct asus_wmi_driver asus_nb_wmi_driver = {\n\t.name = KBUILD_MODNAME,\n\t.owner = THIS_MODULE,\n\t.event_guid = ASUS_NB_WMI_EVENT_GUID,\n\t.keymap = asus_nb_wmi_keymap,\n\t.input_name = \"Asus WMI hotkeys\",\n\t.input_phys = KBUILD_MODNAME \"/input0\",\n\t.quirks = &quirk_asus_unknown\n};\n///\n\nstatic int asus_wmi_add(struct platform_device *pdev)\n{\n\tstruct asus_wmi *asus;\n\tconst char *chassis_type;\n\tacpi_status status;\n\tint err;\n\tu32 result;\n\n\tasus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);\n\tif (!asus)\n\t\treturn -ENOMEM;\n\n\tasus->driver = &asus_nb_wmi_driver;\n\tasus->platform_device = pdev;\n\tasus->driver->platform_device = pdev;\n\n\tplatform_set_drvdata(asus->platform_device, asus);\n\n\terr = asus_wmi_platform_init(asus);\n\tif (err)\n\t\tgoto fail_platform;\n\n\terr = fan_boost_mode_check_present(asus);\n\tif (err)\n\t\tgoto fail_fan_boost_mode;\n\n\terr = throttle_thermal_policy_check_present(asus);\n\tif (err)\n\t\tgoto fail_throttle_thermal_policy;\n\telse\n\t\tthrottle_thermal_policy_set_default(asus);\n\n\terr = asus_wmi_sysfs_init(asus->platform_device);\n\tif (err)\n\t\tgoto fail_sysfs;\n\n\terr = asus_wmi_input_init(asus);\n\tif (err)\n\t\tgoto fail_input;\n\n\terr = asus_wmi_fan_init(asus); /* probably no problems on error */\n\n\terr = asus_wmi_hwmon_init(asus);\n\tif (err)\n\t\tgoto fail_hwmon;\n\n\terr = asus_wmi_led_init(asus);\n\tif (err)\n\t\tgoto fail_leds;\n\n\terr = kbbl_rgb_init(asus);\n\tif (err)\n\t\tgoto fail_rgbkb;\n\n\tasus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);\n\tif (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))\n\t\tasus->driver->wlan_ctrl_by_user = 1;\n\n\tif (!(asus->driver->wlan_ctrl_by_user && ashs_present())) {\n\t\terr = asus_wmi_rfkill_init(asus);\n\t\tif (err)\n\t\t\tgoto fail_rfkill;\n\t}\n\n\tif (asus->driver->quirks->wmi_force_als_set)\n\t\tasus_wmi_set_als();\n\n\t/* Some Asus desktop boards export an acpi-video backlight interface,\n\t   stop this from showing up */\n\tchassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);\n\tif (chassis_type && !strcmp(chassis_type, \"3\"))\n\t\tacpi_video_set_dmi_backlight_type(acpi_backlight_vendor);\n\n\tif (asus->driver->quirks->wmi_backlight_power)\n\t\tacpi_video_set_dmi_backlight_type(acpi_backlight_vendor);\n\n\tif (asus->driver->quirks->wmi_backlight_native)\n\t\tacpi_video_set_dmi_backlight_type(acpi_backlight_native);\n\n\tif (asus->driver->quirks->xusb2pr)\n\t\tasus_wmi_set_xusb2pr(asus);\n\n\tif (acpi_video_get_backlight_type() == acpi_backlight_vendor) {\n\t\terr = asus_wmi_backlight_init(asus);\n\t\tif (err && err != -ENODEV)\n\t\t\tgoto fail_backlight;\n\t} else if (asus->driver->quirks->wmi_backlight_set_devstate)\n\t\terr = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL);\n\n\tif (asus_wmi_has_fnlock_key(asus)) {\n\t\tasus->fnlock_locked = true;\n\t\tasus_wmi_fnlock_update(asus);\n\t}\n\n\tstatus = wmi_install_notify_handler(asus->driver->event_guid,\n\t\t\t\t\t    asus_wmi_notify, asus);\n\tif (ACPI_FAILURE(status)) {\n\t\tpr_err(\"Unable to register notify handler - %d\\n\", status);\n\t\terr = -ENODEV;\n\t\tgoto fail_wmi_handler;\n\t}\n\n\tasus_wmi_battery_init(asus);\n\n\tasus_wmi_debugfs_init(asus);\n\n\treturn 0;\n\nfail_wmi_handler:\n\tasus_wmi_backlight_exit(asus);\nfail_backlight:\n\tasus_wmi_rfkill_exit(asus);\nfail_rfkill:\n\tkbbl_rgb_exit(asus);\nfail_rgbkb:\n\tasus_wmi_led_exit(asus);\nfail_leds:\nfail_hwmon:\n\tasus_wmi_input_exit(asus);\nfail_input:\n\tasus_wmi_sysfs_exit(asus->platform_device);\nfail_sysfs:\nfail_throttle_thermal_policy:\nfail_fan_boost_mode:\nfail_platform:\n\tkfree(asus);\n\treturn err;\n}\n\nstatic int asus_wmi_remove(struct platform_device *device)\n{\n\tstruct asus_wmi *asus;\n\n\tasus = platform_get_drvdata(device);\n\twmi_remove_notify_handler(asus->driver->event_guid);\n\tasus_wmi_backlight_exit(asus);\n\tasus_wmi_input_exit(asus);\n\tasus_wmi_led_exit(asus);\n\tkbbl_rgb_exit(asus);\n\tasus_wmi_rfkill_exit(asus);\n\tasus_wmi_debugfs_exit(asus);\n\tasus_wmi_sysfs_exit(asus->platform_device);\n\tasus_fan_set_auto(asus);\n\tasus_wmi_battery_exit(asus);\n\n\tkfree(asus);\n\treturn 0;\n}\n\n/* Platform driver - hibernate/resume callbacks *******************************/\n\nstatic int asus_hotk_thaw(struct device *device)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(device);\n\n\tif (asus->wlan.rfkill) {\n\t\tbool wlan;\n\n\t\t/*\n\t\t * Work around bios bug - acpi _PTS turns off the wireless led\n\t\t * during suspend.  Normally it restores it on resume, but\n\t\t * we should kick it ourselves in case hibernation is aborted.\n\t\t */\n\t\twlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);\n\t\tasus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);\n\t}\n\n\treturn 0;\n}\n\nstatic int asus_hotk_resume(struct device *device)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(device);\n\n\tif (!IS_ERR_OR_NULL(asus->kbd_led.dev))\n\t\tkbd_led_update(asus);\n\n\tif (asus_wmi_has_fnlock_key(asus))\n\t\tasus_wmi_fnlock_update(asus);\n\n\tif (asus->driver->quirks->use_lid_flip_devid)\n\t\tlid_flip_tablet_mode_get_state(asus);\n\n\treturn 0;\n}\n\nstatic int asus_hotk_restore(struct device *device)\n{\n\tstruct asus_wmi *asus = dev_get_drvdata(device);\n\tint bl;\n\n\t/* Refresh both wlan rfkill state and pci hotplug */\n\tif (asus->wlan.rfkill)\n\t\tasus_rfkill_hotplug(asus);\n\n\tif (asus->bluetooth.rfkill) {\n\t\tbl = !asus_wmi_get_devstate_simple(asus,\n\t\t\t\t\t\t   ASUS_WMI_DEVID_BLUETOOTH);\n\t\trfkill_set_sw_state(asus->bluetooth.rfkill, bl);\n\t}\n\tif (asus->wimax.rfkill) {\n\t\tbl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);\n\t\trfkill_set_sw_state(asus->wimax.rfkill, bl);\n\t}\n\tif (asus->wwan3g.rfkill) {\n\t\tbl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);\n\t\trfkill_set_sw_state(asus->wwan3g.rfkill, bl);\n\t}\n\tif (asus->gps.rfkill) {\n\t\tbl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);\n\t\trfkill_set_sw_state(asus->gps.rfkill, bl);\n\t}\n\tif (asus->uwb.rfkill) {\n\t\tbl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);\n\t\trfkill_set_sw_state(asus->uwb.rfkill, bl);\n\t}\n\tif (!IS_ERR_OR_NULL(asus->kbd_led.dev))\n\t\tkbd_led_update(asus);\n\n\tif (asus_wmi_has_fnlock_key(asus))\n\t\tasus_wmi_fnlock_update(asus);\n\n\tif (asus->driver->quirks->use_lid_flip_devid)\n\t\tlid_flip_tablet_mode_get_state(asus);\n\n\treturn 0;\n}\n\nstatic const struct dev_pm_ops asus_pm_ops = {\n\t.thaw = asus_hotk_thaw,\n\t.restore = asus_hotk_restore,\n\t.resume = asus_hotk_resume,\n};\n\n/// Faustus -------------------------------------------------------------------\nstatic struct platform_device* atw_platform_dev;\n\n// Platform driver ************************************************************\n\nstatic struct platform_driver atw_platform_driver = {\n\t.probe = asus_wmi_add,\n\t.remove = asus_wmi_remove,\n\t.driver = {\n\t\t.name = KBUILD_MODNAME,\n\t\t.owner = THIS_MODULE,\n\t\t.pm = &asus_pm_ops,\n\t}\n};\n\n// Probing ********************************************************************\n\nstatic int __init dmi_check_callback(const struct dmi_system_id *id)\n{\n\tpr_info(\"DMI check: %s\\n\", id->ident);\n\treturn 1;\n}\n\nstatic const struct dmi_system_id atw_dmi_list[] __initconst = {\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505GM\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505GM\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX705GM\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX705GM\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505DD\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505DD\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505DY\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505DY\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505GE\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505GE\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX705GE\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX705GE\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX705DY\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX705DY\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505GD\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505GD\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505DT\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505DT\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX705DT\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX705DT\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505DU\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505DU\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX705DU\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX705DU\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505DV\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505DV\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX505GT\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX505GT\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FA706II\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FA706II\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FA706IU\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FA706IU\"),\n\t\t},\n\t},\n\t{\n\t\t.callback = dmi_check_callback,\n\t\t.ident = \"FX506LI\",\n\t\t.matches = {\n\t\t\tDMI_MATCH(DMI_SYS_VENDOR, \"ASUSTeK COMPUTER INC.\"),\n\t\t\tDMI_MATCH(DMI_PRODUCT_NAME, \"FX506LI\"),\n\t\t},\n\t},\n\t{}\n};\n\nstatic int __init atw_init(void)\n{\n\tint status;\n\n\tif (!let_it_burn) {\n\t\tif (!dmi_check_system(atw_dmi_list)) {\n\t\t\treturn -ENODEV;\n\t\t}\n\t} else {\n\t\tpr_info(\"Omitting DMI verification\\n\");\n\t}\n\n\tif (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {\n\t\tpr_info(\"Method WMI GUID not found\\n\");\n\t\treturn -ENODEV;\n\t}\n\n\tif (!wmi_has_guid(ASUS_NB_WMI_EVENT_GUID)) {\n\t\tpr_info(\"Event WMI GUID not found\\n\");\n\t\treturn -ENODEV;\n\t}\n\n\n\tatw_platform_dev = platform_device_register_simple(\n\t\t\tKBUILD_MODNAME, -1, NULL, 0);\n\tif (IS_ERR(atw_platform_dev)) {\n\t\tstatus = PTR_ERR(atw_platform_dev);\n\t\tgoto fail_dev;\n\t}\n\n\tstatus = platform_driver_probe(&atw_platform_driver, asus_wmi_add);\n\tif (status) {\n\t\tpr_err(\"Can't probe platform driver: %d\\n\", status);\n\t\tgoto fail_driver;\n\t}\n\n\treturn 0;\n\nfail_driver:\n\tplatform_device_unregister(atw_platform_dev);\nfail_dev:\n\treturn status;\n}\n\nstatic void __exit atw_cleanup(void)\n{\n\tpr_info(\"Faustus unloading..\");\n\tplatform_driver_unregister(&atw_platform_driver);\n\tplatform_device_unregister(atw_platform_dev);\n}\n \nmodule_init(atw_init);\nmodule_exit(atw_cleanup);\n"
  },
  {
    "path": "src/faustus.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n/*\n * Asus PC WMI hotkey driver\n *\n * Copyright(C) 2010 Intel Corporation.\n * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>\n *\n * Portions based on wistron_btns.c:\n * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>\n * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>\n * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>\n *\n *  This program is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program; if not, write to the Free Software\n *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n */\n#ifndef __FAUSTUS_H\n#define __FAUSTUS_H\n\n#include <linux/errno.h>\n#include <linux/types.h>\n\n#include <linux/platform_device.h>\n#include <linux/i8042.h>\n\n// platform_data/x86/asus-wmi.h\n\n/* WMI Methods */\n#define ASUS_WMI_METHODID_SPEC\t        0x43455053 /* BIOS SPECification */\n#define ASUS_WMI_METHODID_SFBD\t\t0x44424653 /* Set First Boot Device */\n#define ASUS_WMI_METHODID_GLCD\t\t0x44434C47 /* Get LCD status */\n#define ASUS_WMI_METHODID_GPID\t\t0x44495047 /* Get Panel ID?? (Resol) */\n#define ASUS_WMI_METHODID_QMOD\t\t0x444F4D51 /* Quiet MODe */\n#define ASUS_WMI_METHODID_SPLV\t\t0x4C425053 /* Set Panel Light Value */\n#define ASUS_WMI_METHODID_AGFN\t\t0x4E464741 /* FaN? */\n#define ASUS_WMI_METHODID_SFUN\t\t0x4E554653 /* FUNCtionalities */\n#define ASUS_WMI_METHODID_SDSP\t\t0x50534453 /* Set DiSPlay output */\n#define ASUS_WMI_METHODID_GDSP\t\t0x50534447 /* Get DiSPlay output */\n#define ASUS_WMI_METHODID_DEVP\t\t0x50564544 /* DEVice Policy */\n#define ASUS_WMI_METHODID_OSVR\t\t0x5256534F /* OS VeRsion */\n#define ASUS_WMI_METHODID_DCTS\t\t0x53544344 /* Device status (DCTS) */\n#define ASUS_WMI_METHODID_DSTS\t\t0x53545344 /* Device status (DSTS) */\n#define ASUS_WMI_METHODID_BSTS\t\t0x53545342 /* Bios STatuS ? */\n#define ASUS_WMI_METHODID_DEVS\t\t0x53564544 /* DEVice Set */\n#define ASUS_WMI_METHODID_CFVS\t\t0x53564643 /* CPU Frequency Volt Set */\n#define ASUS_WMI_METHODID_KBFT\t\t0x5446424B /* KeyBoard FilTer */\n#define ASUS_WMI_METHODID_INIT\t\t0x54494E49 /* INITialize */\n#define ASUS_WMI_METHODID_HKEY\t\t0x59454B48 /* Hot KEY ?? */\n\n#define ASUS_WMI_UNSUPPORTED_METHOD\t0xFFFFFFFE\n\n/* Wireless */\n#define ASUS_WMI_DEVID_HW_SWITCH\t0x00010001\n#define ASUS_WMI_DEVID_WIRELESS_LED\t0x00010002\n#define ASUS_WMI_DEVID_CWAP\t\t0x00010003\n#define ASUS_WMI_DEVID_WLAN\t\t0x00010011\n#define ASUS_WMI_DEVID_WLAN_LED\t\t0x00010012\n#define ASUS_WMI_DEVID_BLUETOOTH\t0x00010013\n#define ASUS_WMI_DEVID_GPS\t\t0x00010015\n#define ASUS_WMI_DEVID_WIMAX\t\t0x00010017\n#define ASUS_WMI_DEVID_WWAN3G\t\t0x00010019\n#define ASUS_WMI_DEVID_UWB\t\t0x00010021\n\n/* Leds */\n/* 0x000200XX and 0x000400XX */\n#define ASUS_WMI_DEVID_LED1\t\t0x00020011\n#define ASUS_WMI_DEVID_LED2\t\t0x00020012\n#define ASUS_WMI_DEVID_LED3\t\t0x00020013\n#define ASUS_WMI_DEVID_LED4\t\t0x00020014\n#define ASUS_WMI_DEVID_LED5\t\t0x00020015\n#define ASUS_WMI_DEVID_LED6\t\t0x00020016\n\n/* Backlight and Brightness */\n#define ASUS_WMI_DEVID_ALS_ENABLE\t0x00050001 /* Ambient Light Sensor */\n#define ASUS_WMI_DEVID_BACKLIGHT\t0x00050011\n#define ASUS_WMI_DEVID_BRIGHTNESS\t0x00050012\n#define ASUS_WMI_DEVID_KBD_BACKLIGHT\t0x00050021\n#define ASUS_WMI_DEVID_LIGHT_SENSOR\t0x00050022 /* ?? */\n#define ASUS_WMI_DEVID_LIGHTBAR\t\t0x00050025\n#define ASUS_WMI_DEVID_FAN_BOOST_MODE\t0x00110018\n#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075\n#define ASUS_WMI_DEVID_KBD_RGB\t\t0x00100056\n#define ASUS_WMI_DEVID_KBD_RGB2\t\t0x00100057\n\n/* Misc */\n#define ASUS_WMI_DEVID_CAMERA\t\t0x00060013\n#define ASUS_WMI_DEVID_LID_FLIP\t\t0x00060062\n\n/* Storage */\n#define ASUS_WMI_DEVID_CARDREADER\t0x00080013\n\n/* Input */\n#define ASUS_WMI_DEVID_TOUCHPAD\t\t0x00100011\n#define ASUS_WMI_DEVID_TOUCHPAD_LED\t0x00100012\n#define ASUS_WMI_DEVID_FNLOCK\t\t0x00100023\n\n/* Fan, Thermal */\n#define ASUS_WMI_DEVID_THERMAL_CTRL\t0x00110011\n#define ASUS_WMI_DEVID_FAN_CTRL\t\t0x00110012 /* deprecated */\n#define ASUS_WMI_DEVID_CPU_FAN_CTRL\t0x00110013\n\n/* Power */\n#define ASUS_WMI_DEVID_PROCESSOR_STATE\t0x00120012\n\n/* Deep S3 / Resume on LID open */\n#define ASUS_WMI_DEVID_LID_RESUME\t0x00120031\n\n/* Maximum charging percentage */\n#define ASUS_WMI_DEVID_RSOC\t\t0x00120057\n\n/* Keyboard dock */\n#define ASUS_WMI_DEVID_KBD_DOCK\t\t0x00120063\n\n/* DSTS masks */\n#define ASUS_WMI_DSTS_STATUS_BIT\t0x00000001\n#define ASUS_WMI_DSTS_UNKNOWN_BIT\t0x00000002\n#define ASUS_WMI_DSTS_PRESENCE_BIT\t0x00010000\n#define ASUS_WMI_DSTS_USER_BIT\t\t0x00020000\n#define ASUS_WMI_DSTS_BIOS_BIT\t\t0x00040000\n#define ASUS_WMI_DSTS_BRIGHTNESS_MASK\t0x000000FF\n#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK\t0x0000FF00\n#define ASUS_WMI_DSTS_LIGHTBAR_MASK\t0x0000000F\n\n// drivers/platform/x86/asus-wmi.h\n\n#define ASUS_WMI_KEY_IGNORE (-1)\n#define ASUS_WMI_BRN_DOWN\t0x20\n#define ASUS_WMI_BRN_UP\t\t0x2f\n\nstruct module;\nstruct key_entry;\nstruct asus_wmi;\n\nstruct quirk_entry {\n\tbool hotplug_wireless;\n\tbool scalar_panel_brightness;\n\tbool store_backlight_power;\n\tbool wmi_backlight_power;\n\tbool wmi_backlight_native;\n\tbool wmi_backlight_set_devstate;\n\tbool wmi_force_als_set;\n\tbool use_kbd_dock_devid;\n\tbool use_lid_flip_devid;\n\tint wapf;\n\t/*\n\t * For machines with AMD graphic chips, it will send out WMI event\n\t * and ACPI interrupt at the same time while hitting the hotkey.\n\t * To simplify the problem, we just have to ignore the WMI event,\n\t * and let the ACPI interrupt to send out the key event.\n\t */\n\tint no_display_toggle;\n\tu32 xusb2pr;\n\n\tbool (*i8042_filter)(unsigned char data, unsigned char str,\n\t\t\t     struct serio *serio);\n};\n\nstruct asus_wmi_driver {\n\tint\t\t\tbrightness;\n\tint\t\t\tpanel_power;\n\tint\t\t\twlan_ctrl_by_user;\n\n\tconst char\t\t*name;\n\tstruct module\t\t*owner;\n\n\tconst char\t\t*event_guid;\n\n\tconst struct key_entry\t*keymap;\n\tconst char\t\t*input_name;\n\tconst char\t\t*input_phys;\n\tstruct quirk_entry\t*quirks;\n\t/* Returns new code, value, and autorelease values in arguments.\n\t * Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */\n\tvoid (*key_filter) (struct asus_wmi_driver *driver, int *code,\n\t\t\t    unsigned int *value, bool *autorelease);\n\n\tint (*probe) (struct platform_device *device);\n\tvoid (*detect_quirks) (struct asus_wmi_driver *driver);\n\n\tstruct platform_driver\tplatform_driver;\n\tstruct platform_device *platform_device;\n};\n\n#endif\t/* __FAUSTUS_H */\n"
  }
]