├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── dkms.conf └── src ├── Kbuild ├── Makefile ├── gen_ident.py └── hp-wmi.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.ko 2 | *.o 3 | *.a 4 | module/.* 5 | *.mod 6 | *.mod.c 7 | *.mod.o 8 | modules.order 9 | Module.symvers 10 | omen_cs/* 11 | app 12 | iasl 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | dkms install . 3 | 4 | uninstall: 5 | dkms remove hp-omen-wmi/0.9 --all 6 | 7 | all: install 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HP Omen special feature control for Linux 2 | ----------------------------------------- 3 | 4 | This is a version of the hp-wmi kernel module that implements some of the features of HP Omen Command Centre. 5 | 6 | It's totally experimental right now, and could easily crash your machine. 7 | 8 | **USE AT YOUR OWN RISK** 9 | 10 | Currently working: 11 | 12 | - FourZone keyboard colour control (`/sys/devices/platforms/hp-wmi/rgb-zones/zone0[0-3]`) 13 | - Omen hotkeys 14 | 15 | ## Installation 16 | 17 | 1. Install dkms and kernel headers if needed (already present on Ubuntu) 18 | 19 | 1. Run `sudo make install` 20 | 21 | Module will be built and installed, and DKMS will manage rebuilding it on kernel updates. 22 | 23 | ## Usage 24 | 25 | The module creates four files in `/sys/devices/platform/hp-wmi/rgb_zones/` named `zone00 - zone03`. 26 | 27 | To change zone highlight color, just print hex colour value in RGB format to the respective file. e.g: 28 | 29 | `sudo bash -c 'echo 00FFFF > /sys/devices/platform/hp-wmi/rgb_zones/zone00'` to get sky-blue zone 0. 30 | 31 | Omen and other hotkeys are bound to regular X11 keysyms, use your chosen desktop's hotkey manager to assign them to functions like any other key. 32 | 33 | ## To do: 34 | 35 | - [ ] FourZone brightness control 36 | - [ ] Fan control 37 | 38 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | MAKE="make -C src/ KERNELDIR=/lib/modules/${kernelver}/build" 2 | CLEAN="make -C src/ clean" 3 | BUILT_MODULE_NAME=hp-wmi 4 | BUILT_MODULE_LOCATION=src/ 5 | DEST_MODULE_LOCATION=/kernel/drivers/platform/x86/ 6 | PACKAGE_NAME=hp-omen-wmi 7 | PACKAGE_VERSION=0.9 8 | AUTOINSTALL=yes 9 | 10 | -------------------------------------------------------------------------------- /src/Kbuild: -------------------------------------------------------------------------------- 1 | obj-m := hp-wmi.o 2 | 3 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # normal makefile 2 | KERNELDIR ?= /lib/modules/`uname -r`/build 3 | 4 | default: 5 | $(MAKE) -C $(KERNELDIR) M=$$PWD 6 | 7 | # Module specific targets 8 | genbin: 9 | echo "X" > hp-wmi_bin.o_shipped 10 | 11 | clean: 12 | -$(RM) -f *.a *.ko *.o *.mod *.mod.c *.order *.symvers 13 | 14 | .PHONY: clean 15 | 16 | -------------------------------------------------------------------------------- /src/gen_ident.py: -------------------------------------------------------------------------------- 1 | """Convert HP Omen devicelist json to a id struct""" 2 | import json 3 | 4 | def gen_device_list(devices): 5 | print("static const struct dmi_system_id omen_quirks[] __initconst = {") 6 | for dev in devices: 7 | for board in dev["ProductNum"]: 8 | print(""" {{ 9 | .callback = dmi_matched, 10 | .ident = "{}", 11 | .matches = {{ 12 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "{}"), 13 | }}, 14 | .driver_data = {}, 15 | }},""".format(dev["DisplayName"], board, dev["BackgroundFeature"])) 16 | print(" {}\n};\n") 17 | 18 | if __name__ == "__main__": 19 | devicelist = [] 20 | 21 | with open("omen_cs/HP.Omen.DeviceLib.JSON.DeviceList.json", "r") as f: 22 | devicelist = json.load(f) 23 | print("// ident.h") 24 | print("// generated by gen_ident.py from HP.Omen.DeviceLib.JSON.DeviceList.json\n") 25 | gen_device_list(devicelist) 26 | 27 | -------------------------------------------------------------------------------- /src/hp-wmi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * HP WMI hotkeys 4 | * 5 | * Copyright (C) 2008 Red Hat 6 | * Copyright (C) 2010, 2011 Anssi Hannula 7 | * 8 | * Portions based on wistron_btns.c: 9 | * Copyright (C) 2005 Miloslav Trmac 10 | * Copyright (C) 2005 Bernhard Rosenkraenzer 11 | * Copyright (C) 2005 Dmitry Torokhov 12 | * 13 | * Portions based on alienware-wmi.c: 14 | * Copyright (C) 2014 Dell Inc 15 | */ 16 | 17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef STUPID_INTELLISENSE_HACK 32 | #define pr_err(...) 33 | #define pr_warn(...) 34 | #define pr_info(...) 35 | #define pr_debug(...) 36 | #endif 37 | 38 | MODULE_AUTHOR("Matthew Garrett "); 39 | MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); 40 | MODULE_LICENSE("GPL"); 41 | 42 | MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); 43 | MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); 44 | 45 | #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" 46 | #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" 47 | 48 | enum hp_wmi_radio { 49 | HPWMI_WIFI = 0x0, 50 | HPWMI_BLUETOOTH = 0x1, 51 | HPWMI_WWAN = 0x2, 52 | HPWMI_GPS = 0x3, 53 | }; 54 | 55 | enum hp_wmi_event_ids { 56 | HPWMI_DOCK_EVENT = 0x01, 57 | HPWMI_PARK_HDD = 0x02, 58 | HPWMI_SMART_ADAPTER = 0x03, 59 | HPWMI_BEZEL_BUTTON = 0x04, 60 | HPWMI_WIRELESS = 0x05, 61 | HPWMI_CPU_BATTERY_THROTTLE = 0x06, 62 | HPWMI_LOCK_SWITCH = 0x07, 63 | HPWMI_LID_SWITCH = 0x08, 64 | HPWMI_SCREEN_ROTATION = 0x09, 65 | HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, 66 | HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, 67 | HPWMI_PROXIMITY_SENSOR = 0x0C, 68 | HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, 69 | HPWMI_PEAKSHIFT_PERIOD = 0x0F, 70 | HPWMI_BATTERY_CHARGE_PERIOD = 0x10, 71 | HPWMI_OMEN_KEY = 0x1D 72 | }; 73 | 74 | struct bios_args { 75 | u32 signature; 76 | u32 command; 77 | u32 commandtype; 78 | u32 datasize; 79 | u8 data[128]; 80 | }; 81 | 82 | enum hp_wmi_commandtype { 83 | HPWMI_DISPLAY_QUERY = 0x01, 84 | HPWMI_HDDTEMP_QUERY = 0x02, 85 | HPWMI_ALS_QUERY = 0x03, 86 | HPWMI_HARDWARE_QUERY = 0x04, 87 | HPWMI_WIRELESS_QUERY = 0x05, 88 | HPWMI_BATTERY_QUERY = 0x07, 89 | HPWMI_BIOS_QUERY = 0x09, 90 | HPWMI_FEATURE_QUERY = 0x0b, 91 | HPWMI_HOTKEY_QUERY = 0x0c, 92 | HPWMI_FEATURE2_QUERY = 0x0d, 93 | HPWMI_WIRELESS2_QUERY = 0x1b, 94 | HPWMI_POSTCODEERROR_QUERY = 0x2a, 95 | 96 | HPWMI_FOURZONE_COLOR_GET = 2, 97 | HPWMI_FOURZONE_COLOR_SET = 3, 98 | HPWMI_FOURZONE_BRIGHT_GET = 4, 99 | HPWMI_FOURZONE_BRIGHT_SET = 5, 100 | HPWMI_FOURZONE_ANIM_GET = 6, 101 | HPWMI_FOURZONE_ANIM_SET = 7, 102 | }; 103 | 104 | enum hp_wmi_command { 105 | HPWMI_READ = 0x01, 106 | HPWMI_WRITE = 0x02, 107 | HPWMI_ODM = 0x03, 108 | HPWMI_FOURZONE = 131081, 109 | }; 110 | 111 | enum hp_wmi_hardware_mask { 112 | HPWMI_DOCK_MASK = 0x01, 113 | HPWMI_TABLET_MASK = 0x04, 114 | }; 115 | 116 | struct bios_return { 117 | u32 sigpass; 118 | u32 return_code; 119 | }; 120 | 121 | enum hp_return_value { 122 | HPWMI_RET_WRONG_SIGNATURE = 0x02, 123 | HPWMI_RET_UNKNOWN_COMMAND = 0x03, 124 | HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, 125 | HPWMI_RET_INPUT_SIZE_NULL = 0x05, 126 | HPWMI_RET_INPUT_DATA_NULL = 0x06, 127 | HPWMI_RET_INPUT_DATA_INVALID = 0x07, 128 | HPWMI_RET_RETURN_SIZE_NULL = 0x08, 129 | HPWMI_RET_RETURN_SIZE_INVALID = 0x09, 130 | }; 131 | 132 | enum hp_wireless2_bits { 133 | HPWMI_POWER_STATE = 0x01, 134 | HPWMI_POWER_SOFT = 0x02, 135 | HPWMI_POWER_BIOS = 0x04, 136 | HPWMI_POWER_HARD = 0x08, 137 | }; 138 | 139 | #define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \ 140 | != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) 141 | #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) 142 | 143 | struct bios_rfkill2_device_state { 144 | u8 radio_type; 145 | u8 bus_type; 146 | u16 vendor_id; 147 | u16 product_id; 148 | u16 subsys_vendor_id; 149 | u16 subsys_product_id; 150 | u8 rfkill_id; 151 | u8 power; 152 | u8 unknown[4]; 153 | }; 154 | 155 | /* 7 devices fit into the 128 byte buffer */ 156 | #define HPWMI_MAX_RFKILL2_DEVICES 7 157 | 158 | struct bios_rfkill2_state { 159 | u8 unknown[7]; 160 | u8 count; 161 | u8 pad[8]; 162 | struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; 163 | }; 164 | 165 | // Set if the keycode is a key release 166 | #define HPWMI_HOTKEY_RELEASE_FLAG (1<<16) 167 | 168 | static const struct key_entry hp_wmi_keymap[] = { 169 | { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, 170 | { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, 171 | { KE_KEY, 0x20e6, { KEY_PROG1 } }, 172 | { KE_KEY, 0x20e8, { KEY_MEDIA } }, 173 | { KE_KEY, 0x2142, { KEY_MEDIA } }, 174 | { KE_KEY, 0x213b, { KEY_INFO } }, 175 | { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, 176 | { KE_KEY, 0x216a, { KEY_SETUP } }, 177 | { KE_KEY, 0x231b, { KEY_HELP } }, 178 | { KE_KEY, 0x21A4, { KEY_F14 } }, // Winlock hotkey 179 | { KE_KEY, 0x21A5, { KEY_F15 } }, // Omen key 180 | { KE_KEY, 0x21A7, { KEY_F16 } }, // ??? 181 | { KE_KEY, 0x21A9, { KEY_F17 } }, // Disable touchpad hotkey 182 | { KE_END, 0 } 183 | }; 184 | 185 | static struct input_dev *hp_wmi_input_dev; 186 | static struct platform_device *hp_wmi_platform_dev; 187 | 188 | static struct rfkill *wifi_rfkill; 189 | static struct rfkill *bluetooth_rfkill; 190 | static struct rfkill *wwan_rfkill; 191 | 192 | struct rfkill2_device { 193 | u8 id; 194 | int num; 195 | struct rfkill *rfkill; 196 | }; 197 | 198 | static int rfkill2_count; 199 | static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; 200 | 201 | /* Determine featureset for specific models */ 202 | 203 | struct quirk_entry { 204 | bool fourzone; 205 | }; 206 | 207 | static struct quirk_entry temp_omen = { 208 | .fourzone = true, 209 | }; 210 | 211 | static struct quirk_entry *quirks = &temp_omen; 212 | 213 | /* map output size to the corresponding WMI method id */ 214 | static inline int encode_outsize_for_pvsz(int outsize) 215 | { 216 | if (outsize > 4096) 217 | return -EINVAL; 218 | if (outsize > 1024) 219 | return 5; 220 | if (outsize > 128) 221 | return 4; 222 | if (outsize > 4) 223 | return 3; 224 | if (outsize > 0) 225 | return 2; 226 | return 1; 227 | } 228 | 229 | /* 230 | * hp_wmi_perform_query 231 | * 232 | * query: The commandtype (enum hp_wmi_commandtype) 233 | * write: The command (enum hp_wmi_command) 234 | * buffer: Buffer used as input and/or output 235 | * insize: Size of input buffer 236 | * outsize: Size of output buffer 237 | * 238 | * returns zero on success 239 | * an HP WMI query specific error code (which is positive) 240 | * -EINVAL if the query was not successful at all 241 | * -EINVAL if the output buffer size exceeds buffersize 242 | * 243 | * Note: The buffersize must at least be the maximum of the input and output 244 | * size. E.g. Battery info query is defined to have 1 byte input 245 | * and 128 byte output. The caller would do: 246 | * buffer = kzalloc(128, GFP_KERNEL); 247 | * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) 248 | */ 249 | static int hp_wmi_perform_query(int query, enum hp_wmi_command command, 250 | void *buffer, int insize, int outsize) 251 | { 252 | int mid; 253 | struct bios_return *bios_return; 254 | int actual_outsize; 255 | union acpi_object *obj; 256 | struct bios_args args = { 257 | .signature = 0x55434553, 258 | .command = command, 259 | .commandtype = query, 260 | .datasize = insize, 261 | .data = { 0 }, 262 | }; 263 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; 264 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 265 | int ret = 0; 266 | 267 | mid = encode_outsize_for_pvsz(outsize); 268 | if (WARN_ON(mid < 0)) 269 | return mid; 270 | 271 | if (WARN_ON(insize > sizeof(args.data))) 272 | return -EINVAL; 273 | memcpy(&args.data[0], buffer, insize); 274 | 275 | wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); 276 | 277 | obj = output.pointer; 278 | 279 | if (!obj) 280 | return -EINVAL; 281 | 282 | if (obj->type != ACPI_TYPE_BUFFER) { 283 | ret = -EINVAL; 284 | goto out_free; 285 | } 286 | 287 | bios_return = (struct bios_return *)obj->buffer.pointer; 288 | ret = bios_return->return_code; 289 | 290 | if (ret) { 291 | if (ret != HPWMI_RET_UNKNOWN_CMDTYPE) 292 | pr_warn("query 0x%x returned error 0x%x\n", query, ret); 293 | goto out_free; 294 | } 295 | 296 | /* Ignore output data of zero size */ 297 | if (!outsize) 298 | goto out_free; 299 | 300 | actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); 301 | memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); 302 | memset(buffer + actual_outsize, 0, outsize - actual_outsize); 303 | 304 | out_free: 305 | kfree(obj); 306 | return ret; 307 | } 308 | 309 | static int hp_wmi_read_int(int query) 310 | { 311 | int val = 0, ret; 312 | 313 | ret = hp_wmi_perform_query(query, HPWMI_READ, &val, 314 | sizeof(val), sizeof(val)); 315 | 316 | if (ret) 317 | return ret < 0 ? ret : -EINVAL; 318 | 319 | return val; 320 | } 321 | 322 | static int hp_wmi_hw_state(int mask) 323 | { 324 | int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); 325 | 326 | if (state < 0) 327 | return state; 328 | 329 | return !!(state & mask); 330 | } 331 | 332 | static int __init hp_wmi_bios_2008_later(void) 333 | { 334 | int state = 0; 335 | int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, 336 | sizeof(state), sizeof(state)); 337 | if (!ret) 338 | return 1; 339 | 340 | return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; 341 | } 342 | 343 | static int __init hp_wmi_bios_2009_later(void) 344 | { 345 | u8 state[128]; 346 | int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, 347 | sizeof(state), sizeof(state)); 348 | if (!ret) 349 | return 1; 350 | 351 | return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; 352 | } 353 | 354 | static int __init hp_wmi_enable_hotkeys(void) 355 | { 356 | int value = 0x6e; 357 | int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, 358 | sizeof(value), 0); 359 | 360 | return ret <= 0 ? ret : -EINVAL; 361 | } 362 | 363 | static int hp_wmi_set_block(void *data, bool blocked) 364 | { 365 | enum hp_wmi_radio r = (enum hp_wmi_radio) data; 366 | int query = BIT(r + 8) | ((!blocked) << r); 367 | int ret; 368 | 369 | ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, 370 | &query, sizeof(query), 0); 371 | 372 | return ret <= 0 ? ret : -EINVAL; 373 | } 374 | 375 | static const struct rfkill_ops hp_wmi_rfkill_ops = { 376 | .set_block = hp_wmi_set_block, 377 | }; 378 | 379 | static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) 380 | { 381 | int mask = 0x200 << (r * 8); 382 | 383 | int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); 384 | 385 | /* TBD: Pass error */ 386 | WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); 387 | 388 | return !(wireless & mask); 389 | } 390 | 391 | static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) 392 | { 393 | int mask = 0x800 << (r * 8); 394 | 395 | int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); 396 | 397 | /* TBD: Pass error */ 398 | WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); 399 | 400 | return !(wireless & mask); 401 | } 402 | 403 | static int hp_wmi_rfkill2_set_block(void *data, bool blocked) 404 | { 405 | int rfkill_id = (int)(long)data; 406 | char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; 407 | int ret; 408 | 409 | ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, 410 | buffer, sizeof(buffer), 0); 411 | 412 | return ret <= 0 ? ret : -EINVAL; 413 | } 414 | 415 | static const struct rfkill_ops hp_wmi_rfkill2_ops = { 416 | .set_block = hp_wmi_rfkill2_set_block, 417 | }; 418 | 419 | static int hp_wmi_rfkill2_refresh(void) 420 | { 421 | struct bios_rfkill2_state state; 422 | int err, i; 423 | 424 | err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, 425 | sizeof(state), sizeof(state)); 426 | if (err) 427 | return err; 428 | 429 | for (i = 0; i < rfkill2_count; i++) { 430 | int num = rfkill2[i].num; 431 | struct bios_rfkill2_device_state *devstate; 432 | devstate = &state.device[num]; 433 | 434 | if (num >= state.count || 435 | devstate->rfkill_id != rfkill2[i].id) { 436 | pr_warn("power configuration of the wireless devices unexpectedly changed\n"); 437 | continue; 438 | } 439 | 440 | rfkill_set_states(rfkill2[i].rfkill, 441 | IS_SWBLOCKED(devstate->power), 442 | IS_HWBLOCKED(devstate->power)); 443 | } 444 | 445 | return 0; 446 | } 447 | 448 | static ssize_t display_show(struct device *dev, struct device_attribute *attr, 449 | char *buf) 450 | { 451 | int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); 452 | if (value < 0) 453 | return value; 454 | return sprintf(buf, "%d\n", value); 455 | } 456 | 457 | static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, 458 | char *buf) 459 | { 460 | int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); 461 | if (value < 0) 462 | return value; 463 | return sprintf(buf, "%d\n", value); 464 | } 465 | 466 | static ssize_t als_show(struct device *dev, struct device_attribute *attr, 467 | char *buf) 468 | { 469 | int value = hp_wmi_read_int(HPWMI_ALS_QUERY); 470 | if (value < 0) 471 | return value; 472 | return sprintf(buf, "%d\n", value); 473 | } 474 | 475 | static ssize_t dock_show(struct device *dev, struct device_attribute *attr, 476 | char *buf) 477 | { 478 | int value = hp_wmi_hw_state(HPWMI_DOCK_MASK); 479 | if (value < 0) 480 | return value; 481 | return sprintf(buf, "%d\n", value); 482 | } 483 | 484 | static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, 485 | char *buf) 486 | { 487 | int value = hp_wmi_hw_state(HPWMI_TABLET_MASK); 488 | if (value < 0) 489 | return value; 490 | return sprintf(buf, "%d\n", value); 491 | } 492 | 493 | static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, 494 | char *buf) 495 | { 496 | /* Get the POST error code of previous boot failure. */ 497 | int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); 498 | if (value < 0) 499 | return value; 500 | return sprintf(buf, "0x%x\n", value); 501 | } 502 | 503 | static ssize_t als_store(struct device *dev, struct device_attribute *attr, 504 | const char *buf, size_t count) 505 | { 506 | u32 tmp = simple_strtoul(buf, NULL, 10); 507 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, 508 | sizeof(tmp), sizeof(tmp)); 509 | if (ret) 510 | return ret < 0 ? ret : -EINVAL; 511 | 512 | return count; 513 | } 514 | 515 | static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, 516 | const char *buf, size_t count) 517 | { 518 | long unsigned int tmp2; 519 | int ret; 520 | u32 tmp; 521 | 522 | ret = kstrtoul(buf, 10, &tmp2); 523 | if (!ret && tmp2 != 1) 524 | ret = -EINVAL; 525 | if (ret) 526 | goto out; 527 | 528 | /* Clear the POST error code. It is kept until until cleared. */ 529 | tmp = (u32) tmp2; 530 | ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, 531 | sizeof(tmp), sizeof(tmp)); 532 | 533 | out: 534 | if (ret) 535 | return ret < 0 ? ret : -EINVAL; 536 | 537 | return count; 538 | } 539 | 540 | static DEVICE_ATTR_RO(display); 541 | static DEVICE_ATTR_RO(hddtemp); 542 | static DEVICE_ATTR_RW(als); 543 | static DEVICE_ATTR_RO(dock); 544 | static DEVICE_ATTR_RO(tablet); 545 | static DEVICE_ATTR_RW(postcode); 546 | 547 | static void hp_wmi_notify(u32 value, void *context) 548 | { 549 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 550 | u32 event_id, event_data; 551 | union acpi_object *obj; 552 | acpi_status status; 553 | u32 *location; 554 | int key_code; 555 | 556 | status = wmi_get_event_data(value, &response); 557 | if (status == AE_NOT_FOUND) 558 | { 559 | // We've been woken up without any event data 560 | // Some models do this when the Omen hotkey is pressed 561 | event_id = HPWMI_OMEN_KEY; 562 | } 563 | else if (status != AE_OK) { 564 | pr_info("bad event value 0x%x status 0x%x\n", value, status); 565 | return; 566 | } 567 | else 568 | { 569 | obj = (union acpi_object *)response.pointer; 570 | 571 | if (!obj) 572 | return; 573 | if (obj->type != ACPI_TYPE_BUFFER) { 574 | pr_info("Unknown response received %d\n", obj->type); 575 | kfree(obj); 576 | return; 577 | } 578 | 579 | /* 580 | * Depending on ACPI version the concatenation of id and event data 581 | * inside _WED function will result in a 8 or 16 byte buffer. 582 | */ 583 | location = (u32 *)obj->buffer.pointer; 584 | if (obj->buffer.length == 8) { 585 | event_id = *location; 586 | event_data = *(location + 1); 587 | } else if (obj->buffer.length == 16) { 588 | event_id = *location; 589 | event_data = *(location + 2); 590 | } else { 591 | pr_info("Unknown buffer length %d\n", obj->buffer.length); 592 | kfree(obj); 593 | return; 594 | } 595 | kfree(obj); 596 | } 597 | 598 | switch (event_id) { 599 | case HPWMI_DOCK_EVENT: 600 | if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) 601 | input_report_switch(hp_wmi_input_dev, SW_DOCK, 602 | hp_wmi_hw_state(HPWMI_DOCK_MASK)); 603 | if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) 604 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, 605 | hp_wmi_hw_state(HPWMI_TABLET_MASK)); 606 | input_sync(hp_wmi_input_dev); 607 | break; 608 | case HPWMI_PARK_HDD: 609 | break; 610 | case HPWMI_SMART_ADAPTER: 611 | break; 612 | case HPWMI_BEZEL_BUTTON: 613 | case HPWMI_OMEN_KEY: 614 | key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); 615 | // Some hotkeys generate both press and release events 616 | // Just drop the release events. 617 | if (key_code < 0 || (key_code & HPWMI_HOTKEY_RELEASE_FLAG)) 618 | break; 619 | 620 | if (!sparse_keymap_report_event(hp_wmi_input_dev, 621 | key_code, 1, true)) 622 | pr_info("Unknown key code - 0x%x\n", key_code); 623 | break; 624 | case HPWMI_WIRELESS: 625 | if (rfkill2_count) { 626 | hp_wmi_rfkill2_refresh(); 627 | break; 628 | } 629 | 630 | if (wifi_rfkill) 631 | rfkill_set_states(wifi_rfkill, 632 | hp_wmi_get_sw_state(HPWMI_WIFI), 633 | hp_wmi_get_hw_state(HPWMI_WIFI)); 634 | if (bluetooth_rfkill) 635 | rfkill_set_states(bluetooth_rfkill, 636 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH), 637 | hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); 638 | if (wwan_rfkill) 639 | rfkill_set_states(wwan_rfkill, 640 | hp_wmi_get_sw_state(HPWMI_WWAN), 641 | hp_wmi_get_hw_state(HPWMI_WWAN)); 642 | break; 643 | case HPWMI_CPU_BATTERY_THROTTLE: 644 | pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); 645 | break; 646 | case HPWMI_LOCK_SWITCH: 647 | break; 648 | case HPWMI_LID_SWITCH: 649 | break; 650 | case HPWMI_SCREEN_ROTATION: 651 | break; 652 | case HPWMI_COOLSENSE_SYSTEM_MOBILE: 653 | break; 654 | case HPWMI_COOLSENSE_SYSTEM_HOT: 655 | break; 656 | case HPWMI_PROXIMITY_SENSOR: 657 | break; 658 | case HPWMI_BACKLIT_KB_BRIGHTNESS: 659 | break; 660 | case HPWMI_PEAKSHIFT_PERIOD: 661 | break; 662 | case HPWMI_BATTERY_CHARGE_PERIOD: 663 | break; 664 | default: 665 | pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); 666 | break; 667 | } 668 | } 669 | 670 | static int __init hp_wmi_input_setup(void) 671 | { 672 | acpi_status status; 673 | int err, val; 674 | 675 | hp_wmi_input_dev = input_allocate_device(); 676 | if (!hp_wmi_input_dev) 677 | return -ENOMEM; 678 | 679 | hp_wmi_input_dev->name = "HP WMI hotkeys"; 680 | hp_wmi_input_dev->phys = "wmi/input0"; 681 | hp_wmi_input_dev->id.bustype = BUS_HOST; 682 | 683 | __set_bit(EV_SW, hp_wmi_input_dev->evbit); 684 | 685 | /* Dock */ 686 | val = hp_wmi_hw_state(HPWMI_DOCK_MASK); 687 | if (!(val < 0)) { 688 | __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); 689 | input_report_switch(hp_wmi_input_dev, SW_DOCK, val); 690 | } 691 | 692 | /* Tablet mode */ 693 | val = hp_wmi_hw_state(HPWMI_TABLET_MASK); 694 | if (!(val < 0)) { 695 | __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); 696 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); 697 | } 698 | 699 | err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); 700 | if (err) 701 | goto err_free_dev; 702 | 703 | /* Set initial hardware state */ 704 | input_sync(hp_wmi_input_dev); 705 | 706 | if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) 707 | hp_wmi_enable_hotkeys(); 708 | 709 | status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); 710 | if (ACPI_FAILURE(status)) { 711 | err = -EIO; 712 | goto err_free_dev; 713 | } 714 | 715 | err = input_register_device(hp_wmi_input_dev); 716 | if (err) 717 | goto err_uninstall_notifier; 718 | 719 | return 0; 720 | 721 | err_uninstall_notifier: 722 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); 723 | err_free_dev: 724 | input_free_device(hp_wmi_input_dev); 725 | return err; 726 | } 727 | 728 | static void hp_wmi_input_destroy(void) 729 | { 730 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); 731 | input_unregister_device(hp_wmi_input_dev); 732 | } 733 | 734 | static void cleanup_sysfs(struct platform_device *device) 735 | { 736 | device_remove_file(&device->dev, &dev_attr_display); 737 | device_remove_file(&device->dev, &dev_attr_hddtemp); 738 | device_remove_file(&device->dev, &dev_attr_als); 739 | device_remove_file(&device->dev, &dev_attr_dock); 740 | device_remove_file(&device->dev, &dev_attr_tablet); 741 | device_remove_file(&device->dev, &dev_attr_postcode); 742 | } 743 | 744 | static int __init hp_wmi_rfkill_setup(struct platform_device *device) 745 | { 746 | int err, wireless; 747 | 748 | wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); 749 | if (wireless < 0) 750 | return wireless; 751 | 752 | err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, 753 | sizeof(wireless), 0); 754 | if (err) 755 | return err; 756 | 757 | if (wireless & 0x1) { 758 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, 759 | RFKILL_TYPE_WLAN, 760 | &hp_wmi_rfkill_ops, 761 | (void *) HPWMI_WIFI); 762 | if (!wifi_rfkill) 763 | return -ENOMEM; 764 | rfkill_init_sw_state(wifi_rfkill, 765 | hp_wmi_get_sw_state(HPWMI_WIFI)); 766 | rfkill_set_hw_state(wifi_rfkill, 767 | hp_wmi_get_hw_state(HPWMI_WIFI)); 768 | err = rfkill_register(wifi_rfkill); 769 | if (err) 770 | goto register_wifi_error; 771 | } 772 | 773 | if (wireless & 0x2) { 774 | bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, 775 | RFKILL_TYPE_BLUETOOTH, 776 | &hp_wmi_rfkill_ops, 777 | (void *) HPWMI_BLUETOOTH); 778 | if (!bluetooth_rfkill) { 779 | err = -ENOMEM; 780 | goto register_bluetooth_error; 781 | } 782 | rfkill_init_sw_state(bluetooth_rfkill, 783 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); 784 | rfkill_set_hw_state(bluetooth_rfkill, 785 | hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); 786 | err = rfkill_register(bluetooth_rfkill); 787 | if (err) 788 | goto register_bluetooth_error; 789 | } 790 | 791 | if (wireless & 0x4) { 792 | wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, 793 | RFKILL_TYPE_WWAN, 794 | &hp_wmi_rfkill_ops, 795 | (void *) HPWMI_WWAN); 796 | if (!wwan_rfkill) { 797 | err = -ENOMEM; 798 | goto register_wwan_error; 799 | } 800 | rfkill_init_sw_state(wwan_rfkill, 801 | hp_wmi_get_sw_state(HPWMI_WWAN)); 802 | rfkill_set_hw_state(wwan_rfkill, 803 | hp_wmi_get_hw_state(HPWMI_WWAN)); 804 | err = rfkill_register(wwan_rfkill); 805 | if (err) 806 | goto register_wwan_error; 807 | } 808 | 809 | return 0; 810 | 811 | register_wwan_error: 812 | rfkill_destroy(wwan_rfkill); 813 | wwan_rfkill = NULL; 814 | if (bluetooth_rfkill) 815 | rfkill_unregister(bluetooth_rfkill); 816 | register_bluetooth_error: 817 | rfkill_destroy(bluetooth_rfkill); 818 | bluetooth_rfkill = NULL; 819 | if (wifi_rfkill) 820 | rfkill_unregister(wifi_rfkill); 821 | register_wifi_error: 822 | rfkill_destroy(wifi_rfkill); 823 | wifi_rfkill = NULL; 824 | return err; 825 | } 826 | 827 | static int __init hp_wmi_rfkill2_setup(struct platform_device *device) 828 | { 829 | struct bios_rfkill2_state state; 830 | int err, i; 831 | 832 | err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, 833 | sizeof(state), sizeof(state)); 834 | if (err) 835 | return err < 0 ? err : -EINVAL; 836 | 837 | if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { 838 | pr_warn("unable to parse 0x1b query output\n"); 839 | return -EINVAL; 840 | } 841 | 842 | for (i = 0; i < state.count; i++) { 843 | struct rfkill *rfkill; 844 | enum rfkill_type type; 845 | char *name; 846 | switch (state.device[i].radio_type) { 847 | case HPWMI_WIFI: 848 | type = RFKILL_TYPE_WLAN; 849 | name = "hp-wifi"; 850 | break; 851 | case HPWMI_BLUETOOTH: 852 | type = RFKILL_TYPE_BLUETOOTH; 853 | name = "hp-bluetooth"; 854 | break; 855 | case HPWMI_WWAN: 856 | type = RFKILL_TYPE_WWAN; 857 | name = "hp-wwan"; 858 | break; 859 | case HPWMI_GPS: 860 | type = RFKILL_TYPE_GPS; 861 | name = "hp-gps"; 862 | break; 863 | default: 864 | pr_warn("unknown device type 0x%x\n", 865 | state.device[i].radio_type); 866 | continue; 867 | } 868 | 869 | if (!state.device[i].vendor_id) { 870 | pr_warn("zero device %d while %d reported\n", 871 | i, state.count); 872 | continue; 873 | } 874 | 875 | rfkill = rfkill_alloc(name, &device->dev, type, 876 | &hp_wmi_rfkill2_ops, (void *)(long)i); 877 | if (!rfkill) { 878 | err = -ENOMEM; 879 | goto fail; 880 | } 881 | 882 | rfkill2[rfkill2_count].id = state.device[i].rfkill_id; 883 | rfkill2[rfkill2_count].num = i; 884 | rfkill2[rfkill2_count].rfkill = rfkill; 885 | 886 | rfkill_init_sw_state(rfkill, IS_SWBLOCKED(state.device[i].power)); 887 | rfkill_set_hw_state(rfkill, IS_HWBLOCKED(state.device[i].power)); 888 | 889 | if (!(state.device[i].power & HPWMI_POWER_BIOS)) 890 | pr_info("device %s blocked by BIOS\n", name); 891 | 892 | err = rfkill_register(rfkill); 893 | if (err) { 894 | rfkill_destroy(rfkill); 895 | goto fail; 896 | } 897 | 898 | rfkill2_count++; 899 | } 900 | 901 | return 0; 902 | fail: 903 | for (; rfkill2_count > 0; rfkill2_count--) { 904 | rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); 905 | rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); 906 | } 907 | return err; 908 | } 909 | 910 | /* Support for the HP Omen FourZone keyboard lighting */ 911 | 912 | #define FOURZONE_COUNT 4 913 | 914 | struct color_platform { 915 | u8 blue; 916 | u8 green; 917 | u8 red; 918 | } __packed; 919 | 920 | struct platform_zone { 921 | u8 offset; 922 | struct device_attribute *attr; 923 | struct color_platform colors; 924 | }; 925 | 926 | static struct device_attribute *zone_dev_attrs; 927 | static struct attribute **zone_attrs; 928 | static struct platform_zone *zone_data; 929 | 930 | static struct attribute_group zone_attribute_group = { 931 | .name = "rgb_zones", 932 | }; 933 | 934 | /* 935 | * Helpers used for zone control 936 | */ 937 | static int parse_rgb(const char *buf, struct platform_zone *zone) 938 | { 939 | long unsigned int rgb; 940 | int ret; 941 | union color_union { 942 | struct color_platform cp; 943 | int package; 944 | } repackager; 945 | 946 | ret = kstrtoul(buf, 16, &rgb); 947 | if (ret) 948 | return ret; 949 | 950 | /* RGB triplet notation is 24-bit hexadecimal */ 951 | if (rgb > 0xFFFFFF) 952 | return -EINVAL; 953 | 954 | repackager.package = rgb; 955 | pr_debug("hp-wmi: r:%d g:%d b:%d\n", 956 | repackager.cp.red, repackager.cp.green, repackager.cp.blue); 957 | zone->colors = repackager.cp; 958 | return 0; 959 | } 960 | 961 | static struct platform_zone *match_zone(struct device_attribute *attr) 962 | { 963 | u8 zone; 964 | 965 | for (zone = 0; zone < FOURZONE_COUNT; zone++) { 966 | if ((struct device_attribute *)zone_data[zone].attr == attr) { 967 | pr_debug("hp-wmi: matched zone location: %d\n", 968 | zone_data[zone].offset); 969 | return &zone_data[zone]; 970 | } 971 | } 972 | return NULL; 973 | } 974 | 975 | /* 976 | * Individual RGB zone control 977 | */ 978 | static int fourzone_update_led(struct platform_zone *zone, enum hp_wmi_command read_or_write) 979 | { 980 | u8 state[128]; 981 | 982 | int ret = hp_wmi_perform_query(HPWMI_FOURZONE_COLOR_GET, HPWMI_FOURZONE, &state, 983 | sizeof(state), sizeof(state)); 984 | 985 | if (ret) { 986 | pr_warn("fourzone_color_get returned error 0x%x\n", ret); 987 | return ret <= 0 ? ret : -EINVAL; 988 | } 989 | 990 | if (read_or_write == HPWMI_WRITE) { 991 | // Zones start at offset 25. Wonder what's in the rest of the buffer? 992 | state[zone->offset + 0] = zone->colors.red; 993 | state[zone->offset + 1] = zone->colors.green; 994 | state[zone->offset + 2] = zone->colors.blue; 995 | 996 | ret = hp_wmi_perform_query(HPWMI_FOURZONE_COLOR_SET, HPWMI_FOURZONE, &state, 997 | sizeof(state), sizeof(state)); 998 | 999 | if (ret) 1000 | pr_warn("fourzone_color_set returned error 0x%x\n", ret); 1001 | return ret; 1002 | 1003 | } else { 1004 | zone->colors.red = state[zone->offset + 0]; 1005 | zone->colors.green = state[zone->offset + 1]; 1006 | zone->colors.blue = state[zone->offset + 2]; 1007 | } 1008 | return 0; 1009 | } 1010 | 1011 | static ssize_t zone_show(struct device *dev, struct device_attribute *attr, 1012 | char *buf) 1013 | { 1014 | struct platform_zone *target_zone; 1015 | int ret; 1016 | 1017 | target_zone = match_zone(attr); 1018 | if (target_zone == NULL) 1019 | return sprintf(buf, "red: -1, green: -1, blue: -1\n"); 1020 | 1021 | ret = fourzone_update_led(target_zone, HPWMI_READ); 1022 | 1023 | if (ret) 1024 | return sprintf(buf, "red: -1, green: -1, blue: -1\n"); 1025 | 1026 | return sprintf(buf, "red: %d, green: %d, blue: %d\n", 1027 | target_zone->colors.red, 1028 | target_zone->colors.green, target_zone->colors.blue); 1029 | 1030 | } 1031 | 1032 | static ssize_t zone_set(struct device *dev, struct device_attribute *attr, 1033 | const char *buf, size_t count) 1034 | { 1035 | struct platform_zone *target_zone; 1036 | int ret; 1037 | target_zone = match_zone(attr); 1038 | if (target_zone == NULL) { 1039 | pr_err("hp-wmi: invalid target zone\n"); 1040 | return 1; 1041 | } 1042 | ret = parse_rgb(buf, target_zone); 1043 | if (ret) 1044 | return ret; 1045 | ret = fourzone_update_led(target_zone, HPWMI_WRITE); 1046 | return ret ? ret : count; 1047 | } 1048 | 1049 | /* 1050 | static void global_led_set(struct led_classdev *led_cdev, 1051 | enum led_brightness brightness) 1052 | { 1053 | int ret; 1054 | global_brightness = brightness; 1055 | ret = alienware_update_led(&zone_data[0]); 1056 | if (ret) 1057 | pr_err("LED brightness update failed\n"); 1058 | } 1059 | 1060 | static enum led_brightness global_led_get(struct led_classdev *led_cdev) 1061 | { 1062 | return global_brightness; 1063 | } 1064 | 1065 | static struct led_classdev global_led = { 1066 | .brightness_set = global_led_set, 1067 | .brightness_get = global_led_get, 1068 | .name = "hp-omen::global_brightness", 1069 | }; 1070 | */ 1071 | 1072 | // static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, 1073 | // store_control_state); 1074 | 1075 | static int fourzone_setup(struct platform_device *dev) 1076 | { 1077 | u8 zone; 1078 | char buffer[10]; 1079 | char *name; 1080 | 1081 | if (!quirks->fourzone) 1082 | return 0; 1083 | 1084 | // global_led.max_brightness = 0x0F; 1085 | // global_brightness = global_led.max_brightness; 1086 | 1087 | /* 1088 | * - zone_dev_attrs num_zones + 1 is for individual zones and then 1089 | * null terminated 1090 | * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + 1091 | * the lighting control + null terminated 1092 | * - zone_data num_zones is for the distinct zones 1093 | */ 1094 | 1095 | zone_dev_attrs = 1096 | kcalloc(FOURZONE_COUNT + 1, sizeof(struct device_attribute), 1097 | GFP_KERNEL); 1098 | if (!zone_dev_attrs) 1099 | return -ENOMEM; 1100 | 1101 | zone_attrs = 1102 | kcalloc(FOURZONE_COUNT + 1 /* 2 */, sizeof(struct attribute *), 1103 | GFP_KERNEL); 1104 | if (!zone_attrs) 1105 | return -ENOMEM; 1106 | 1107 | zone_data = 1108 | kcalloc(FOURZONE_COUNT, sizeof(struct platform_zone), 1109 | GFP_KERNEL); 1110 | if (!zone_data) 1111 | return -ENOMEM; 1112 | 1113 | for (zone = 0; zone < FOURZONE_COUNT; zone++) { 1114 | sprintf(buffer, "zone%02hhX", zone); 1115 | name = kstrdup(buffer, GFP_KERNEL); 1116 | if (name == NULL) 1117 | return 1; 1118 | sysfs_attr_init(&zone_dev_attrs[zone].attr); 1119 | zone_dev_attrs[zone].attr.name = name; 1120 | zone_dev_attrs[zone].attr.mode = 0644; 1121 | zone_dev_attrs[zone].show = zone_show; 1122 | zone_dev_attrs[zone].store = zone_set; 1123 | zone_data[zone].offset = 25 + (zone * 3); 1124 | zone_attrs[zone] = &zone_dev_attrs[zone].attr; 1125 | zone_data[zone].attr = &zone_dev_attrs[zone]; 1126 | } 1127 | // zone_attrs[FOURZONE_COUNT] = &dev_attr_lighting_control_state.attr; 1128 | zone_attribute_group.attrs = zone_attrs; 1129 | 1130 | // led_classdev_register(&dev->dev, &global_led); 1131 | 1132 | return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); 1133 | } 1134 | 1135 | static int __init hp_wmi_bios_setup(struct platform_device *device) 1136 | { 1137 | int err; 1138 | 1139 | /* clear detected rfkill devices */ 1140 | wifi_rfkill = NULL; 1141 | bluetooth_rfkill = NULL; 1142 | wwan_rfkill = NULL; 1143 | rfkill2_count = 0; 1144 | 1145 | if (hp_wmi_rfkill_setup(device)) 1146 | hp_wmi_rfkill2_setup(device); 1147 | 1148 | fourzone_setup(device); 1149 | 1150 | err = device_create_file(&device->dev, &dev_attr_display); 1151 | if (err) 1152 | goto add_sysfs_error; 1153 | err = device_create_file(&device->dev, &dev_attr_hddtemp); 1154 | if (err) 1155 | goto add_sysfs_error; 1156 | err = device_create_file(&device->dev, &dev_attr_als); 1157 | if (err) 1158 | goto add_sysfs_error; 1159 | err = device_create_file(&device->dev, &dev_attr_dock); 1160 | if (err) 1161 | goto add_sysfs_error; 1162 | err = device_create_file(&device->dev, &dev_attr_tablet); 1163 | if (err) 1164 | goto add_sysfs_error; 1165 | err = device_create_file(&device->dev, &dev_attr_postcode); 1166 | if (err) 1167 | goto add_sysfs_error; 1168 | 1169 | return 0; 1170 | 1171 | add_sysfs_error: 1172 | cleanup_sysfs(device); 1173 | return err; 1174 | } 1175 | 1176 | static int __exit hp_wmi_bios_remove(struct platform_device *device) 1177 | { 1178 | int i; 1179 | cleanup_sysfs(device); 1180 | 1181 | for (i = 0; i < rfkill2_count; i++) { 1182 | rfkill_unregister(rfkill2[i].rfkill); 1183 | rfkill_destroy(rfkill2[i].rfkill); 1184 | } 1185 | 1186 | if (wifi_rfkill) { 1187 | rfkill_unregister(wifi_rfkill); 1188 | rfkill_destroy(wifi_rfkill); 1189 | } 1190 | if (bluetooth_rfkill) { 1191 | rfkill_unregister(bluetooth_rfkill); 1192 | rfkill_destroy(bluetooth_rfkill); 1193 | } 1194 | if (wwan_rfkill) { 1195 | rfkill_unregister(wwan_rfkill); 1196 | rfkill_destroy(wwan_rfkill); 1197 | } 1198 | 1199 | return 0; 1200 | } 1201 | 1202 | static int hp_wmi_resume_handler(struct device *device) 1203 | { 1204 | /* 1205 | * Hardware state may have changed while suspended, so trigger 1206 | * input events for the current state. As this is a switch, 1207 | * the input layer will only actually pass it on if the state 1208 | * changed. 1209 | */ 1210 | if (hp_wmi_input_dev) { 1211 | if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) 1212 | input_report_switch(hp_wmi_input_dev, SW_DOCK, 1213 | hp_wmi_hw_state(HPWMI_DOCK_MASK)); 1214 | if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) 1215 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, 1216 | hp_wmi_hw_state(HPWMI_TABLET_MASK)); 1217 | input_sync(hp_wmi_input_dev); 1218 | } 1219 | 1220 | if (rfkill2_count) 1221 | hp_wmi_rfkill2_refresh(); 1222 | 1223 | if (wifi_rfkill) 1224 | rfkill_set_states(wifi_rfkill, 1225 | hp_wmi_get_sw_state(HPWMI_WIFI), 1226 | hp_wmi_get_hw_state(HPWMI_WIFI)); 1227 | if (bluetooth_rfkill) 1228 | rfkill_set_states(bluetooth_rfkill, 1229 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH), 1230 | hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); 1231 | if (wwan_rfkill) 1232 | rfkill_set_states(wwan_rfkill, 1233 | hp_wmi_get_sw_state(HPWMI_WWAN), 1234 | hp_wmi_get_hw_state(HPWMI_WWAN)); 1235 | 1236 | return 0; 1237 | } 1238 | 1239 | static const struct dev_pm_ops hp_wmi_pm_ops = { 1240 | .resume = hp_wmi_resume_handler, 1241 | .restore = hp_wmi_resume_handler, 1242 | }; 1243 | 1244 | static struct platform_driver hp_wmi_driver = { 1245 | .driver = { 1246 | .name = "hp-wmi", 1247 | .pm = &hp_wmi_pm_ops, 1248 | }, 1249 | .remove = __exit_p(hp_wmi_bios_remove), 1250 | }; 1251 | 1252 | static int __init hp_wmi_init(void) 1253 | { 1254 | int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); 1255 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); 1256 | int err; 1257 | 1258 | if (!bios_capable && !event_capable) 1259 | return -ENODEV; 1260 | 1261 | if (event_capable) { 1262 | err = hp_wmi_input_setup(); 1263 | if (err) 1264 | return err; 1265 | } 1266 | 1267 | if (bios_capable) { 1268 | hp_wmi_platform_dev = 1269 | platform_device_register_simple("hp-wmi", -1, NULL, 0); 1270 | if (IS_ERR(hp_wmi_platform_dev)) { 1271 | err = PTR_ERR(hp_wmi_platform_dev); 1272 | goto err_destroy_input; 1273 | } 1274 | 1275 | err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); 1276 | if (err) 1277 | goto err_unregister_device; 1278 | } 1279 | 1280 | return 0; 1281 | 1282 | err_unregister_device: 1283 | platform_device_unregister(hp_wmi_platform_dev); 1284 | err_destroy_input: 1285 | if (event_capable) 1286 | hp_wmi_input_destroy(); 1287 | 1288 | return err; 1289 | } 1290 | module_init(hp_wmi_init); 1291 | 1292 | static void __exit hp_wmi_exit(void) 1293 | { 1294 | if (wmi_has_guid(HPWMI_EVENT_GUID)) 1295 | hp_wmi_input_destroy(); 1296 | 1297 | if (hp_wmi_platform_dev) { 1298 | platform_device_unregister(hp_wmi_platform_dev); 1299 | platform_driver_unregister(&hp_wmi_driver); 1300 | } 1301 | } 1302 | module_exit(hp_wmi_exit); 1303 | --------------------------------------------------------------------------------