blob: a8aa2eadfd82cbf6d0d9d39e35a67de4beecbc0e [file] [log] [blame]
Yong Wangee027e42010-03-21 10:26:34 +08001/*
Corentin Charye12e6d92011-02-26 10:20:31 +01002 * Asus PC WMI hotkey driver
Yong Wangee027e42010-03-21 10:26:34 +08003 *
4 * Copyright(C) 2010 Intel Corporation.
Corentin Chary57ab7da2011-02-26 10:20:32 +01005 * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
Yong Wangee027e42010-03-21 10:26:34 +08006 *
7 * Portions based on wistron_btns.c:
8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
9 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
10 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
Yong Wang81248882010-04-11 09:26:33 +080027#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
Yong Wangee027e42010-03-21 10:26:34 +080029#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/types.h>
Tejun Heoa32f3922010-04-05 11:37:59 +090033#include <linux/slab.h>
Yong Wangee027e42010-03-21 10:26:34 +080034#include <linux/input.h>
35#include <linux/input/sparse-keymap.h>
Yong Wang3d7b1652010-04-11 09:27:54 +080036#include <linux/fb.h>
37#include <linux/backlight.h>
Corentin Chary084fca62010-11-29 08:14:06 +010038#include <linux/leds.h>
Corentin Charyba48fdb2010-11-29 08:14:07 +010039#include <linux/rfkill.h>
Corentin Charyafa7c882011-02-06 13:28:28 +010040#include <linux/pci.h>
41#include <linux/pci_hotplug.h>
Corentin Charye07babd2011-02-26 10:20:42 +010042#include <linux/hwmon.h>
43#include <linux/hwmon-sysfs.h>
Corentin Chary8c1b2d82010-11-29 08:14:09 +010044#include <linux/debugfs.h>
45#include <linux/seq_file.h>
Yong Wang45f2c692010-04-11 09:27:19 +080046#include <linux/platform_device.h>
Corentin Chary6118b8a2011-07-01 11:34:36 +020047#include <linux/thermal.h>
Lv Zheng8b484632013-12-03 08:49:16 +080048#include <linux/acpi.h>
Hans de Goeded8c66f62014-07-08 10:47:21 +020049#include <linux/dmi.h>
AceLan Kao272c77d2012-06-13 09:32:06 +020050#include <acpi/video.h>
Yong Wangee027e42010-03-21 10:26:34 +080051
Corentin Charye12e6d92011-02-26 10:20:31 +010052#include "asus-wmi.h"
Yong Wang45f2c692010-04-11 09:27:19 +080053
Corentin Chary5909c652012-12-17 16:00:05 -080054MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>, "
Corentin Charye12e6d92011-02-26 10:20:31 +010055 "Yong Wang <yong.y.wang@intel.com>");
56MODULE_DESCRIPTION("Asus Generic WMI Driver");
Yong Wangee027e42010-03-21 10:26:34 +080057MODULE_LICENSE("GPL");
58
Corentin Charye12e6d92011-02-26 10:20:31 +010059#define to_asus_wmi_driver(pdrv) \
60 (container_of((pdrv), struct asus_wmi_driver, platform_driver))
Yong Wangee027e42010-03-21 10:26:34 +080061
Corentin Charye12e6d92011-02-26 10:20:31 +010062#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
Yong Wangee027e42010-03-21 10:26:34 +080063
Corentin Chary33e0e6f2011-02-06 13:28:34 +010064#define NOTIFY_BRNUP_MIN 0x11
65#define NOTIFY_BRNUP_MAX 0x1f
66#define NOTIFY_BRNDOWN_MIN 0x20
67#define NOTIFY_BRNDOWN_MAX 0x2e
Corentin Charye9809c02011-07-01 11:34:31 +020068#define NOTIFY_KBD_BRTUP 0xc4
69#define NOTIFY_KBD_BRTDWN 0xc5
Chris Chiued99d292018-06-20 22:46:45 +080070#define NOTIFY_KBD_BRTTOGGLE 0xc7
Yong Wangee027e42010-03-21 10:26:34 +080071
Corentin Chary43815942011-02-06 13:28:43 +010072/* WMI Methods */
Corentin Chary2f686b52011-02-26 10:20:41 +010073#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
74#define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */
75#define ASUS_WMI_METHODID_GLCD 0x44434C47 /* Get LCD status */
76#define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */
77#define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */
78#define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */
Kast Bernd53e755c2015-05-13 16:24:16 +020079#define ASUS_WMI_METHODID_AGFN 0x4E464741 /* FaN? */
Corentin Chary2f686b52011-02-26 10:20:41 +010080#define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */
81#define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */
82#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */
83#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */
84#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */
85#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */
86#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/
87#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */
88#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */
89#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */
90#define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */
91#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */
92#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */
Yong Wang3d7b1652010-04-11 09:27:54 +080093
Corentin Charyd33da3b2011-02-26 10:20:35 +010094#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE
95
Corentin Chary43815942011-02-06 13:28:43 +010096/* Wireless */
Corentin Chary2f686b52011-02-26 10:20:41 +010097#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001
98#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
Corentin Chary79ec1172011-07-01 11:34:35 +020099#define ASUS_WMI_DEVID_CWAP 0x00010003
Corentin Charye12e6d92011-02-26 10:20:31 +0100100#define ASUS_WMI_DEVID_WLAN 0x00010011
AceLan Kaoa50bd122012-07-26 17:13:31 +0800101#define ASUS_WMI_DEVID_WLAN_LED 0x00010012
Corentin Charye12e6d92011-02-26 10:20:31 +0100102#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
Corentin Chary2f686b52011-02-26 10:20:41 +0100103#define ASUS_WMI_DEVID_GPS 0x00010015
Corentin Charye12e6d92011-02-26 10:20:31 +0100104#define ASUS_WMI_DEVID_WIMAX 0x00010017
105#define ASUS_WMI_DEVID_WWAN3G 0x00010019
Corentin Chary2f686b52011-02-26 10:20:41 +0100106#define ASUS_WMI_DEVID_UWB 0x00010021
107
108/* Leds */
109/* 0x000200XX and 0x000400XX */
Corentin Chary79ec1172011-07-01 11:34:35 +0200110#define ASUS_WMI_DEVID_LED1 0x00020011
111#define ASUS_WMI_DEVID_LED2 0x00020012
112#define ASUS_WMI_DEVID_LED3 0x00020013
113#define ASUS_WMI_DEVID_LED4 0x00020014
114#define ASUS_WMI_DEVID_LED5 0x00020015
115#define ASUS_WMI_DEVID_LED6 0x00020016
Corentin Chary43815942011-02-06 13:28:43 +0100116
117/* Backlight and Brightness */
Oleksij Rempelaca234f2016-04-01 13:35:21 +0200118#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */
Corentin Charye12e6d92011-02-26 10:20:31 +0100119#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011
120#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012
Corentin Chary2f686b52011-02-26 10:20:41 +0100121#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
122#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
Maxime Bellengé4c059842017-09-23 18:23:37 +0200123#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
Corentin Chary43815942011-02-06 13:28:43 +0100124
125/* Misc */
Corentin Charye12e6d92011-02-26 10:20:31 +0100126#define ASUS_WMI_DEVID_CAMERA 0x00060013
Corentin Chary43815942011-02-06 13:28:43 +0100127
128/* Storage */
Corentin Charye12e6d92011-02-26 10:20:31 +0100129#define ASUS_WMI_DEVID_CARDREADER 0x00080013
Corentin Chary43815942011-02-06 13:28:43 +0100130
131/* Input */
Corentin Chary57ab7da2011-02-26 10:20:32 +0100132#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011
Corentin Charye12e6d92011-02-26 10:20:31 +0100133#define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012
Yong Wang3d7b1652010-04-11 09:27:54 +0800134
Corentin Chary2f686b52011-02-26 10:20:41 +0100135/* Fan, Thermal */
136#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011
137#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012
138
139/* Power */
140#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012
141
AceLan Kaoc0b91b62012-06-13 09:32:07 +0200142/* Deep S3 / Resume on LID open */
143#define ASUS_WMI_DEVID_LID_RESUME 0x00120031
144
Corentin Chary43815942011-02-06 13:28:43 +0100145/* DSTS masks */
Corentin Charye12e6d92011-02-26 10:20:31 +0100146#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001
Corentin Charya75fe0d2011-02-26 10:20:34 +0100147#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002
Corentin Charye12e6d92011-02-26 10:20:31 +0100148#define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000
Corentin Chary2f686b52011-02-26 10:20:41 +0100149#define ASUS_WMI_DSTS_USER_BIT 0x00020000
150#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000
Corentin Charye12e6d92011-02-26 10:20:31 +0100151#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF
152#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00
Maxime Bellengé4c059842017-09-23 18:23:37 +0200153#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F
Yong Wangee027e42010-03-21 10:26:34 +0800154
Kast Bernd53e755c2015-05-13 16:24:16 +0200155#define ASUS_FAN_DESC "cpu_fan"
156#define ASUS_FAN_MFUN 0x13
157#define ASUS_FAN_SFUN_READ 0x06
158#define ASUS_FAN_SFUN_WRITE 0x07
159#define ASUS_FAN_CTRL_MANUAL 1
160#define ASUS_FAN_CTRL_AUTO 2
161
Kai-Chuan Hsieh8023eff2016-09-01 23:55:55 +0800162#define USB_INTEL_XUSB2PR 0xD0
163#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
164
João Paulo Rechi Vita71050ae2017-02-20 14:50:22 -0500165static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
166
João Paulo Rechi Vitacf48bf92018-05-22 14:30:15 -0700167static bool ashs_present(void)
168{
169 int i = 0;
170 while (ashs_ids[i]) {
171 if (acpi_dev_found(ashs_ids[i++]))
172 return true;
173 }
174 return false;
175}
176
Yong Wang3d7b1652010-04-11 09:27:54 +0800177struct bios_args {
Corentin Charyd33da3b2011-02-26 10:20:35 +0100178 u32 arg0;
179 u32 arg1;
180} __packed;
Yong Wang3d7b1652010-04-11 09:27:54 +0800181
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100182/*
Kast Bernd53e755c2015-05-13 16:24:16 +0200183 * Struct that's used for all methods called via AGFN. Naming is
184 * identically to the AML code.
185 */
186struct agfn_args {
187 u16 mfun; /* probably "Multi-function" to be called */
188 u16 sfun; /* probably "Sub-function" to be called */
189 u16 len; /* size of the hole struct, including subfunction fields */
190 u8 stas; /* not used by now */
191 u8 err; /* zero on success */
192} __packed;
193
194/* struct used for calling fan read and write methods */
195struct fan_args {
196 struct agfn_args agfn; /* common fields */
197 u8 fan; /* fan number: 0: set auto mode 1: 1st fan */
198 u32 speed; /* read: RPM/100 - write: 0-255 */
199} __packed;
200
201/*
Corentin Charye12e6d92011-02-26 10:20:31 +0100202 * <platform>/ - debugfs root directory
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100203 * dev_id - current dev_id
204 * ctrl_param - current ctrl_param
Corentin Charyef343492011-02-26 10:20:39 +0100205 * method_id - current method_id
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100206 * devs - call DEVS(dev_id, ctrl_param) and print result
207 * dsts - call DSTS(dev_id) and print result
Corentin Charyef343492011-02-26 10:20:39 +0100208 * call - call method_id(dev_id, ctrl_param) and print result
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100209 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100210struct asus_wmi_debug {
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100211 struct dentry *root;
Corentin Charyef343492011-02-26 10:20:39 +0100212 u32 method_id;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100213 u32 dev_id;
214 u32 ctrl_param;
215};
216
Corentin Charya7ce3f02011-02-26 10:20:33 +0100217struct asus_rfkill {
218 struct asus_wmi *asus;
219 struct rfkill *rfkill;
220 u32 dev_id;
221};
222
Corentin Charye12e6d92011-02-26 10:20:31 +0100223struct asus_wmi {
Corentin Chary1d070f82011-02-26 10:20:36 +0100224 int dsts_id;
Corentin Chary46dbca82011-02-26 10:20:38 +0100225 int spec;
226 int sfun;
Corentin Chary1d070f82011-02-26 10:20:36 +0100227
Yong Wang81248882010-04-11 09:26:33 +0800228 struct input_dev *inputdev;
Yong Wang3d7b1652010-04-11 09:27:54 +0800229 struct backlight_device *backlight_device;
Corentin Chary27c136c2010-11-29 08:14:05 +0100230 struct platform_device *platform_device;
Corentin Chary084fca62010-11-29 08:14:06 +0100231
AceLan Kao6cae06e2012-07-27 16:51:59 +0800232 struct led_classdev wlan_led;
233 int wlan_led_wk;
Corentin Chary084fca62010-11-29 08:14:06 +0100234 struct led_classdev tpd_led;
235 int tpd_led_wk;
Corentin Charye9809c02011-07-01 11:34:31 +0200236 struct led_classdev kbd_led;
237 int kbd_led_wk;
Maxime Bellengé4c059842017-09-23 18:23:37 +0200238 struct led_classdev lightbar_led;
239 int lightbar_led_wk;
Corentin Chary084fca62010-11-29 08:14:06 +0100240 struct workqueue_struct *led_workqueue;
241 struct work_struct tpd_led_work;
Corentin Charye9809c02011-07-01 11:34:31 +0200242 struct work_struct kbd_led_work;
AceLan Kao6cae06e2012-07-27 16:51:59 +0800243 struct work_struct wlan_led_work;
Maxime Bellengé4c059842017-09-23 18:23:37 +0200244 struct work_struct lightbar_led_work;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100245
Corentin Charya7ce3f02011-02-26 10:20:33 +0100246 struct asus_rfkill wlan;
247 struct asus_rfkill bluetooth;
248 struct asus_rfkill wimax;
249 struct asus_rfkill wwan3g;
Corentin Chary43be8bd2011-07-01 11:34:40 +0200250 struct asus_rfkill gps;
Corentin Charya912d322011-07-01 11:34:41 +0200251 struct asus_rfkill uwb;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100252
Kast Bernd53e755c2015-05-13 16:24:16 +0200253 bool asus_hwmon_fan_manual_mode;
254 int asus_hwmon_num_fans;
255 int asus_hwmon_pwm;
256
Corentin Charyafa7c882011-02-06 13:28:28 +0100257 struct hotplug_slot *hotplug_slot;
258 struct mutex hotplug_lock;
Corentin Chary279f8f92011-02-06 13:28:29 +0100259 struct mutex wmi_lock;
260 struct workqueue_struct *hotplug_workqueue;
261 struct work_struct hotplug_work;
Corentin Charyafa7c882011-02-06 13:28:28 +0100262
Corentin Charye12e6d92011-02-26 10:20:31 +0100263 struct asus_wmi_debug debug;
264
265 struct asus_wmi_driver *driver;
Yong Wang81248882010-04-11 09:26:33 +0800266};
267
Corentin Charye12e6d92011-02-26 10:20:31 +0100268static int asus_wmi_input_init(struct asus_wmi *asus)
Yong Wangee027e42010-03-21 10:26:34 +0800269{
270 int err;
271
Corentin Charye12e6d92011-02-26 10:20:31 +0100272 asus->inputdev = input_allocate_device();
273 if (!asus->inputdev)
Yong Wangee027e42010-03-21 10:26:34 +0800274 return -ENOMEM;
275
Corentin Chary58a9f392011-03-30 16:32:32 +0200276 asus->inputdev->name = asus->driver->input_name;
277 asus->inputdev->phys = asus->driver->input_phys;
Corentin Charye12e6d92011-02-26 10:20:31 +0100278 asus->inputdev->id.bustype = BUS_HOST;
279 asus->inputdev->dev.parent = &asus->platform_device->dev;
Seth Forshee39bbde02011-07-04 09:49:20 +0200280 set_bit(EV_REP, asus->inputdev->evbit);
Yong Wangee027e42010-03-21 10:26:34 +0800281
Corentin Charye12e6d92011-02-26 10:20:31 +0100282 err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
Yong Wangee027e42010-03-21 10:26:34 +0800283 if (err)
284 goto err_free_dev;
285
Corentin Charye12e6d92011-02-26 10:20:31 +0100286 err = input_register_device(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800287 if (err)
Michał Kępieńc2ef3a12017-03-09 13:11:39 +0100288 goto err_free_dev;
Yong Wangee027e42010-03-21 10:26:34 +0800289
290 return 0;
291
Yong Wangee027e42010-03-21 10:26:34 +0800292err_free_dev:
Corentin Charye12e6d92011-02-26 10:20:31 +0100293 input_free_device(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800294 return err;
295}
296
Corentin Charye12e6d92011-02-26 10:20:31 +0100297static void asus_wmi_input_exit(struct asus_wmi *asus)
Yong Wang81248882010-04-11 09:26:33 +0800298{
Michał Kępieńc2ef3a12017-03-09 13:11:39 +0100299 if (asus->inputdev)
Corentin Charye12e6d92011-02-26 10:20:31 +0100300 input_unregister_device(asus->inputdev);
Yong Wang81248882010-04-11 09:26:33 +0800301
Corentin Charye12e6d92011-02-26 10:20:31 +0100302 asus->inputdev = NULL;
Yong Wang81248882010-04-11 09:26:33 +0800303}
304
Corentin Charyd33da3b2011-02-26 10:20:35 +0100305static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
306 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800307{
Corentin Charyd33da3b2011-02-26 10:20:35 +0100308 struct bios_args args = {
309 .arg0 = arg0,
310 .arg1 = arg1,
311 };
312 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
Yong Wang3d7b1652010-04-11 09:27:54 +0800313 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
Yong Wang3d7b1652010-04-11 09:27:54 +0800314 acpi_status status;
Corentin Charyd33da3b2011-02-26 10:20:35 +0100315 union acpi_object *obj;
Rickard Strandqvist8ad3be12014-06-01 14:58:52 +0200316 u32 tmp = 0;
Yong Wang3d7b1652010-04-11 09:27:54 +0800317
Pali Rohár0fe57262017-08-12 09:44:16 +0200318 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
Corentin Charyafa7c882011-02-06 13:28:28 +0100319 &input, &output);
Yong Wang3d7b1652010-04-11 09:27:54 +0800320
321 if (ACPI_FAILURE(status))
Corentin Charyd33da3b2011-02-26 10:20:35 +0100322 goto exit;
Yong Wang3d7b1652010-04-11 09:27:54 +0800323
324 obj = (union acpi_object *)output.pointer;
325 if (obj && obj->type == ACPI_TYPE_INTEGER)
Corentin Charye12e6d92011-02-26 10:20:31 +0100326 tmp = (u32) obj->integer.value;
Yong Wang3d7b1652010-04-11 09:27:54 +0800327
Corentin Chary2a3f0062010-11-29 08:14:10 +0100328 if (retval)
329 *retval = tmp;
Yong Wang3d7b1652010-04-11 09:27:54 +0800330
331 kfree(obj);
332
Corentin Charyd33da3b2011-02-26 10:20:35 +0100333exit:
334 if (ACPI_FAILURE(status))
335 return -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +0800336
Corentin Charyd33da3b2011-02-26 10:20:35 +0100337 if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
338 return -ENODEV;
339
340 return 0;
Yong Wang3d7b1652010-04-11 09:27:54 +0800341}
342
Kast Bernd53e755c2015-05-13 16:24:16 +0200343static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
344{
345 struct acpi_buffer input;
346 u64 phys_addr;
347 u32 retval;
348 u32 status = -1;
349
350 /*
351 * Copy to dma capable address otherwise memory corruption occurs as
352 * bios has to be able to access it.
353 */
354 input.pointer = kzalloc(args.length, GFP_DMA | GFP_KERNEL);
355 input.length = args.length;
356 if (!input.pointer)
357 return -ENOMEM;
358 phys_addr = virt_to_phys(input.pointer);
359 memcpy(input.pointer, args.pointer, args.length);
360
361 status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
362 phys_addr, 0, &retval);
363 if (!status)
364 memcpy(args.pointer, input.pointer, args.length);
365
366 kfree(input.pointer);
367 if (status)
368 return -ENXIO;
369
370 return retval;
371}
372
Corentin Chary1d070f82011-02-26 10:20:36 +0100373static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
Corentin Charyd33da3b2011-02-26 10:20:35 +0100374{
Corentin Chary1d070f82011-02-26 10:20:36 +0100375 return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
Corentin Charyd33da3b2011-02-26 10:20:35 +0100376}
377
378static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
Corentin Chary1d070f82011-02-26 10:20:36 +0100379 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800380{
Corentin Charyd33da3b2011-02-26 10:20:35 +0100381 return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
382 ctrl_param, retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800383}
384
Corentin Chary5c956382011-02-06 13:28:31 +0100385/* Helper for special devices with magic return codes */
Corentin Chary1d070f82011-02-26 10:20:36 +0100386static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
387 u32 dev_id, u32 mask)
Corentin Chary5c956382011-02-06 13:28:31 +0100388{
389 u32 retval = 0;
Corentin Charyd33da3b2011-02-26 10:20:35 +0100390 int err;
Corentin Chary5c956382011-02-06 13:28:31 +0100391
Corentin Chary1d070f82011-02-26 10:20:36 +0100392 err = asus_wmi_get_devstate(asus, dev_id, &retval);
Corentin Chary5c956382011-02-06 13:28:31 +0100393
Corentin Charyd33da3b2011-02-26 10:20:35 +0100394 if (err < 0)
395 return err;
Corentin Chary5c956382011-02-06 13:28:31 +0100396
Corentin Charye12e6d92011-02-26 10:20:31 +0100397 if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))
Corentin Chary5c956382011-02-06 13:28:31 +0100398 return -ENODEV;
399
Corentin Charya75fe0d2011-02-26 10:20:34 +0100400 if (mask == ASUS_WMI_DSTS_STATUS_BIT) {
401 if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)
402 return -ENODEV;
403 }
404
Corentin Charyb7187262011-02-06 13:28:39 +0100405 return retval & mask;
406}
407
Corentin Chary1d070f82011-02-26 10:20:36 +0100408static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
Corentin Charyb7187262011-02-06 13:28:39 +0100409{
Corentin Chary1d070f82011-02-26 10:20:36 +0100410 return asus_wmi_get_devstate_bits(asus, dev_id,
411 ASUS_WMI_DSTS_STATUS_BIT);
Corentin Chary5c956382011-02-06 13:28:31 +0100412}
413
Corentin Chary084fca62010-11-29 08:14:06 +0100414/*
415 * LEDs
416 */
417/*
418 * These functions actually update the LED's, and are called from a
419 * workqueue. By doing this as separate work rather than when the LED
Corentin Charye12e6d92011-02-26 10:20:31 +0100420 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
Corentin Chary084fca62010-11-29 08:14:06 +0100421 * potentially bad time, such as a timer interrupt.
422 */
423static void tpd_led_update(struct work_struct *work)
424{
425 int ctrl_param;
Corentin Charye12e6d92011-02-26 10:20:31 +0100426 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100427
Corentin Charye12e6d92011-02-26 10:20:31 +0100428 asus = container_of(work, struct asus_wmi, tpd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100429
Corentin Charye12e6d92011-02-26 10:20:31 +0100430 ctrl_param = asus->tpd_led_wk;
431 asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100432}
433
434static void tpd_led_set(struct led_classdev *led_cdev,
435 enum led_brightness value)
436{
Corentin Charye12e6d92011-02-26 10:20:31 +0100437 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100438
Corentin Charye12e6d92011-02-26 10:20:31 +0100439 asus = container_of(led_cdev, struct asus_wmi, tpd_led);
Corentin Chary084fca62010-11-29 08:14:06 +0100440
Corentin Charye12e6d92011-02-26 10:20:31 +0100441 asus->tpd_led_wk = !!value;
442 queue_work(asus->led_workqueue, &asus->tpd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100443}
444
Corentin Charye12e6d92011-02-26 10:20:31 +0100445static int read_tpd_led_state(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100446{
Corentin Chary1d070f82011-02-26 10:20:36 +0100447 return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);
Corentin Chary084fca62010-11-29 08:14:06 +0100448}
449
450static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
451{
Corentin Charye12e6d92011-02-26 10:20:31 +0100452 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100453
Corentin Charye12e6d92011-02-26 10:20:31 +0100454 asus = container_of(led_cdev, struct asus_wmi, tpd_led);
Corentin Chary084fca62010-11-29 08:14:06 +0100455
Corentin Charye12e6d92011-02-26 10:20:31 +0100456 return read_tpd_led_state(asus);
Corentin Chary084fca62010-11-29 08:14:06 +0100457}
458
Corentin Charye9809c02011-07-01 11:34:31 +0200459static void kbd_led_update(struct work_struct *work)
Corentin Chary084fca62010-11-29 08:14:06 +0100460{
Corentin Charye9809c02011-07-01 11:34:31 +0200461 int ctrl_param = 0;
462 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100463
Corentin Charye9809c02011-07-01 11:34:31 +0200464 asus = container_of(work, struct asus_wmi, kbd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100465
Corentin Charye9809c02011-07-01 11:34:31 +0200466 /*
467 * bits 0-2: level
468 * bit 7: light on/off
469 */
470 if (asus->kbd_led_wk > 0)
471 ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
Corentin Chary084fca62010-11-29 08:14:06 +0100472
Corentin Charye9809c02011-07-01 11:34:31 +0200473 asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
Chris Chiudbb3d782018-06-20 22:46:44 +0800474 led_classdev_notify_brightness_hw_changed(&asus->kbd_led, asus->kbd_led_wk);
Corentin Charye9809c02011-07-01 11:34:31 +0200475}
Corentin Chary084fca62010-11-29 08:14:06 +0100476
Corentin Charye9809c02011-07-01 11:34:31 +0200477static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
478{
479 int retval;
480
481 /*
482 * bits 0-2: level
483 * bit 7: light on/off
484 * bit 8-10: environment (0: dark, 1: normal, 2: light)
485 * bit 17: status unknown
486 */
487 retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
488 0xFFFF);
489
Corentin Charyaf965e92011-07-01 11:34:34 +0200490 /* Unknown status is considered as off */
Corentin Charye9809c02011-07-01 11:34:31 +0200491 if (retval == 0x8000)
Corentin Charyaf965e92011-07-01 11:34:34 +0200492 retval = 0;
Corentin Charye9809c02011-07-01 11:34:31 +0200493
494 if (retval >= 0) {
495 if (level)
Corentin Charyc09b2232012-03-20 09:53:04 +0100496 *level = retval & 0x7F;
Corentin Charye9809c02011-07-01 11:34:31 +0200497 if (env)
498 *env = (retval >> 8) & 0x7F;
499 retval = 0;
Corentin Chary084fca62010-11-29 08:14:06 +0100500 }
501
Corentin Charye9809c02011-07-01 11:34:31 +0200502 return retval;
503}
504
Chris Chiudbb3d782018-06-20 22:46:44 +0800505static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
Corentin Charye9809c02011-07-01 11:34:31 +0200506{
507 struct asus_wmi *asus;
Chris Chiudbb3d782018-06-20 22:46:44 +0800508 int max_level;
Corentin Charye9809c02011-07-01 11:34:31 +0200509
510 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
Chris Chiudbb3d782018-06-20 22:46:44 +0800511 max_level = asus->kbd_led.max_brightness;
Corentin Charye9809c02011-07-01 11:34:31 +0200512
Chris Chiudbb3d782018-06-20 22:46:44 +0800513 if (value > max_level)
514 value = max_level;
Corentin Charye9809c02011-07-01 11:34:31 +0200515 else if (value < 0)
516 value = 0;
517
518 asus->kbd_led_wk = value;
519 queue_work(asus->led_workqueue, &asus->kbd_led_work);
520}
521
Chris Chiudbb3d782018-06-20 22:46:44 +0800522static void kbd_led_set(struct led_classdev *led_cdev,
523 enum led_brightness value)
524{
525 do_kbd_led_set(led_cdev, value);
526}
527
Corentin Charye9809c02011-07-01 11:34:31 +0200528static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
529{
530 struct asus_wmi *asus;
531 int retval, value;
532
533 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
534
535 retval = kbd_led_read(asus, &value, NULL);
536
537 if (retval < 0)
538 return retval;
539
540 return value;
Corentin Chary084fca62010-11-29 08:14:06 +0100541}
542
AceLan Kao6cae06e2012-07-27 16:51:59 +0800543static int wlan_led_unknown_state(struct asus_wmi *asus)
544{
545 u32 result;
546
547 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
548
549 return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
550}
551
552static int wlan_led_presence(struct asus_wmi *asus)
553{
554 u32 result;
555
556 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
557
558 return result & ASUS_WMI_DSTS_PRESENCE_BIT;
559}
560
561static void wlan_led_update(struct work_struct *work)
562{
563 int ctrl_param;
564 struct asus_wmi *asus;
565
566 asus = container_of(work, struct asus_wmi, wlan_led_work);
567
568 ctrl_param = asus->wlan_led_wk;
569 asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
570}
571
572static void wlan_led_set(struct led_classdev *led_cdev,
573 enum led_brightness value)
574{
575 struct asus_wmi *asus;
576
577 asus = container_of(led_cdev, struct asus_wmi, wlan_led);
578
579 asus->wlan_led_wk = !!value;
580 queue_work(asus->led_workqueue, &asus->wlan_led_work);
581}
582
583static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
584{
585 struct asus_wmi *asus;
586 u32 result;
587
588 asus = container_of(led_cdev, struct asus_wmi, wlan_led);
589 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
590
591 return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
592}
593
Maxime Bellengé4c059842017-09-23 18:23:37 +0200594static void lightbar_led_update(struct work_struct *work)
595{
596 struct asus_wmi *asus;
597 int ctrl_param;
598
599 asus = container_of(work, struct asus_wmi, lightbar_led_work);
600
601 ctrl_param = asus->lightbar_led_wk;
602 asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);
603}
604
605static void lightbar_led_set(struct led_classdev *led_cdev,
606 enum led_brightness value)
607{
608 struct asus_wmi *asus;
609
610 asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
611
612 asus->lightbar_led_wk = !!value;
613 queue_work(asus->led_workqueue, &asus->lightbar_led_work);
614}
615
616static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
617{
618 struct asus_wmi *asus;
619 u32 result;
620
621 asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
622 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
623
624 return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
625}
626
627static int lightbar_led_presence(struct asus_wmi *asus)
628{
629 u32 result;
630
631 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
632
633 return result & ASUS_WMI_DSTS_PRESENCE_BIT;
634}
635
Corentin Charye12e6d92011-02-26 10:20:31 +0100636static void asus_wmi_led_exit(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100637{
Axel Line9298022011-08-08 17:16:01 +0800638 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
639 led_classdev_unregister(&asus->kbd_led);
640 if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
Corentin Charye12e6d92011-02-26 10:20:31 +0100641 led_classdev_unregister(&asus->tpd_led);
AceLan Kao6cae06e2012-07-27 16:51:59 +0800642 if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
643 led_classdev_unregister(&asus->wlan_led);
Maxime Bellengé4c059842017-09-23 18:23:37 +0200644 if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
645 led_classdev_unregister(&asus->lightbar_led);
Corentin Charye12e6d92011-02-26 10:20:31 +0100646 if (asus->led_workqueue)
647 destroy_workqueue(asus->led_workqueue);
Corentin Chary084fca62010-11-29 08:14:06 +0100648}
649
Corentin Charye9809c02011-07-01 11:34:31 +0200650static int asus_wmi_led_init(struct asus_wmi *asus)
651{
Oleksij Rempel30734042015-09-14 11:16:30 +0200652 int rv = 0, led_val;
Corentin Charye9809c02011-07-01 11:34:31 +0200653
654 asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
655 if (!asus->led_workqueue)
656 return -ENOMEM;
657
658 if (read_tpd_led_state(asus) >= 0) {
659 INIT_WORK(&asus->tpd_led_work, tpd_led_update);
660
661 asus->tpd_led.name = "asus::touchpad";
662 asus->tpd_led.brightness_set = tpd_led_set;
663 asus->tpd_led.brightness_get = tpd_led_get;
664 asus->tpd_led.max_brightness = 1;
665
666 rv = led_classdev_register(&asus->platform_device->dev,
667 &asus->tpd_led);
668 if (rv)
669 goto error;
670 }
671
Oleksij Rempel30734042015-09-14 11:16:30 +0200672 led_val = kbd_led_read(asus, NULL, NULL);
673 if (led_val >= 0) {
Corentin Charye9809c02011-07-01 11:34:31 +0200674 INIT_WORK(&asus->kbd_led_work, kbd_led_update);
675
Oleksij Rempel30734042015-09-14 11:16:30 +0200676 asus->kbd_led_wk = led_val;
Corentin Charye9809c02011-07-01 11:34:31 +0200677 asus->kbd_led.name = "asus::kbd_backlight";
Chris Chiudbb3d782018-06-20 22:46:44 +0800678 asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
Corentin Charye9809c02011-07-01 11:34:31 +0200679 asus->kbd_led.brightness_set = kbd_led_set;
680 asus->kbd_led.brightness_get = kbd_led_get;
681 asus->kbd_led.max_brightness = 3;
682
683 rv = led_classdev_register(&asus->platform_device->dev,
684 &asus->kbd_led);
AceLan Kao6cae06e2012-07-27 16:51:59 +0800685 if (rv)
686 goto error;
687 }
688
AceLan Kaof5156232014-07-09 16:18:18 +0800689 if (wlan_led_presence(asus) && (asus->driver->quirks->wapf > 0)) {
AceLan Kao6cae06e2012-07-27 16:51:59 +0800690 INIT_WORK(&asus->wlan_led_work, wlan_led_update);
691
692 asus->wlan_led.name = "asus::wlan";
693 asus->wlan_led.brightness_set = wlan_led_set;
694 if (!wlan_led_unknown_state(asus))
695 asus->wlan_led.brightness_get = wlan_led_get;
696 asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
697 asus->wlan_led.max_brightness = 1;
698 asus->wlan_led.default_trigger = "asus-wlan";
699
700 rv = led_classdev_register(&asus->platform_device->dev,
701 &asus->wlan_led);
Maxime Bellengé4c059842017-09-23 18:23:37 +0200702 if (rv)
703 goto error;
704 }
705
706 if (lightbar_led_presence(asus)) {
707 INIT_WORK(&asus->lightbar_led_work, lightbar_led_update);
708
709 asus->lightbar_led.name = "asus::lightbar";
710 asus->lightbar_led.brightness_set = lightbar_led_set;
711 asus->lightbar_led.brightness_get = lightbar_led_get;
712 asus->lightbar_led.max_brightness = 1;
713
714 rv = led_classdev_register(&asus->platform_device->dev,
715 &asus->lightbar_led);
Corentin Charye9809c02011-07-01 11:34:31 +0200716 }
717
718error:
719 if (rv)
720 asus_wmi_led_exit(asus);
721
722 return rv;
723}
724
725
Corentin Chary084fca62010-11-29 08:14:06 +0100726/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100727 * PCI hotplug (for wlan rfkill)
728 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100729static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100730{
Corentin Chary1d070f82011-02-26 10:20:36 +0100731 int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100732
Corentin Chary5c956382011-02-06 13:28:31 +0100733 if (result < 0)
Corentin Charyafa7c882011-02-06 13:28:28 +0100734 return false;
Corentin Chary5c956382011-02-06 13:28:31 +0100735 return !result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100736}
737
Corentin Charye12e6d92011-02-26 10:20:31 +0100738static void asus_rfkill_hotplug(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100739{
740 struct pci_dev *dev;
741 struct pci_bus *bus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100742 bool blocked;
Corentin Charyafa7c882011-02-06 13:28:28 +0100743 bool absent;
744 u32 l;
745
Corentin Charye12e6d92011-02-26 10:20:31 +0100746 mutex_lock(&asus->wmi_lock);
747 blocked = asus_wlan_rfkill_blocked(asus);
748 mutex_unlock(&asus->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100749
Corentin Charye12e6d92011-02-26 10:20:31 +0100750 mutex_lock(&asus->hotplug_lock);
Rafael J. Wysocki8b9ec1d2014-01-10 15:27:08 +0100751 pci_lock_rescan_remove();
Corentin Charyafa7c882011-02-06 13:28:28 +0100752
Corentin Charya7ce3f02011-02-26 10:20:33 +0100753 if (asus->wlan.rfkill)
754 rfkill_set_sw_state(asus->wlan.rfkill, blocked);
Corentin Chary279f8f92011-02-06 13:28:29 +0100755
Corentin Charye12e6d92011-02-26 10:20:31 +0100756 if (asus->hotplug_slot) {
Corentin Charyafa7c882011-02-06 13:28:28 +0100757 bus = pci_find_bus(0, 1);
758 if (!bus) {
Joe Perches5ad77dc2011-03-29 15:21:35 -0700759 pr_warn("Unable to find PCI bus 1?\n");
Corentin Charyafa7c882011-02-06 13:28:28 +0100760 goto out_unlock;
761 }
762
763 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
764 pr_err("Unable to read PCI config space?\n");
765 goto out_unlock;
766 }
767 absent = (l == 0xffffffff);
768
769 if (blocked != absent) {
Joe Perches5ad77dc2011-03-29 15:21:35 -0700770 pr_warn("BIOS says wireless lan is %s, "
771 "but the pci device is %s\n",
772 blocked ? "blocked" : "unblocked",
773 absent ? "absent" : "present");
774 pr_warn("skipped wireless hotplug as probably "
775 "inappropriate for this model\n");
Corentin Charyafa7c882011-02-06 13:28:28 +0100776 goto out_unlock;
777 }
778
779 if (!blocked) {
780 dev = pci_get_slot(bus, 0);
781 if (dev) {
782 /* Device already present */
783 pci_dev_put(dev);
784 goto out_unlock;
785 }
786 dev = pci_scan_single_device(bus, 0);
787 if (dev) {
788 pci_bus_assign_resources(bus);
Yijing Wangc893d132014-05-30 11:01:03 +0800789 pci_bus_add_device(dev);
Corentin Charyafa7c882011-02-06 13:28:28 +0100790 }
791 } else {
792 dev = pci_get_slot(bus, 0);
793 if (dev) {
Yinghai Lu210647a2012-02-25 13:54:20 -0800794 pci_stop_and_remove_bus_device(dev);
Corentin Charyafa7c882011-02-06 13:28:28 +0100795 pci_dev_put(dev);
796 }
797 }
798 }
799
800out_unlock:
Rafael J. Wysocki8b9ec1d2014-01-10 15:27:08 +0100801 pci_unlock_rescan_remove();
Corentin Charye12e6d92011-02-26 10:20:31 +0100802 mutex_unlock(&asus->hotplug_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100803}
804
Corentin Charye12e6d92011-02-26 10:20:31 +0100805static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)
Corentin Charyafa7c882011-02-06 13:28:28 +0100806{
Corentin Charye12e6d92011-02-26 10:20:31 +0100807 struct asus_wmi *asus = data;
Corentin Charyafa7c882011-02-06 13:28:28 +0100808
809 if (event != ACPI_NOTIFY_BUS_CHECK)
810 return;
811
Corentin Chary279f8f92011-02-06 13:28:29 +0100812 /*
Corentin Charye12e6d92011-02-26 10:20:31 +0100813 * We can't call directly asus_rfkill_hotplug because most
Corentin Chary279f8f92011-02-06 13:28:29 +0100814 * of the time WMBC is still being executed and not reetrant.
815 * There is currently no way to tell ACPICA that we want this
Corentin Charye12e6d92011-02-26 10:20:31 +0100816 * method to be serialized, we schedule a asus_rfkill_hotplug
Corentin Chary279f8f92011-02-06 13:28:29 +0100817 * call later, in a safer context.
818 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100819 queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
Corentin Charyafa7c882011-02-06 13:28:28 +0100820}
821
Corentin Charye12e6d92011-02-26 10:20:31 +0100822static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)
Corentin Charyafa7c882011-02-06 13:28:28 +0100823{
824 acpi_status status;
825 acpi_handle handle;
826
827 status = acpi_get_handle(NULL, node, &handle);
828
829 if (ACPI_SUCCESS(status)) {
830 status = acpi_install_notify_handler(handle,
831 ACPI_SYSTEM_NOTIFY,
Corentin Charye12e6d92011-02-26 10:20:31 +0100832 asus_rfkill_notify, asus);
Corentin Charyafa7c882011-02-06 13:28:28 +0100833 if (ACPI_FAILURE(status))
Joe Perches5ad77dc2011-03-29 15:21:35 -0700834 pr_warn("Failed to register notify on %s\n", node);
Corentin Charyafa7c882011-02-06 13:28:28 +0100835 } else
836 return -ENODEV;
837
838 return 0;
839}
840
Corentin Charye12e6d92011-02-26 10:20:31 +0100841static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
Corentin Charyafa7c882011-02-06 13:28:28 +0100842{
843 acpi_status status = AE_OK;
844 acpi_handle handle;
845
846 status = acpi_get_handle(NULL, node, &handle);
847
848 if (ACPI_SUCCESS(status)) {
849 status = acpi_remove_notify_handler(handle,
Corentin Charye12e6d92011-02-26 10:20:31 +0100850 ACPI_SYSTEM_NOTIFY,
851 asus_rfkill_notify);
Corentin Charyafa7c882011-02-06 13:28:28 +0100852 if (ACPI_FAILURE(status))
853 pr_err("Error removing rfkill notify handler %s\n",
Corentin Charye12e6d92011-02-26 10:20:31 +0100854 node);
Corentin Charyafa7c882011-02-06 13:28:28 +0100855 }
856}
857
Corentin Charye12e6d92011-02-26 10:20:31 +0100858static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
859 u8 *value)
Corentin Charyafa7c882011-02-06 13:28:28 +0100860{
Corentin Chary1d070f82011-02-26 10:20:36 +0100861 struct asus_wmi *asus = hotplug_slot->private;
862 int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100863
Corentin Chary5c956382011-02-06 13:28:31 +0100864 if (result < 0)
865 return result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100866
Corentin Chary5c956382011-02-06 13:28:31 +0100867 *value = !!result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100868 return 0;
869}
870
Lukas Wunner81c4b5b2018-09-08 09:59:01 +0200871static const struct hotplug_slot_ops asus_hotplug_slot_ops = {
Corentin Charye12e6d92011-02-26 10:20:31 +0100872 .get_adapter_status = asus_get_adapter_status,
873 .get_power_status = asus_get_adapter_status,
Corentin Charyafa7c882011-02-06 13:28:28 +0100874};
875
Corentin Charye12e6d92011-02-26 10:20:31 +0100876static void asus_hotplug_work(struct work_struct *work)
Corentin Chary279f8f92011-02-06 13:28:29 +0100877{
Corentin Charye12e6d92011-02-26 10:20:31 +0100878 struct asus_wmi *asus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100879
Corentin Charye12e6d92011-02-26 10:20:31 +0100880 asus = container_of(work, struct asus_wmi, hotplug_work);
881 asus_rfkill_hotplug(asus);
Corentin Chary279f8f92011-02-06 13:28:29 +0100882}
883
Corentin Charye12e6d92011-02-26 10:20:31 +0100884static int asus_setup_pci_hotplug(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100885{
886 int ret = -ENOMEM;
887 struct pci_bus *bus = pci_find_bus(0, 1);
888
889 if (!bus) {
890 pr_err("Unable to find wifi PCI bus\n");
891 return -ENODEV;
892 }
893
Corentin Charye12e6d92011-02-26 10:20:31 +0100894 asus->hotplug_workqueue =
895 create_singlethread_workqueue("hotplug_workqueue");
896 if (!asus->hotplug_workqueue)
Corentin Chary279f8f92011-02-06 13:28:29 +0100897 goto error_workqueue;
898
Corentin Charye12e6d92011-02-26 10:20:31 +0100899 INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
Corentin Chary279f8f92011-02-06 13:28:29 +0100900
Corentin Charye12e6d92011-02-26 10:20:31 +0100901 asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
902 if (!asus->hotplug_slot)
Corentin Charyafa7c882011-02-06 13:28:28 +0100903 goto error_slot;
904
Corentin Charye12e6d92011-02-26 10:20:31 +0100905 asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
906 GFP_KERNEL);
907 if (!asus->hotplug_slot->info)
Corentin Charyafa7c882011-02-06 13:28:28 +0100908 goto error_info;
909
Corentin Charye12e6d92011-02-26 10:20:31 +0100910 asus->hotplug_slot->private = asus;
Corentin Charye12e6d92011-02-26 10:20:31 +0100911 asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
912 asus_get_adapter_status(asus->hotplug_slot,
913 &asus->hotplug_slot->info->adapter_status);
Corentin Charyafa7c882011-02-06 13:28:28 +0100914
Corentin Charye12e6d92011-02-26 10:20:31 +0100915 ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi");
Corentin Charyafa7c882011-02-06 13:28:28 +0100916 if (ret) {
917 pr_err("Unable to register hotplug slot - %d\n", ret);
918 goto error_register;
919 }
920
921 return 0;
922
923error_register:
Corentin Charye12e6d92011-02-26 10:20:31 +0100924 kfree(asus->hotplug_slot->info);
Corentin Charyafa7c882011-02-06 13:28:28 +0100925error_info:
Corentin Charye12e6d92011-02-26 10:20:31 +0100926 kfree(asus->hotplug_slot);
927 asus->hotplug_slot = NULL;
Corentin Charyafa7c882011-02-06 13:28:28 +0100928error_slot:
Corentin Charye12e6d92011-02-26 10:20:31 +0100929 destroy_workqueue(asus->hotplug_workqueue);
Corentin Chary279f8f92011-02-06 13:28:29 +0100930error_workqueue:
Corentin Charyafa7c882011-02-06 13:28:28 +0100931 return ret;
932}
933
934/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100935 * Rfkill devices
936 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100937static int asus_rfkill_set(void *data, bool blocked)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100938{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100939 struct asus_rfkill *priv = data;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100940 u32 ctrl_param = !blocked;
AceLan Kaoa50bd122012-07-26 17:13:31 +0800941 u32 dev_id = priv->dev_id;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100942
AceLan Kaoa50bd122012-07-26 17:13:31 +0800943 /*
944 * If the user bit is set, BIOS can't set and record the wlan status,
945 * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
946 * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
947 * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
948 * while setting the wlan status through WMI.
949 * This is also the behavior that windows app will do.
950 */
951 if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
952 priv->asus->driver->wlan_ctrl_by_user)
953 dev_id = ASUS_WMI_DEVID_WLAN_LED;
954
955 return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100956}
957
Corentin Charye12e6d92011-02-26 10:20:31 +0100958static void asus_rfkill_query(struct rfkill *rfkill, void *data)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100959{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100960 struct asus_rfkill *priv = data;
Corentin Chary5c956382011-02-06 13:28:31 +0100961 int result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100962
Corentin Chary1d070f82011-02-26 10:20:36 +0100963 result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100964
Corentin Chary5c956382011-02-06 13:28:31 +0100965 if (result < 0)
Corentin Charye12e6d92011-02-26 10:20:31 +0100966 return;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100967
Corentin Charya7ce3f02011-02-26 10:20:33 +0100968 rfkill_set_sw_state(priv->rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100969}
970
Corentin Charye12e6d92011-02-26 10:20:31 +0100971static int asus_rfkill_wlan_set(void *data, bool blocked)
Corentin Chary279f8f92011-02-06 13:28:29 +0100972{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100973 struct asus_rfkill *priv = data;
974 struct asus_wmi *asus = priv->asus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100975 int ret;
976
977 /*
978 * This handler is enabled only if hotplug is enabled.
Corentin Charye12e6d92011-02-26 10:20:31 +0100979 * In this case, the asus_wmi_set_devstate() will
Corentin Chary279f8f92011-02-06 13:28:29 +0100980 * trigger a wmi notification and we need to wait
981 * this call to finish before being able to call
982 * any wmi method
983 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100984 mutex_lock(&asus->wmi_lock);
Corentin Charya7ce3f02011-02-26 10:20:33 +0100985 ret = asus_rfkill_set(data, blocked);
Corentin Charye12e6d92011-02-26 10:20:31 +0100986 mutex_unlock(&asus->wmi_lock);
Corentin Chary279f8f92011-02-06 13:28:29 +0100987 return ret;
988}
989
Corentin Charye12e6d92011-02-26 10:20:31 +0100990static const struct rfkill_ops asus_rfkill_wlan_ops = {
991 .set_block = asus_rfkill_wlan_set,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100992 .query = asus_rfkill_query,
Corentin Chary279f8f92011-02-06 13:28:29 +0100993};
994
Corentin Charye12e6d92011-02-26 10:20:31 +0100995static const struct rfkill_ops asus_rfkill_ops = {
996 .set_block = asus_rfkill_set,
997 .query = asus_rfkill_query,
Corentin Charyba48fdb2010-11-29 08:14:07 +0100998};
999
Corentin Charye12e6d92011-02-26 10:20:31 +01001000static int asus_new_rfkill(struct asus_wmi *asus,
Corentin Charya7ce3f02011-02-26 10:20:33 +01001001 struct asus_rfkill *arfkill,
Corentin Charye12e6d92011-02-26 10:20:31 +01001002 const char *name, enum rfkill_type type, int dev_id)
Corentin Charyba48fdb2010-11-29 08:14:07 +01001003{
Corentin Chary1d070f82011-02-26 10:20:36 +01001004 int result = asus_wmi_get_devstate_simple(asus, dev_id);
Corentin Charya7ce3f02011-02-26 10:20:33 +01001005 struct rfkill **rfkill = &arfkill->rfkill;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001006
Corentin Chary5c956382011-02-06 13:28:31 +01001007 if (result < 0)
1008 return result;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001009
Corentin Charya7ce3f02011-02-26 10:20:33 +01001010 arfkill->dev_id = dev_id;
1011 arfkill->asus = asus;
1012
AceLan Kaoc87992d2012-03-20 09:53:08 +01001013 if (dev_id == ASUS_WMI_DEVID_WLAN &&
1014 asus->driver->quirks->hotplug_wireless)
Corentin Charye12e6d92011-02-26 10:20:31 +01001015 *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
Corentin Charya7ce3f02011-02-26 10:20:33 +01001016 &asus_rfkill_wlan_ops, arfkill);
Corentin Chary279f8f92011-02-06 13:28:29 +01001017 else
Corentin Charye12e6d92011-02-26 10:20:31 +01001018 *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
Corentin Charya7ce3f02011-02-26 10:20:33 +01001019 &asus_rfkill_ops, arfkill);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001020
1021 if (!*rfkill)
1022 return -EINVAL;
1023
AceLan Kaoe8f56c82013-05-30 10:31:50 +08001024 if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
AceLan Kaof5156232014-07-09 16:18:18 +08001025 (asus->driver->quirks->wapf > 0))
AceLan Kao6cae06e2012-07-27 16:51:59 +08001026 rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
1027
Corentin Chary5c956382011-02-06 13:28:31 +01001028 rfkill_init_sw_state(*rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001029 result = rfkill_register(*rfkill);
1030 if (result) {
1031 rfkill_destroy(*rfkill);
1032 *rfkill = NULL;
1033 return result;
1034 }
1035 return 0;
1036}
1037
Corentin Charye12e6d92011-02-26 10:20:31 +01001038static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
Corentin Charyba48fdb2010-11-29 08:14:07 +01001039{
João Paulo Rechi Vitacf48bf92018-05-22 14:30:15 -07001040 if (asus->driver->wlan_ctrl_by_user && ashs_present())
1041 return;
1042
Corentin Charye12e6d92011-02-26 10:20:31 +01001043 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
1044 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
1045 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
Corentin Charya7ce3f02011-02-26 10:20:33 +01001046 if (asus->wlan.rfkill) {
1047 rfkill_unregister(asus->wlan.rfkill);
1048 rfkill_destroy(asus->wlan.rfkill);
1049 asus->wlan.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001050 }
Corentin Charyafa7c882011-02-06 13:28:28 +01001051 /*
1052 * Refresh pci hotplug in case the rfkill state was changed after
Corentin Charye12e6d92011-02-26 10:20:31 +01001053 * asus_unregister_rfkill_notifier()
Corentin Charyafa7c882011-02-06 13:28:28 +01001054 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001055 asus_rfkill_hotplug(asus);
Lukas Wunner51bbf9b2018-07-19 17:27:43 -05001056 if (asus->hotplug_slot) {
Corentin Charye12e6d92011-02-26 10:20:31 +01001057 pci_hp_deregister(asus->hotplug_slot);
Lukas Wunner51bbf9b2018-07-19 17:27:43 -05001058 kfree(asus->hotplug_slot->info);
1059 kfree(asus->hotplug_slot);
1060 }
Corentin Charye12e6d92011-02-26 10:20:31 +01001061 if (asus->hotplug_workqueue)
1062 destroy_workqueue(asus->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +01001063
Corentin Charya7ce3f02011-02-26 10:20:33 +01001064 if (asus->bluetooth.rfkill) {
1065 rfkill_unregister(asus->bluetooth.rfkill);
1066 rfkill_destroy(asus->bluetooth.rfkill);
1067 asus->bluetooth.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001068 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01001069 if (asus->wimax.rfkill) {
1070 rfkill_unregister(asus->wimax.rfkill);
1071 rfkill_destroy(asus->wimax.rfkill);
1072 asus->wimax.rfkill = NULL;
Corentin Chary2e9e1592011-02-06 13:28:37 +01001073 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01001074 if (asus->wwan3g.rfkill) {
1075 rfkill_unregister(asus->wwan3g.rfkill);
1076 rfkill_destroy(asus->wwan3g.rfkill);
1077 asus->wwan3g.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +01001078 }
Corentin Chary43be8bd2011-07-01 11:34:40 +02001079 if (asus->gps.rfkill) {
1080 rfkill_unregister(asus->gps.rfkill);
1081 rfkill_destroy(asus->gps.rfkill);
1082 asus->gps.rfkill = NULL;
1083 }
Corentin Charya912d322011-07-01 11:34:41 +02001084 if (asus->uwb.rfkill) {
1085 rfkill_unregister(asus->uwb.rfkill);
1086 rfkill_destroy(asus->uwb.rfkill);
1087 asus->uwb.rfkill = NULL;
1088 }
Corentin Charyba48fdb2010-11-29 08:14:07 +01001089}
1090
Corentin Charye12e6d92011-02-26 10:20:31 +01001091static int asus_wmi_rfkill_init(struct asus_wmi *asus)
Corentin Charyba48fdb2010-11-29 08:14:07 +01001092{
1093 int result = 0;
1094
Corentin Charye12e6d92011-02-26 10:20:31 +01001095 mutex_init(&asus->hotplug_lock);
1096 mutex_init(&asus->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +01001097
Corentin Charya7ce3f02011-02-26 10:20:33 +01001098 result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
1099 RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001100
1101 if (result && result != -ENODEV)
1102 goto exit;
1103
Corentin Charya7ce3f02011-02-26 10:20:33 +01001104 result = asus_new_rfkill(asus, &asus->bluetooth,
Corentin Charye12e6d92011-02-26 10:20:31 +01001105 "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
1106 ASUS_WMI_DEVID_BLUETOOTH);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001107
1108 if (result && result != -ENODEV)
1109 goto exit;
1110
Corentin Charya7ce3f02011-02-26 10:20:33 +01001111 result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
1112 RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
Corentin Chary2e9e1592011-02-06 13:28:37 +01001113
1114 if (result && result != -ENODEV)
1115 goto exit;
1116
Corentin Charya7ce3f02011-02-26 10:20:33 +01001117 result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
1118 RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001119
1120 if (result && result != -ENODEV)
1121 goto exit;
1122
Corentin Chary43be8bd2011-07-01 11:34:40 +02001123 result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
1124 RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
1125
1126 if (result && result != -ENODEV)
1127 goto exit;
1128
Corentin Charya912d322011-07-01 11:34:41 +02001129 result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
1130 RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
1131
1132 if (result && result != -ENODEV)
1133 goto exit;
1134
AceLan Kaoc87992d2012-03-20 09:53:08 +01001135 if (!asus->driver->quirks->hotplug_wireless)
Corentin Charyc14d4b82011-02-06 13:28:40 +01001136 goto exit;
1137
Corentin Charye12e6d92011-02-26 10:20:31 +01001138 result = asus_setup_pci_hotplug(asus);
Corentin Charyafa7c882011-02-06 13:28:28 +01001139 /*
1140 * If we get -EBUSY then something else is handling the PCI hotplug -
1141 * don't fail in this case
1142 */
1143 if (result == -EBUSY)
1144 result = 0;
1145
Corentin Charye12e6d92011-02-26 10:20:31 +01001146 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
1147 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
1148 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
Corentin Charyafa7c882011-02-06 13:28:28 +01001149 /*
1150 * Refresh pci hotplug in case the rfkill state was changed during
1151 * setup.
1152 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001153 asus_rfkill_hotplug(asus);
Corentin Charyafa7c882011-02-06 13:28:28 +01001154
Corentin Charyba48fdb2010-11-29 08:14:07 +01001155exit:
1156 if (result && result != -ENODEV)
Corentin Charye12e6d92011-02-26 10:20:31 +01001157 asus_wmi_rfkill_exit(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001158
1159 if (result == -ENODEV)
1160 result = 0;
1161
1162 return result;
1163}
1164
Kai-Chuan Hsieh8023eff2016-09-01 23:55:55 +08001165static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
1166{
1167 struct pci_dev *xhci_pdev;
1168 u32 orig_ports_available;
1169 u32 ports_available = asus->driver->quirks->xusb2pr;
1170
1171 xhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1172 PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,
1173 NULL);
1174
1175 if (!xhci_pdev)
1176 return;
1177
1178 pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
1179 &orig_ports_available);
1180
1181 pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
1182 cpu_to_le32(ports_available));
1183
1184 pr_info("set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\n",
1185 orig_ports_available, ports_available);
1186}
1187
Corentin Charyba48fdb2010-11-29 08:14:07 +01001188/*
Oleksij Rempele9b61512017-04-28 16:19:49 +02001189 * Some devices dont support or have borcken get_als method
1190 * but still support set method.
1191 */
1192static void asus_wmi_set_als(void)
1193{
1194 asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
1195}
1196
1197/*
Corentin Charye07babd2011-02-26 10:20:42 +01001198 * Hwmon device
1199 */
Kast Bernd53e755c2015-05-13 16:24:16 +02001200static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
1201 int *speed)
1202{
1203 struct fan_args args = {
1204 .agfn.len = sizeof(args),
1205 .agfn.mfun = ASUS_FAN_MFUN,
1206 .agfn.sfun = ASUS_FAN_SFUN_READ,
1207 .fan = fan,
1208 .speed = 0,
1209 };
1210 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
1211 int status;
1212
1213 if (fan != 1)
1214 return -EINVAL;
1215
1216 status = asus_wmi_evaluate_method_agfn(input);
1217
1218 if (status || args.agfn.err)
1219 return -ENXIO;
1220
1221 if (speed)
1222 *speed = args.speed;
1223
1224 return 0;
1225}
1226
1227static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
1228 int *speed)
1229{
1230 struct fan_args args = {
1231 .agfn.len = sizeof(args),
1232 .agfn.mfun = ASUS_FAN_MFUN,
1233 .agfn.sfun = ASUS_FAN_SFUN_WRITE,
1234 .fan = fan,
1235 .speed = speed ? *speed : 0,
1236 };
1237 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
1238 int status;
1239
1240 /* 1: for setting 1st fan's speed 0: setting auto mode */
1241 if (fan != 1 && fan != 0)
1242 return -EINVAL;
1243
1244 status = asus_wmi_evaluate_method_agfn(input);
1245
1246 if (status || args.agfn.err)
1247 return -ENXIO;
1248
1249 if (speed && fan == 1)
1250 asus->asus_hwmon_pwm = *speed;
1251
1252 return 0;
1253}
1254
1255/*
1256 * Check if we can read the speed of one fan. If true we assume we can also
1257 * control it.
1258 */
1259static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans)
1260{
1261 int status;
1262 int speed = 0;
1263
1264 *num_fans = 0;
1265
1266 status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed);
1267 if (!status)
1268 *num_fans = 1;
1269
1270 return 0;
1271}
1272
1273static int asus_hwmon_fan_set_auto(struct asus_wmi *asus)
1274{
1275 int status;
1276
1277 status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL);
1278 if (status)
1279 return -ENXIO;
1280
1281 asus->asus_hwmon_fan_manual_mode = false;
1282
1283 return 0;
1284}
1285
1286static int asus_hwmon_fan_rpm_show(struct device *dev, int fan)
1287{
1288 struct asus_wmi *asus = dev_get_drvdata(dev);
1289 int value;
1290 int ret;
1291
1292 /* no speed readable on manual mode */
1293 if (asus->asus_hwmon_fan_manual_mode)
1294 return -ENXIO;
1295
1296 ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value);
1297 if (ret) {
1298 pr_warn("reading fan speed failed: %d\n", ret);
1299 return -ENXIO;
1300 }
1301
1302 return value;
1303}
1304
1305static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value)
1306{
1307 int err;
1308
1309 if (asus->asus_hwmon_pwm >= 0) {
1310 *value = asus->asus_hwmon_pwm;
1311 return;
1312 }
1313
1314 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, value);
1315 if (err < 0)
1316 return;
1317
1318 *value &= 0xFF;
1319
1320 if (*value == 1) /* Low Speed */
1321 *value = 85;
1322 else if (*value == 2)
1323 *value = 170;
1324 else if (*value == 3)
1325 *value = 255;
1326 else if (*value) {
1327 pr_err("Unknown fan speed %#x\n", *value);
1328 *value = -1;
1329 }
1330}
1331
1332static ssize_t pwm1_show(struct device *dev,
Corentin Chary49979d02011-07-01 11:34:26 +02001333 struct device_attribute *attr,
1334 char *buf)
Corentin Charye07babd2011-02-26 10:20:42 +01001335{
1336 struct asus_wmi *asus = dev_get_drvdata(dev);
Kast Bernd53e755c2015-05-13 16:24:16 +02001337 int value;
Corentin Charye07babd2011-02-26 10:20:42 +01001338
Kast Bernd53e755c2015-05-13 16:24:16 +02001339 asus_hwmon_pwm_show(asus, 0, &value);
Corentin Charye07babd2011-02-26 10:20:42 +01001340
1341 return sprintf(buf, "%d\n", value);
1342}
1343
Kast Bernd53e755c2015-05-13 16:24:16 +02001344static ssize_t pwm1_store(struct device *dev,
1345 struct device_attribute *attr,
1346 const char *buf, size_t count) {
1347 struct asus_wmi *asus = dev_get_drvdata(dev);
1348 int value;
1349 int state;
1350 int ret;
1351
1352 ret = kstrtouint(buf, 10, &value);
1353
1354 if (ret)
1355 return ret;
1356
1357 value = clamp(value, 0, 255);
1358
1359 state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value);
1360 if (state)
1361 pr_warn("Setting fan speed failed: %d\n", state);
1362 else
1363 asus->asus_hwmon_fan_manual_mode = true;
1364
1365 return count;
1366}
1367
1368static ssize_t fan1_input_show(struct device *dev,
1369 struct device_attribute *attr,
1370 char *buf)
1371{
1372 int value = asus_hwmon_fan_rpm_show(dev, 0);
1373
1374 return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);
1375
1376}
1377
1378static ssize_t pwm1_enable_show(struct device *dev,
1379 struct device_attribute *attr,
1380 char *buf)
1381{
1382 struct asus_wmi *asus = dev_get_drvdata(dev);
1383
1384 if (asus->asus_hwmon_fan_manual_mode)
1385 return sprintf(buf, "%d\n", ASUS_FAN_CTRL_MANUAL);
1386
1387 return sprintf(buf, "%d\n", ASUS_FAN_CTRL_AUTO);
1388}
1389
1390static ssize_t pwm1_enable_store(struct device *dev,
1391 struct device_attribute *attr,
1392 const char *buf, size_t count)
1393{
1394 struct asus_wmi *asus = dev_get_drvdata(dev);
1395 int status = 0;
1396 int state;
1397 int ret;
1398
1399 ret = kstrtouint(buf, 10, &state);
1400
1401 if (ret)
1402 return ret;
1403
1404 if (state == ASUS_FAN_CTRL_MANUAL)
1405 asus->asus_hwmon_fan_manual_mode = true;
1406 else
1407 status = asus_hwmon_fan_set_auto(asus);
1408
1409 if (status)
1410 return status;
1411
1412 return count;
1413}
1414
1415static ssize_t fan1_label_show(struct device *dev,
1416 struct device_attribute *attr,
1417 char *buf)
1418{
1419 return sprintf(buf, "%s\n", ASUS_FAN_DESC);
1420}
1421
Corentin Chary6118b8a2011-07-01 11:34:36 +02001422static ssize_t asus_hwmon_temp1(struct device *dev,
1423 struct device_attribute *attr,
1424 char *buf)
1425{
1426 struct asus_wmi *asus = dev_get_drvdata(dev);
1427 u32 value;
1428 int err;
1429
1430 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
1431
1432 if (err < 0)
1433 return err;
1434
Rasmus Villemoese866a2e2015-10-01 23:45:31 +02001435 value = DECI_KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
Corentin Chary6118b8a2011-07-01 11:34:36 +02001436
1437 return sprintf(buf, "%d\n", value);
1438}
1439
Kast Bernd53e755c2015-05-13 16:24:16 +02001440/* Fan1 */
1441static DEVICE_ATTR_RW(pwm1);
1442static DEVICE_ATTR_RW(pwm1_enable);
1443static DEVICE_ATTR_RO(fan1_input);
1444static DEVICE_ATTR_RO(fan1_label);
1445
1446/* Temperature */
Guenter Roeck50a639f2013-11-23 11:03:17 -08001447static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
Corentin Charye07babd2011-02-26 10:20:42 +01001448
1449static struct attribute *hwmon_attributes[] = {
Guenter Roeck50a639f2013-11-23 11:03:17 -08001450 &dev_attr_pwm1.attr,
Kast Bernd53e755c2015-05-13 16:24:16 +02001451 &dev_attr_pwm1_enable.attr,
1452 &dev_attr_fan1_input.attr,
1453 &dev_attr_fan1_label.attr,
1454
Guenter Roeck50a639f2013-11-23 11:03:17 -08001455 &dev_attr_temp1_input.attr,
Corentin Charye07babd2011-02-26 10:20:42 +01001456 NULL
1457};
1458
Al Viro587a1f12011-07-23 23:11:19 -04001459static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
Corentin Charye02431d2011-07-01 11:34:37 +02001460 struct attribute *attr, int idx)
Corentin Charye07babd2011-02-26 10:20:42 +01001461{
1462 struct device *dev = container_of(kobj, struct device, kobj);
1463 struct platform_device *pdev = to_platform_device(dev->parent);
1464 struct asus_wmi *asus = platform_get_drvdata(pdev);
Corentin Charye07babd2011-02-26 10:20:42 +01001465 int dev_id = -1;
Kast Bernd53e755c2015-05-13 16:24:16 +02001466 int fan_attr = -1;
Corentin Charye07babd2011-02-26 10:20:42 +01001467 u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
Kast Bernd53e755c2015-05-13 16:24:16 +02001468 bool ok = true;
Corentin Charye07babd2011-02-26 10:20:42 +01001469
Guenter Roeck50a639f2013-11-23 11:03:17 -08001470 if (attr == &dev_attr_pwm1.attr)
Corentin Charye07babd2011-02-26 10:20:42 +01001471 dev_id = ASUS_WMI_DEVID_FAN_CTRL;
Guenter Roeck50a639f2013-11-23 11:03:17 -08001472 else if (attr == &dev_attr_temp1_input.attr)
Corentin Charye02431d2011-07-01 11:34:37 +02001473 dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
Corentin Charye07babd2011-02-26 10:20:42 +01001474
Kast Bernd53e755c2015-05-13 16:24:16 +02001475
1476 if (attr == &dev_attr_fan1_input.attr
1477 || attr == &dev_attr_fan1_label.attr
1478 || attr == &dev_attr_pwm1.attr
1479 || attr == &dev_attr_pwm1_enable.attr) {
1480 fan_attr = 1;
1481 }
1482
Corentin Charye07babd2011-02-26 10:20:42 +01001483 if (dev_id != -1) {
1484 int err = asus_wmi_get_devstate(asus, dev_id, &value);
1485
Kast Bernd53e755c2015-05-13 16:24:16 +02001486 if (err < 0 && fan_attr == -1)
Al Viroe772aed2011-07-23 20:59:40 -04001487 return 0; /* can't return negative here */
Corentin Charye07babd2011-02-26 10:20:42 +01001488 }
1489
1490 if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) {
1491 /*
1492 * We need to find a better way, probably using sfun,
1493 * bits or spec ...
1494 * Currently we disable it if:
1495 * - ASUS_WMI_UNSUPPORTED_METHOD is returned
1496 * - reverved bits are non-zero
1497 * - sfun and presence bit are not set
1498 */
Corentin Chary49979d02011-07-01 11:34:26 +02001499 if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
Corentin Charye07babd2011-02-26 10:20:42 +01001500 || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
1501 ok = false;
Kast Bernd53e755c2015-05-13 16:24:16 +02001502 else
1503 ok = fan_attr <= asus->asus_hwmon_num_fans;
Corentin Charye02431d2011-07-01 11:34:37 +02001504 } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
1505 /* If value is zero, something is clearly wrong */
Kast Bernd53e755c2015-05-13 16:24:16 +02001506 if (!value)
Corentin Charye02431d2011-07-01 11:34:37 +02001507 ok = false;
Kast Bernd53e755c2015-05-13 16:24:16 +02001508 } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
1509 ok = true;
1510 } else {
1511 ok = false;
Corentin Charye07babd2011-02-26 10:20:42 +01001512 }
1513
1514 return ok ? attr->mode : 0;
1515}
1516
Arvind Yadave90d9ba2017-07-11 16:18:19 +05301517static const struct attribute_group hwmon_attribute_group = {
Corentin Charye07babd2011-02-26 10:20:42 +01001518 .is_visible = asus_hwmon_sysfs_is_visible,
1519 .attrs = hwmon_attributes
1520};
Guenter Roeck50a639f2013-11-23 11:03:17 -08001521__ATTRIBUTE_GROUPS(hwmon_attribute);
Corentin Charye07babd2011-02-26 10:20:42 +01001522
1523static int asus_wmi_hwmon_init(struct asus_wmi *asus)
1524{
1525 struct device *hwmon;
Corentin Charye07babd2011-02-26 10:20:42 +01001526
Guenter Roeck50a639f2013-11-23 11:03:17 -08001527 hwmon = hwmon_device_register_with_groups(&asus->platform_device->dev,
1528 "asus", asus,
1529 hwmon_attribute_groups);
Corentin Charye07babd2011-02-26 10:20:42 +01001530 if (IS_ERR(hwmon)) {
1531 pr_err("Could not register asus hwmon device\n");
1532 return PTR_ERR(hwmon);
1533 }
Guenter Roeck50a639f2013-11-23 11:03:17 -08001534 return 0;
Corentin Charye07babd2011-02-26 10:20:42 +01001535}
1536
1537/*
Corentin Chary084fca62010-11-29 08:14:06 +01001538 * Backlight
1539 */
Corentin Chary1d070f82011-02-26 10:20:36 +01001540static int read_backlight_power(struct asus_wmi *asus)
Corentin Charyb7187262011-02-06 13:28:39 +01001541{
AceLan Kao6e0044b2012-03-20 09:53:09 +01001542 int ret;
1543 if (asus->driver->quirks->store_backlight_power)
1544 ret = !asus->driver->panel_power;
1545 else
1546 ret = asus_wmi_get_devstate_simple(asus,
1547 ASUS_WMI_DEVID_BACKLIGHT);
Corentin Charyb7187262011-02-06 13:28:39 +01001548
1549 if (ret < 0)
1550 return ret;
1551
1552 return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
1553}
1554
Corentin Chary8fbea012011-02-26 10:20:37 +01001555static int read_brightness_max(struct asus_wmi *asus)
1556{
1557 u32 retval;
1558 int err;
1559
1560 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
1561
1562 if (err < 0)
1563 return err;
1564
1565 retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;
1566 retval >>= 8;
1567
1568 if (!retval)
1569 return -ENODEV;
1570
1571 return retval;
1572}
1573
Yong Wang3d7b1652010-04-11 09:27:54 +08001574static int read_brightness(struct backlight_device *bd)
1575{
Corentin Chary1d070f82011-02-26 10:20:36 +01001576 struct asus_wmi *asus = bl_get_data(bd);
Dan Carpenter0986f252011-03-15 10:06:23 +03001577 u32 retval;
1578 int err;
Yong Wang3d7b1652010-04-11 09:27:54 +08001579
Corentin Chary1d070f82011-02-26 10:20:36 +01001580 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +08001581
Corentin Charyd33da3b2011-02-26 10:20:35 +01001582 if (err < 0)
1583 return err;
1584
1585 return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
Yong Wang3d7b1652010-04-11 09:27:54 +08001586}
1587
AceLan Kaoc87992d2012-03-20 09:53:08 +01001588static u32 get_scalar_command(struct backlight_device *bd)
1589{
1590 struct asus_wmi *asus = bl_get_data(bd);
1591 u32 ctrl_param = 0;
1592
1593 if ((asus->driver->brightness < bd->props.brightness) ||
1594 bd->props.brightness == bd->props.max_brightness)
1595 ctrl_param = 0x00008001;
1596 else if ((asus->driver->brightness > bd->props.brightness) ||
1597 bd->props.brightness == 0)
1598 ctrl_param = 0x00008000;
1599
1600 asus->driver->brightness = bd->props.brightness;
1601
1602 return ctrl_param;
1603}
1604
Yong Wang3d7b1652010-04-11 09:27:54 +08001605static int update_bl_status(struct backlight_device *bd)
1606{
Corentin Chary1d070f82011-02-26 10:20:36 +01001607 struct asus_wmi *asus = bl_get_data(bd);
Corentin Charydfed65d2010-11-29 08:14:12 +01001608 u32 ctrl_param;
AceLan Kao6e0044b2012-03-20 09:53:09 +01001609 int power, err = 0;
Corentin Charyb7187262011-02-06 13:28:39 +01001610
Corentin Chary1d070f82011-02-26 10:20:36 +01001611 power = read_backlight_power(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01001612 if (power != -ENODEV && bd->props.power != power) {
1613 ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
Corentin Charyd33da3b2011-02-26 10:20:35 +01001614 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
1615 ctrl_param, NULL);
AceLan Kao6e0044b2012-03-20 09:53:09 +01001616 if (asus->driver->quirks->store_backlight_power)
1617 asus->driver->panel_power = bd->props.power;
AceLan Kao6e0044b2012-03-20 09:53:09 +01001618
Corentin Charyade28ab2012-03-20 09:53:14 +01001619 /* When using scalar brightness, updating the brightness
1620 * will mess with the backlight power */
1621 if (asus->driver->quirks->scalar_panel_brightness)
1622 return err;
Corentin Charyb7187262011-02-06 13:28:39 +01001623 }
Corentin Charyade28ab2012-03-20 09:53:14 +01001624
1625 if (asus->driver->quirks->scalar_panel_brightness)
1626 ctrl_param = get_scalar_command(bd);
1627 else
1628 ctrl_param = bd->props.brightness;
1629
1630 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
1631 ctrl_param, NULL);
1632
Corentin Chary8fbea012011-02-26 10:20:37 +01001633 return err;
Yong Wang3d7b1652010-04-11 09:27:54 +08001634}
1635
Corentin Charye12e6d92011-02-26 10:20:31 +01001636static const struct backlight_ops asus_wmi_bl_ops = {
Yong Wang3d7b1652010-04-11 09:27:54 +08001637 .get_brightness = read_brightness,
1638 .update_status = update_bl_status,
1639};
1640
Corentin Charye12e6d92011-02-26 10:20:31 +01001641static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)
Yong Wang3d7b1652010-04-11 09:27:54 +08001642{
Corentin Charye12e6d92011-02-26 10:20:31 +01001643 struct backlight_device *bd = asus->backlight_device;
Yong Wang3d7b1652010-04-11 09:27:54 +08001644 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +02001645 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +08001646
1647 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
1648 new = code - NOTIFY_BRNUP_MIN + 1;
1649 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
1650 new = code - NOTIFY_BRNDOWN_MIN;
1651
1652 bd->props.brightness = new;
1653 backlight_update_status(bd);
1654 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1655
1656 return old;
1657}
1658
Corentin Charye12e6d92011-02-26 10:20:31 +01001659static int asus_wmi_backlight_init(struct asus_wmi *asus)
Yong Wang3d7b1652010-04-11 09:27:54 +08001660{
1661 struct backlight_device *bd;
1662 struct backlight_properties props;
Corentin Charyb7187262011-02-06 13:28:39 +01001663 int max;
1664 int power;
1665
Corentin Chary8fbea012011-02-26 10:20:37 +01001666 max = read_brightness_max(asus);
Hans de Goede86ac27352014-07-08 10:47:22 +02001667 if (max < 0)
Corentin Chary8fbea012011-02-26 10:20:37 +01001668 return max;
1669
1670 power = read_backlight_power(asus);
1671
Corentin Charyb7187262011-02-06 13:28:39 +01001672 if (power == -ENODEV)
1673 power = FB_BLANK_UNBLANK;
Corentin Chary8fbea012011-02-26 10:20:37 +01001674 else if (power < 0)
1675 return power;
Yong Wang3d7b1652010-04-11 09:27:54 +08001676
1677 memset(&props, 0, sizeof(struct backlight_properties));
Axel Lin60cfa092011-06-29 11:43:30 +08001678 props.type = BACKLIGHT_PLATFORM;
Corentin Charyb7187262011-02-06 13:28:39 +01001679 props.max_brightness = max;
Corentin Charye12e6d92011-02-26 10:20:31 +01001680 bd = backlight_device_register(asus->driver->name,
1681 &asus->platform_device->dev, asus,
1682 &asus_wmi_bl_ops, &props);
Yong Wang3d7b1652010-04-11 09:27:54 +08001683 if (IS_ERR(bd)) {
1684 pr_err("Could not register backlight device\n");
1685 return PTR_ERR(bd);
1686 }
1687
Corentin Charye12e6d92011-02-26 10:20:31 +01001688 asus->backlight_device = bd;
Yong Wang3d7b1652010-04-11 09:27:54 +08001689
AceLan Kao6e0044b2012-03-20 09:53:09 +01001690 if (asus->driver->quirks->store_backlight_power)
1691 asus->driver->panel_power = power;
1692
Yong Wang3d7b1652010-04-11 09:27:54 +08001693 bd->props.brightness = read_brightness(bd);
Corentin Charyb7187262011-02-06 13:28:39 +01001694 bd->props.power = power;
Yong Wang3d7b1652010-04-11 09:27:54 +08001695 backlight_update_status(bd);
1696
AceLan Kaoc87992d2012-03-20 09:53:08 +01001697 asus->driver->brightness = bd->props.brightness;
1698
Yong Wang3d7b1652010-04-11 09:27:54 +08001699 return 0;
1700}
1701
Corentin Charye12e6d92011-02-26 10:20:31 +01001702static void asus_wmi_backlight_exit(struct asus_wmi *asus)
Yong Wang3d7b1652010-04-11 09:27:54 +08001703{
Markus Elfring00981812014-11-24 20:30:29 +01001704 backlight_device_unregister(asus->backlight_device);
Yong Wang3d7b1652010-04-11 09:27:54 +08001705
Corentin Charye12e6d92011-02-26 10:20:31 +01001706 asus->backlight_device = NULL;
Yong Wang3d7b1652010-04-11 09:27:54 +08001707}
1708
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001709static int is_display_toggle(int code)
1710{
1711 /* display toggle keys */
1712 if ((code >= 0x61 && code <= 0x67) ||
1713 (code >= 0x8c && code <= 0x93) ||
1714 (code >= 0xa0 && code <= 0xa7) ||
1715 (code >= 0xd0 && code <= 0xd5))
1716 return 1;
1717
1718 return 0;
1719}
1720
Corentin Charye12e6d92011-02-26 10:20:31 +01001721static void asus_wmi_notify(u32 value, void *context)
Yong Wang3d7b1652010-04-11 09:27:54 +08001722{
Corentin Charye12e6d92011-02-26 10:20:31 +01001723 struct asus_wmi *asus = context;
Yong Wang3d7b1652010-04-11 09:27:54 +08001724 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
1725 union acpi_object *obj;
1726 acpi_status status;
1727 int code;
1728 int orig_code;
Seth Forsheec4453f62011-07-01 11:34:27 +02001729 unsigned int key_value = 1;
1730 bool autorelease = 1;
Yong Wang3d7b1652010-04-11 09:27:54 +08001731
1732 status = wmi_get_event_data(value, &response);
1733 if (status != AE_OK) {
1734 pr_err("bad event status 0x%x\n", status);
1735 return;
1736 }
1737
1738 obj = (union acpi_object *)response.pointer;
1739
Corentin Chary57ab7da2011-02-26 10:20:32 +01001740 if (!obj || obj->type != ACPI_TYPE_INTEGER)
1741 goto exit;
Yong Wang3d7b1652010-04-11 09:27:54 +08001742
Corentin Chary57ab7da2011-02-26 10:20:32 +01001743 code = obj->integer.value;
1744 orig_code = code;
Yong Wang3d7b1652010-04-11 09:27:54 +08001745
Seth Forsheec4453f62011-07-01 11:34:27 +02001746 if (asus->driver->key_filter) {
1747 asus->driver->key_filter(asus->driver, &code, &key_value,
1748 &autorelease);
1749 if (code == ASUS_WMI_KEY_IGNORE)
1750 goto exit;
1751 }
1752
Corentin Chary57ab7da2011-02-26 10:20:32 +01001753 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
Corentin Chary3ba03022012-11-29 09:12:38 +01001754 code = ASUS_WMI_BRN_UP;
Corentin Chary57ab7da2011-02-26 10:20:32 +01001755 else if (code >= NOTIFY_BRNDOWN_MIN &&
1756 code <= NOTIFY_BRNDOWN_MAX)
Corentin Chary3ba03022012-11-29 09:12:38 +01001757 code = ASUS_WMI_BRN_DOWN;
Yong Wang3d7b1652010-04-11 09:27:54 +08001758
Corentin Chary3ba03022012-11-29 09:12:38 +01001759 if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
Hans de Goede62c4aa12015-06-16 16:27:58 +02001760 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
Corentin Chary57ab7da2011-02-26 10:20:32 +01001761 asus_wmi_backlight_notify(asus, orig_code);
Corentin Chary3ba03022012-11-29 09:12:38 +01001762 goto exit;
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001763 }
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001764 }
1765
Chris Chiudbb3d782018-06-20 22:46:44 +08001766 if (code == NOTIFY_KBD_BRTUP) {
1767 do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1);
1768 goto exit;
1769 }
1770 if (code == NOTIFY_KBD_BRTDWN) {
1771 do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk - 1);
1772 goto exit;
1773 }
Chris Chiued99d292018-06-20 22:46:45 +08001774 if (code == NOTIFY_KBD_BRTTOGGLE) {
1775 if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
1776 do_kbd_led_set(&asus->kbd_led, 0);
1777 else
1778 do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1);
1779 goto exit;
1780 }
Chris Chiudbb3d782018-06-20 22:46:44 +08001781
AceLan Kaoa2a96f02012-10-03 11:26:31 +02001782 if (is_display_toggle(code) &&
1783 asus->driver->quirks->no_display_toggle)
1784 goto exit;
1785
1786 if (!sparse_keymap_report_event(asus->inputdev, code,
1787 key_value, autorelease))
Corentin Chary57ab7da2011-02-26 10:20:32 +01001788 pr_info("Unknown key %x pressed\n", code);
Yong Wang3d7b1652010-04-11 09:27:54 +08001789
Corentin Chary57ab7da2011-02-26 10:20:32 +01001790exit:
Yong Wang3d7b1652010-04-11 09:27:54 +08001791 kfree(obj);
1792}
1793
Corentin Chary9e1565b2011-02-06 13:28:36 +01001794/*
1795 * Sys helpers
1796 */
1797static int parse_arg(const char *buf, unsigned long count, int *val)
1798{
1799 if (!count)
1800 return 0;
1801 if (sscanf(buf, "%i", val) != 1)
1802 return -EINVAL;
1803 return count;
1804}
1805
Corentin Chary1d070f82011-02-26 10:20:36 +01001806static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
1807 const char *buf, size_t count)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001808{
Corentin Chary9e1565b2011-02-06 13:28:36 +01001809 u32 retval;
Corentin Charyd33da3b2011-02-26 10:20:35 +01001810 int rv, err, value;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001811
Corentin Chary1d070f82011-02-26 10:20:36 +01001812 value = asus_wmi_get_devstate_simple(asus, devid);
Dan Carpenterb8298342015-11-11 01:18:16 +03001813 if (value < 0)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001814 return value;
1815
1816 rv = parse_arg(buf, count, &value);
Corentin Charyd33da3b2011-02-26 10:20:35 +01001817 err = asus_wmi_set_devstate(devid, value, &retval);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001818
Corentin Charyd33da3b2011-02-26 10:20:35 +01001819 if (err < 0)
1820 return err;
1821
Corentin Chary9e1565b2011-02-06 13:28:36 +01001822 return rv;
1823}
1824
Corentin Chary1d070f82011-02-26 10:20:36 +01001825static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001826{
Corentin Chary1d070f82011-02-26 10:20:36 +01001827 int value = asus_wmi_get_devstate_simple(asus, devid);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001828
1829 if (value < 0)
1830 return value;
1831
1832 return sprintf(buf, "%d\n", value);
1833}
1834
Corentin Charye12e6d92011-02-26 10:20:31 +01001835#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001836 static ssize_t show_##_name(struct device *dev, \
1837 struct device_attribute *attr, \
1838 char *buf) \
1839 { \
Corentin Chary1d070f82011-02-26 10:20:36 +01001840 struct asus_wmi *asus = dev_get_drvdata(dev); \
1841 \
1842 return show_sys_wmi(asus, _cm, buf); \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001843 } \
1844 static ssize_t store_##_name(struct device *dev, \
1845 struct device_attribute *attr, \
1846 const char *buf, size_t count) \
1847 { \
Corentin Chary1d070f82011-02-26 10:20:36 +01001848 struct asus_wmi *asus = dev_get_drvdata(dev); \
1849 \
1850 return store_sys_wmi(asus, _cm, buf, count); \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001851 } \
1852 static struct device_attribute dev_attr_##_name = { \
1853 .attr = { \
1854 .name = __stringify(_name), \
1855 .mode = _mode }, \
1856 .show = show_##_name, \
1857 .store = store_##_name, \
1858 }
1859
Corentin Charye12e6d92011-02-26 10:20:31 +01001860ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
1861ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
1862ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
AceLan Kaoc0b91b62012-06-13 09:32:07 +02001863ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
Oleksij Rempelaca234f2016-04-01 13:35:21 +02001864ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001865
Jérémy Lefaurea8fe3422017-04-21 22:19:45 -04001866static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr,
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -07001867 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001868{
Corentin Chary3df5fda2011-07-01 11:34:38 +02001869 int value, rv;
Chris Bagwell7f80d732010-10-11 18:47:18 -05001870
1871 if (!count || sscanf(buf, "%i", &value) != 1)
1872 return -EINVAL;
1873 if (value < 0 || value > 2)
1874 return -EINVAL;
1875
Corentin Chary3df5fda2011-07-01 11:34:38 +02001876 rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
1877 if (rv < 0)
1878 return rv;
1879
1880 return count;
Chris Bagwell7f80d732010-10-11 18:47:18 -05001881}
1882
Jérémy Lefaurea8fe3422017-04-21 22:19:45 -04001883static DEVICE_ATTR_WO(cpufv);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001884
Corentin Chary4e37b422010-11-29 08:14:08 +01001885static struct attribute *platform_attributes[] = {
1886 &dev_attr_cpufv.attr,
Corentin Chary9e1565b2011-02-06 13:28:36 +01001887 &dev_attr_camera.attr,
1888 &dev_attr_cardr.attr,
Corentin Chary4615bb62011-02-06 13:28:42 +01001889 &dev_attr_touchpad.attr,
AceLan Kaoc0b91b62012-06-13 09:32:07 +02001890 &dev_attr_lid_resume.attr,
Oleksij Rempelaca234f2016-04-01 13:35:21 +02001891 &dev_attr_als_enable.attr,
Corentin Chary4e37b422010-11-29 08:14:08 +01001892 NULL
1893};
1894
Al Viro587a1f12011-07-23 23:11:19 -04001895static umode_t asus_sysfs_is_visible(struct kobject *kobj,
Corentin Charye12e6d92011-02-26 10:20:31 +01001896 struct attribute *attr, int idx)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001897{
Corentin Chary1d070f82011-02-26 10:20:36 +01001898 struct device *dev = container_of(kobj, struct device, kobj);
Wolfram Sangd605ca22018-04-19 16:06:10 +02001899 struct asus_wmi *asus = dev_get_drvdata(dev);
Corentin Chary1d070f82011-02-26 10:20:36 +01001900 bool ok = true;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001901 int devid = -1;
1902
1903 if (attr == &dev_attr_camera.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01001904 devid = ASUS_WMI_DEVID_CAMERA;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001905 else if (attr == &dev_attr_cardr.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01001906 devid = ASUS_WMI_DEVID_CARDREADER;
Corentin Chary4615bb62011-02-06 13:28:42 +01001907 else if (attr == &dev_attr_touchpad.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01001908 devid = ASUS_WMI_DEVID_TOUCHPAD;
AceLan Kaoc0b91b62012-06-13 09:32:07 +02001909 else if (attr == &dev_attr_lid_resume.attr)
1910 devid = ASUS_WMI_DEVID_LID_RESUME;
Oleksij Rempelaca234f2016-04-01 13:35:21 +02001911 else if (attr == &dev_attr_als_enable.attr)
1912 devid = ASUS_WMI_DEVID_ALS_ENABLE;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001913
1914 if (devid != -1)
Corentin Chary1d070f82011-02-26 10:20:36 +01001915 ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001916
Corentin Chary1d070f82011-02-26 10:20:36 +01001917 return ok ? attr->mode : 0;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001918}
1919
Arvind Yadave90d9ba2017-07-11 16:18:19 +05301920static const struct attribute_group platform_attribute_group = {
Corentin Charye12e6d92011-02-26 10:20:31 +01001921 .is_visible = asus_sysfs_is_visible,
1922 .attrs = platform_attributes
Corentin Chary4e37b422010-11-29 08:14:08 +01001923};
1924
Corentin Charye12e6d92011-02-26 10:20:31 +01001925static void asus_wmi_sysfs_exit(struct platform_device *device)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001926{
Corentin Chary4e37b422010-11-29 08:14:08 +01001927 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001928}
1929
Corentin Charye12e6d92011-02-26 10:20:31 +01001930static int asus_wmi_sysfs_init(struct platform_device *device)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001931{
Corentin Chary4e37b422010-11-29 08:14:08 +01001932 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001933}
1934
Corentin Chary27c136c2010-11-29 08:14:05 +01001935/*
1936 * Platform device
1937 */
Joe Perches39ddf3b2011-03-29 15:21:32 -07001938static int asus_wmi_platform_init(struct asus_wmi *asus)
Corentin Chary27c136c2010-11-29 08:14:05 +01001939{
Corentin Chary46dbca82011-02-26 10:20:38 +01001940 int rv;
1941
1942 /* INIT enable hotkeys on some models */
1943 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
vic0ed60652013-05-22 21:32:10 +03001944 pr_info("Initialization: %#x\n", rv);
Corentin Chary46dbca82011-02-26 10:20:38 +01001945
1946 /* We don't know yet what to do with this version... */
1947 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
vic0ed60652013-05-22 21:32:10 +03001948 pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF);
Corentin Chary46dbca82011-02-26 10:20:38 +01001949 asus->spec = rv;
1950 }
1951
1952 /*
1953 * The SFUN method probably allows the original driver to get the list
1954 * of features supported by a given model. For now, 0x0100 or 0x0800
1955 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
1956 * The significance of others is yet to be found.
1957 */
1958 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
vic0ed60652013-05-22 21:32:10 +03001959 pr_info("SFUN value: %#x\n", rv);
Corentin Chary46dbca82011-02-26 10:20:38 +01001960 asus->sfun = rv;
1961 }
1962
Corentin Chary1d070f82011-02-26 10:20:36 +01001963 /*
1964 * Eee PC and Notebooks seems to have different method_id for DSTS,
1965 * but it may also be related to the BIOS's SPEC.
1966 * Note, on most Eeepc, there is no way to check if a method exist
1967 * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
1968 * but once again, SPEC may probably be used for that kind of things.
1969 */
1970 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
1971 asus->dsts_id = ASUS_WMI_METHODID_DSTS;
Alex Hung63a78bb2012-06-20 11:47:35 +08001972 else
Corentin Chary1d070f82011-02-26 10:20:36 +01001973 asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
1974
Corentin Charyfddbfed2011-07-01 11:34:39 +02001975 /* CWAP allow to define the behavior of the Fn+F2 key,
1976 * this method doesn't seems to be present on Eee PCs */
Corentin Chary6a2bccc2012-03-20 09:53:10 +01001977 if (asus->driver->quirks->wapf >= 0)
Corentin Charyfddbfed2011-07-01 11:34:39 +02001978 asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
Corentin Chary6a2bccc2012-03-20 09:53:10 +01001979 asus->driver->quirks->wapf, NULL);
Corentin Charyfddbfed2011-07-01 11:34:39 +02001980
Corentin Charye12e6d92011-02-26 10:20:31 +01001981 return asus_wmi_sysfs_init(asus->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +01001982}
1983
Corentin Charye12e6d92011-02-26 10:20:31 +01001984static void asus_wmi_platform_exit(struct asus_wmi *asus)
Corentin Chary27c136c2010-11-29 08:14:05 +01001985{
Corentin Charye12e6d92011-02-26 10:20:31 +01001986 asus_wmi_sysfs_exit(asus->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +01001987}
1988
1989/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001990 * debugfs
1991 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001992struct asus_wmi_debugfs_node {
1993 struct asus_wmi *asus;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001994 char *name;
Corentin Charye12e6d92011-02-26 10:20:31 +01001995 int (*show) (struct seq_file *m, void *data);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001996};
1997
1998static int show_dsts(struct seq_file *m, void *data)
1999{
Corentin Charye12e6d92011-02-26 10:20:31 +01002000 struct asus_wmi *asus = m->private;
Corentin Charyd33da3b2011-02-26 10:20:35 +01002001 int err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002002 u32 retval = -1;
2003
Corentin Chary1d070f82011-02-26 10:20:36 +01002004 err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002005
Corentin Charyd33da3b2011-02-26 10:20:35 +01002006 if (err < 0)
2007 return err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002008
Corentin Charyef343492011-02-26 10:20:39 +01002009 seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002010
2011 return 0;
2012}
2013
2014static int show_devs(struct seq_file *m, void *data)
2015{
Corentin Charye12e6d92011-02-26 10:20:31 +01002016 struct asus_wmi *asus = m->private;
Corentin Charyd33da3b2011-02-26 10:20:35 +01002017 int err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002018 u32 retval = -1;
2019
Corentin Charyd33da3b2011-02-26 10:20:35 +01002020 err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,
2021 &retval);
2022
2023 if (err < 0)
2024 return err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002025
Corentin Charyef343492011-02-26 10:20:39 +01002026 seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id,
Corentin Charye12e6d92011-02-26 10:20:31 +01002027 asus->debug.ctrl_param, retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002028
2029 return 0;
2030}
2031
Corentin Charyef343492011-02-26 10:20:39 +01002032static int show_call(struct seq_file *m, void *data)
2033{
2034 struct asus_wmi *asus = m->private;
2035 struct bios_args args = {
2036 .arg0 = asus->debug.dev_id,
2037 .arg1 = asus->debug.ctrl_param,
2038 };
2039 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
2040 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
2041 union acpi_object *obj;
2042 acpi_status status;
2043
2044 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
Pali Rohár0fe57262017-08-12 09:44:16 +02002045 0, asus->debug.method_id,
Corentin Charyef343492011-02-26 10:20:39 +01002046 &input, &output);
2047
2048 if (ACPI_FAILURE(status))
2049 return -EIO;
2050
2051 obj = (union acpi_object *)output.pointer;
2052 if (obj && obj->type == ACPI_TYPE_INTEGER)
2053 seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id,
2054 asus->debug.dev_id, asus->debug.ctrl_param,
2055 (u32) obj->integer.value);
2056 else
2057 seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id,
2058 asus->debug.dev_id, asus->debug.ctrl_param,
Dan Carpentera1d60862011-03-15 10:07:37 +03002059 obj ? obj->type : -1);
Corentin Charyef343492011-02-26 10:20:39 +01002060
2061 kfree(obj);
2062
2063 return 0;
2064}
2065
Corentin Charye12e6d92011-02-26 10:20:31 +01002066static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {
2067 {NULL, "devs", show_devs},
2068 {NULL, "dsts", show_dsts},
Corentin Charyef343492011-02-26 10:20:39 +01002069 {NULL, "call", show_call},
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002070};
2071
Corentin Charye12e6d92011-02-26 10:20:31 +01002072static int asus_wmi_debugfs_open(struct inode *inode, struct file *file)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002073{
Corentin Charye12e6d92011-02-26 10:20:31 +01002074 struct asus_wmi_debugfs_node *node = inode->i_private;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002075
Corentin Charye12e6d92011-02-26 10:20:31 +01002076 return single_open(file, node->show, node->asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002077}
2078
Corentin Charye12e6d92011-02-26 10:20:31 +01002079static const struct file_operations asus_wmi_debugfs_io_ops = {
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002080 .owner = THIS_MODULE,
Corentin Charye12e6d92011-02-26 10:20:31 +01002081 .open = asus_wmi_debugfs_open,
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002082 .read = seq_read,
2083 .llseek = seq_lseek,
2084 .release = single_release,
2085};
2086
Corentin Charye12e6d92011-02-26 10:20:31 +01002087static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002088{
Corentin Charye12e6d92011-02-26 10:20:31 +01002089 debugfs_remove_recursive(asus->debug.root);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002090}
2091
Corentin Charye12e6d92011-02-26 10:20:31 +01002092static int asus_wmi_debugfs_init(struct asus_wmi *asus)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002093{
2094 struct dentry *dent;
2095 int i;
2096
Corentin Charye12e6d92011-02-26 10:20:31 +01002097 asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
2098 if (!asus->debug.root) {
vic0ed60652013-05-22 21:32:10 +03002099 pr_err("failed to create debugfs directory\n");
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002100 goto error_debugfs;
2101 }
2102
Corentin Charyef343492011-02-26 10:20:39 +01002103 dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR,
2104 asus->debug.root, &asus->debug.method_id);
2105 if (!dent)
2106 goto error_debugfs;
2107
Corentin Charye12e6d92011-02-26 10:20:31 +01002108 dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR,
2109 asus->debug.root, &asus->debug.dev_id);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002110 if (!dent)
2111 goto error_debugfs;
2112
Corentin Charye12e6d92011-02-26 10:20:31 +01002113 dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR,
2114 asus->debug.root, &asus->debug.ctrl_param);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002115 if (!dent)
2116 goto error_debugfs;
2117
Corentin Charye12e6d92011-02-26 10:20:31 +01002118 for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
2119 struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002120
Corentin Charye12e6d92011-02-26 10:20:31 +01002121 node->asus = asus;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002122 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
Corentin Charye12e6d92011-02-26 10:20:31 +01002123 asus->debug.root, node,
2124 &asus_wmi_debugfs_io_ops);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002125 if (!dent) {
2126 pr_err("failed to create debug file: %s\n", node->name);
2127 goto error_debugfs;
2128 }
2129 }
2130
2131 return 0;
2132
2133error_debugfs:
Corentin Charye12e6d92011-02-26 10:20:31 +01002134 asus_wmi_debugfs_exit(asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002135 return -ENOMEM;
2136}
2137
Kast Bernd53e755c2015-05-13 16:24:16 +02002138static int asus_wmi_fan_init(struct asus_wmi *asus)
2139{
2140 int status;
2141
2142 asus->asus_hwmon_pwm = -1;
2143 asus->asus_hwmon_num_fans = -1;
2144 asus->asus_hwmon_fan_manual_mode = false;
2145
2146 status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
2147 if (status) {
2148 asus->asus_hwmon_num_fans = 0;
2149 pr_warn("Could not determine number of fans: %d\n", status);
2150 return -ENXIO;
2151 }
2152
2153 pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
2154 return 0;
2155}
2156
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002157/*
Corentin Chary27c136c2010-11-29 08:14:05 +01002158 * WMI Driver
2159 */
Corentin Charye12e6d92011-02-26 10:20:31 +01002160static int asus_wmi_add(struct platform_device *pdev)
Corentin Charyafa7c882011-02-06 13:28:28 +01002161{
Corentin Charye12e6d92011-02-26 10:20:31 +01002162 struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
2163 struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
2164 struct asus_wmi *asus;
Hans de Goeded8c66f62014-07-08 10:47:21 +02002165 const char *chassis_type;
Yong Wangee027e42010-03-21 10:26:34 +08002166 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01002167 int err;
AceLan Kaoa50bd122012-07-26 17:13:31 +08002168 u32 result;
Yong Wangee027e42010-03-21 10:26:34 +08002169
Corentin Charye12e6d92011-02-26 10:20:31 +01002170 asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
2171 if (!asus)
Corentin Charya04ce292011-02-06 13:28:33 +01002172 return -ENOMEM;
2173
Corentin Charye12e6d92011-02-26 10:20:31 +01002174 asus->driver = wdrv;
2175 asus->platform_device = pdev;
2176 wdrv->platform_device = pdev;
2177 platform_set_drvdata(asus->platform_device, asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002178
AceLan Kaoc87992d2012-03-20 09:53:08 +01002179 if (wdrv->detect_quirks)
2180 wdrv->detect_quirks(asus->driver);
Corentin Charyafa7c882011-02-06 13:28:28 +01002181
Corentin Charye12e6d92011-02-26 10:20:31 +01002182 err = asus_wmi_platform_init(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002183 if (err)
2184 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08002185
Corentin Charye12e6d92011-02-26 10:20:31 +01002186 err = asus_wmi_input_init(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002187 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01002188 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08002189
Kast Bernd53e755c2015-05-13 16:24:16 +02002190 err = asus_wmi_fan_init(asus); /* probably no problems on error */
2191 asus_hwmon_fan_set_auto(asus);
2192
Corentin Charye07babd2011-02-26 10:20:42 +01002193 err = asus_wmi_hwmon_init(asus);
2194 if (err)
2195 goto fail_hwmon;
2196
Corentin Charye12e6d92011-02-26 10:20:31 +01002197 err = asus_wmi_led_init(asus);
Corentin Chary084fca62010-11-29 08:14:06 +01002198 if (err)
2199 goto fail_leds;
2200
João Paulo Rechi Vita71050ae2017-02-20 14:50:22 -05002201 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
2202 if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
2203 asus->driver->wlan_ctrl_by_user = 1;
2204
João Paulo Rechi Vitad1c4e9b2017-02-20 14:50:23 -05002205 if (!(asus->driver->wlan_ctrl_by_user && ashs_present())) {
João Paulo Rechi Vitaa977e592016-06-13 16:57:31 -04002206 err = asus_wmi_rfkill_init(asus);
2207 if (err)
2208 goto fail_rfkill;
2209 }
Corentin Charyba48fdb2010-11-29 08:14:07 +01002210
Oleksij Rempele9b61512017-04-28 16:19:49 +02002211 if (asus->driver->quirks->wmi_force_als_set)
2212 asus_wmi_set_als();
2213
Hans de Goeded8c66f62014-07-08 10:47:21 +02002214 /* Some Asus desktop boards export an acpi-video backlight interface,
2215 stop this from showing up */
2216 chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
2217 if (chassis_type && !strcmp(chassis_type, "3"))
Hans de Goede62c4aa12015-06-16 16:27:58 +02002218 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
2219
AceLan Kao272c77d2012-06-13 09:32:06 +02002220 if (asus->driver->quirks->wmi_backlight_power)
Hans de Goede62c4aa12015-06-16 16:27:58 +02002221 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
2222
zino lin999d4372016-08-28 16:12:06 +08002223 if (asus->driver->quirks->wmi_backlight_native)
2224 acpi_video_set_dmi_backlight_type(acpi_backlight_native);
2225
Kai-Chuan Hsieh8023eff2016-09-01 23:55:55 +08002226 if (asus->driver->quirks->xusb2pr)
2227 asus_wmi_set_xusb2pr(asus);
2228
Hans de Goede62c4aa12015-06-16 16:27:58 +02002229 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
Corentin Charye12e6d92011-02-26 10:20:31 +01002230 err = asus_wmi_backlight_init(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01002231 if (err && err != -ENODEV)
Corentin Chary27c136c2010-11-29 08:14:05 +01002232 goto fail_backlight;
Hans de Goede62c4aa12015-06-16 16:27:58 +02002233 }
Yong Wang45f2c692010-04-11 09:27:19 +08002234
Corentin Charye12e6d92011-02-26 10:20:31 +01002235 status = wmi_install_notify_handler(asus->driver->event_guid,
2236 asus_wmi_notify, asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002237 if (ACPI_FAILURE(status)) {
Corentin Charye12e6d92011-02-26 10:20:31 +01002238 pr_err("Unable to register notify handler - %d\n", status);
Yong Wang45f2c692010-04-11 09:27:19 +08002239 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01002240 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08002241 }
2242
Corentin Charye12e6d92011-02-26 10:20:31 +01002243 err = asus_wmi_debugfs_init(asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002244 if (err)
2245 goto fail_debugfs;
2246
Corentin Charya04ce292011-02-06 13:28:33 +01002247 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08002248
Corentin Chary8c1b2d82010-11-29 08:14:09 +01002249fail_debugfs:
Corentin Charye12e6d92011-02-26 10:20:31 +01002250 wmi_remove_notify_handler(asus->driver->event_guid);
Corentin Chary27c136c2010-11-29 08:14:05 +01002251fail_wmi_handler:
Corentin Charye12e6d92011-02-26 10:20:31 +01002252 asus_wmi_backlight_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002253fail_backlight:
Corentin Charye12e6d92011-02-26 10:20:31 +01002254 asus_wmi_rfkill_exit(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +01002255fail_rfkill:
Corentin Charye12e6d92011-02-26 10:20:31 +01002256 asus_wmi_led_exit(asus);
Corentin Chary084fca62010-11-29 08:14:06 +01002257fail_leds:
Corentin Charye07babd2011-02-26 10:20:42 +01002258fail_hwmon:
Corentin Charye12e6d92011-02-26 10:20:31 +01002259 asus_wmi_input_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002260fail_input:
Corentin Charye12e6d92011-02-26 10:20:31 +01002261 asus_wmi_platform_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01002262fail_platform:
Corentin Charye12e6d92011-02-26 10:20:31 +01002263 kfree(asus);
Corentin Charya04ce292011-02-06 13:28:33 +01002264 return err;
Yong Wang45f2c692010-04-11 09:27:19 +08002265}
2266
Corentin Charye12e6d92011-02-26 10:20:31 +01002267static int asus_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08002268{
Corentin Charye12e6d92011-02-26 10:20:31 +01002269 struct asus_wmi *asus;
Yong Wang45f2c692010-04-11 09:27:19 +08002270
Corentin Charye12e6d92011-02-26 10:20:31 +01002271 asus = platform_get_drvdata(device);
2272 wmi_remove_notify_handler(asus->driver->event_guid);
2273 asus_wmi_backlight_exit(asus);
2274 asus_wmi_input_exit(asus);
2275 asus_wmi_led_exit(asus);
2276 asus_wmi_rfkill_exit(asus);
2277 asus_wmi_debugfs_exit(asus);
2278 asus_wmi_platform_exit(asus);
Kast Bernd53e755c2015-05-13 16:24:16 +02002279 asus_hwmon_fan_set_auto(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002280
Corentin Charye12e6d92011-02-26 10:20:31 +01002281 kfree(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08002282 return 0;
2283}
2284
Corentin Chary0773d7f2011-02-06 13:28:32 +01002285/*
2286 * Platform driver - hibernate/resume callbacks
2287 */
Corentin Charye12e6d92011-02-26 10:20:31 +01002288static int asus_hotk_thaw(struct device *device)
Corentin Chary0773d7f2011-02-06 13:28:32 +01002289{
Corentin Charye12e6d92011-02-26 10:20:31 +01002290 struct asus_wmi *asus = dev_get_drvdata(device);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002291
Corentin Charya7ce3f02011-02-26 10:20:33 +01002292 if (asus->wlan.rfkill) {
Corentin Chary0773d7f2011-02-06 13:28:32 +01002293 bool wlan;
2294
2295 /*
2296 * Work around bios bug - acpi _PTS turns off the wireless led
2297 * during suspend. Normally it restores it on resume, but
2298 * we should kick it ourselves in case hibernation is aborted.
2299 */
Corentin Chary1d070f82011-02-26 10:20:36 +01002300 wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charye12e6d92011-02-26 10:20:31 +01002301 asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002302 }
2303
2304 return 0;
2305}
2306
Oleksij Rempel30734042015-09-14 11:16:30 +02002307static int asus_hotk_resume(struct device *device)
2308{
2309 struct asus_wmi *asus = dev_get_drvdata(device);
2310
2311 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
2312 queue_work(asus->led_workqueue, &asus->kbd_led_work);
2313
2314 return 0;
2315}
2316
Corentin Charye12e6d92011-02-26 10:20:31 +01002317static int asus_hotk_restore(struct device *device)
Corentin Chary0773d7f2011-02-06 13:28:32 +01002318{
Corentin Charye12e6d92011-02-26 10:20:31 +01002319 struct asus_wmi *asus = dev_get_drvdata(device);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002320 int bl;
2321
2322 /* Refresh both wlan rfkill state and pci hotplug */
Corentin Charya7ce3f02011-02-26 10:20:33 +01002323 if (asus->wlan.rfkill)
Corentin Charye12e6d92011-02-26 10:20:31 +01002324 asus_rfkill_hotplug(asus);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002325
Corentin Charya7ce3f02011-02-26 10:20:33 +01002326 if (asus->bluetooth.rfkill) {
Corentin Chary1d070f82011-02-26 10:20:36 +01002327 bl = !asus_wmi_get_devstate_simple(asus,
2328 ASUS_WMI_DEVID_BLUETOOTH);
Corentin Charya7ce3f02011-02-26 10:20:33 +01002329 rfkill_set_sw_state(asus->bluetooth.rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01002330 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01002331 if (asus->wimax.rfkill) {
Corentin Chary1d070f82011-02-26 10:20:36 +01002332 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);
Corentin Charya7ce3f02011-02-26 10:20:33 +01002333 rfkill_set_sw_state(asus->wimax.rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01002334 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01002335 if (asus->wwan3g.rfkill) {
Corentin Chary1d070f82011-02-26 10:20:36 +01002336 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
Corentin Charya7ce3f02011-02-26 10:20:33 +01002337 rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002338 }
Corentin Chary43be8bd2011-07-01 11:34:40 +02002339 if (asus->gps.rfkill) {
2340 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
2341 rfkill_set_sw_state(asus->gps.rfkill, bl);
2342 }
Corentin Charya912d322011-07-01 11:34:41 +02002343 if (asus->uwb.rfkill) {
2344 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
2345 rfkill_set_sw_state(asus->uwb.rfkill, bl);
2346 }
Oleksij Rempel30734042015-09-14 11:16:30 +02002347 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
2348 queue_work(asus->led_workqueue, &asus->kbd_led_work);
Corentin Chary0773d7f2011-02-06 13:28:32 +01002349
2350 return 0;
2351}
2352
Corentin Charye12e6d92011-02-26 10:20:31 +01002353static const struct dev_pm_ops asus_pm_ops = {
2354 .thaw = asus_hotk_thaw,
2355 .restore = asus_hotk_restore,
Oleksij Rempel30734042015-09-14 11:16:30 +02002356 .resume = asus_hotk_resume,
Corentin Chary0773d7f2011-02-06 13:28:32 +01002357};
2358
Corentin Charye12e6d92011-02-26 10:20:31 +01002359static int asus_wmi_probe(struct platform_device *pdev)
Corentin Charyd358cb52010-11-29 08:14:14 +01002360{
Corentin Charye12e6d92011-02-26 10:20:31 +01002361 struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
2362 struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
2363 int ret;
Corentin Charyd358cb52010-11-29 08:14:14 +01002364
Corentin Charye12e6d92011-02-26 10:20:31 +01002365 if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
Joe Perches5ad77dc2011-03-29 15:21:35 -07002366 pr_warn("Management GUID not found\n");
Yong Wangee027e42010-03-21 10:26:34 +08002367 return -ENODEV;
2368 }
2369
Corentin Charye12e6d92011-02-26 10:20:31 +01002370 if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
Joe Perches5ad77dc2011-03-29 15:21:35 -07002371 pr_warn("Event GUID not found\n");
Corentin Charyd358cb52010-11-29 08:14:14 +01002372 return -ENODEV;
2373 }
2374
Corentin Charye12e6d92011-02-26 10:20:31 +01002375 if (wdrv->probe) {
2376 ret = wdrv->probe(pdev);
2377 if (ret)
2378 return ret;
2379 }
2380
2381 return asus_wmi_add(pdev);
Corentin Charya04ce292011-02-06 13:28:33 +01002382}
Yong Wangee027e42010-03-21 10:26:34 +08002383
Corentin Charye12e6d92011-02-26 10:20:31 +01002384static bool used;
Yong Wangee027e42010-03-21 10:26:34 +08002385
Corentin Chary8fe8c252011-07-01 11:34:32 +02002386int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
Corentin Charya04ce292011-02-06 13:28:33 +01002387{
Corentin Charye12e6d92011-02-26 10:20:31 +01002388 struct platform_driver *platform_driver;
2389 struct platform_device *platform_device;
2390
2391 if (used)
2392 return -EBUSY;
2393
2394 platform_driver = &driver->platform_driver;
2395 platform_driver->remove = asus_wmi_remove;
2396 platform_driver->driver.owner = driver->owner;
2397 platform_driver->driver.name = driver->name;
2398 platform_driver->driver.pm = &asus_pm_ops;
2399
2400 platform_device = platform_create_bundle(platform_driver,
2401 asus_wmi_probe,
Corentin Charya04ce292011-02-06 13:28:33 +01002402 NULL, 0, NULL, 0);
2403 if (IS_ERR(platform_device))
2404 return PTR_ERR(platform_device);
Corentin Charye12e6d92011-02-26 10:20:31 +01002405
2406 used = true;
2407 return 0;
2408}
2409EXPORT_SYMBOL_GPL(asus_wmi_register_driver);
2410
2411void asus_wmi_unregister_driver(struct asus_wmi_driver *driver)
2412{
2413 platform_device_unregister(driver->platform_device);
2414 platform_driver_unregister(&driver->platform_driver);
2415 used = false;
2416}
2417EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
2418
2419static int __init asus_wmi_init(void)
2420{
2421 if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
vic0ed60652013-05-22 21:32:10 +03002422 pr_info("Asus Management GUID not found\n");
Corentin Charye12e6d92011-02-26 10:20:31 +01002423 return -ENODEV;
2424 }
2425
vic0ed60652013-05-22 21:32:10 +03002426 pr_info("ASUS WMI generic driver loaded\n");
Yong Wangee027e42010-03-21 10:26:34 +08002427 return 0;
2428}
2429
Corentin Charye12e6d92011-02-26 10:20:31 +01002430static void __exit asus_wmi_exit(void)
Yong Wangee027e42010-03-21 10:26:34 +08002431{
vic0ed60652013-05-22 21:32:10 +03002432 pr_info("ASUS WMI generic driver unloaded\n");
Yong Wangee027e42010-03-21 10:26:34 +08002433}
2434
Corentin Charye12e6d92011-02-26 10:20:31 +01002435module_init(asus_wmi_init);
2436module_exit(asus_wmi_exit);