blob: c7a36f6b058048eb092717eff06a9e413a6a2546 [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>
Yong Wangee027e42010-03-21 10:26:34 +080048#include <acpi/acpi_bus.h>
49#include <acpi/acpi_drivers.h>
AceLan Kao272c77d2012-06-13 09:32:06 +020050#ifdef CONFIG_ACPI_VIDEO
51#include <acpi/video.h>
52#endif
Yong Wangee027e42010-03-21 10:26:34 +080053
Corentin Charye12e6d92011-02-26 10:20:31 +010054#include "asus-wmi.h"
Yong Wang45f2c692010-04-11 09:27:19 +080055
Corentin Charye12e6d92011-02-26 10:20:31 +010056MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>, "
57 "Yong Wang <yong.y.wang@intel.com>");
58MODULE_DESCRIPTION("Asus Generic WMI Driver");
Yong Wangee027e42010-03-21 10:26:34 +080059MODULE_LICENSE("GPL");
60
Corentin Charye12e6d92011-02-26 10:20:31 +010061#define to_platform_driver(drv) \
62 (container_of((drv), struct platform_driver, driver))
Corentin Charyd358cb52010-11-29 08:14:14 +010063
Corentin Charye12e6d92011-02-26 10:20:31 +010064#define to_asus_wmi_driver(pdrv) \
65 (container_of((pdrv), struct asus_wmi_driver, platform_driver))
Yong Wangee027e42010-03-21 10:26:34 +080066
Corentin Charye12e6d92011-02-26 10:20:31 +010067#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
Yong Wangee027e42010-03-21 10:26:34 +080068
Corentin Chary33e0e6f2011-02-06 13:28:34 +010069#define NOTIFY_BRNUP_MIN 0x11
70#define NOTIFY_BRNUP_MAX 0x1f
71#define NOTIFY_BRNDOWN_MIN 0x20
72#define NOTIFY_BRNDOWN_MAX 0x2e
Corentin Charye9809c02011-07-01 11:34:31 +020073#define NOTIFY_KBD_BRTUP 0xc4
74#define NOTIFY_KBD_BRTDWN 0xc5
Yong Wangee027e42010-03-21 10:26:34 +080075
Corentin Chary43815942011-02-06 13:28:43 +010076/* WMI Methods */
Corentin Chary2f686b52011-02-26 10:20:41 +010077#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
78#define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */
79#define ASUS_WMI_METHODID_GLCD 0x44434C47 /* Get LCD status */
80#define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */
81#define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */
82#define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */
83#define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */
84#define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */
85#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */
86#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */
87#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */
88#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */
89#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/
90#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */
91#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */
92#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */
93#define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */
94#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */
95#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */
Yong Wang3d7b1652010-04-11 09:27:54 +080096
Corentin Charyd33da3b2011-02-26 10:20:35 +010097#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE
98
Corentin Chary43815942011-02-06 13:28:43 +010099/* Wireless */
Corentin Chary2f686b52011-02-26 10:20:41 +0100100#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001
101#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
Corentin Chary79ec1172011-07-01 11:34:35 +0200102#define ASUS_WMI_DEVID_CWAP 0x00010003
Corentin Charye12e6d92011-02-26 10:20:31 +0100103#define ASUS_WMI_DEVID_WLAN 0x00010011
104#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
Corentin Chary2f686b52011-02-26 10:20:41 +0100105#define ASUS_WMI_DEVID_GPS 0x00010015
Corentin Charye12e6d92011-02-26 10:20:31 +0100106#define ASUS_WMI_DEVID_WIMAX 0x00010017
107#define ASUS_WMI_DEVID_WWAN3G 0x00010019
Corentin Chary2f686b52011-02-26 10:20:41 +0100108#define ASUS_WMI_DEVID_UWB 0x00010021
109
110/* Leds */
111/* 0x000200XX and 0x000400XX */
Corentin Chary79ec1172011-07-01 11:34:35 +0200112#define ASUS_WMI_DEVID_LED1 0x00020011
113#define ASUS_WMI_DEVID_LED2 0x00020012
114#define ASUS_WMI_DEVID_LED3 0x00020013
115#define ASUS_WMI_DEVID_LED4 0x00020014
116#define ASUS_WMI_DEVID_LED5 0x00020015
117#define ASUS_WMI_DEVID_LED6 0x00020016
Corentin Chary43815942011-02-06 13:28:43 +0100118
119/* Backlight and Brightness */
Corentin Charye12e6d92011-02-26 10:20:31 +0100120#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011
121#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012
Corentin Chary2f686b52011-02-26 10:20:41 +0100122#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
123#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
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
Yong Wangee027e42010-03-21 10:26:34 +0800153
Yong Wang3d7b1652010-04-11 09:27:54 +0800154struct bios_args {
Corentin Charyd33da3b2011-02-26 10:20:35 +0100155 u32 arg0;
156 u32 arg1;
157} __packed;
Yong Wang3d7b1652010-04-11 09:27:54 +0800158
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100159/*
Corentin Charye12e6d92011-02-26 10:20:31 +0100160 * <platform>/ - debugfs root directory
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100161 * dev_id - current dev_id
162 * ctrl_param - current ctrl_param
Corentin Charyef343492011-02-26 10:20:39 +0100163 * method_id - current method_id
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100164 * devs - call DEVS(dev_id, ctrl_param) and print result
165 * dsts - call DSTS(dev_id) and print result
Corentin Charyef343492011-02-26 10:20:39 +0100166 * call - call method_id(dev_id, ctrl_param) and print result
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100167 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100168struct asus_wmi_debug {
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100169 struct dentry *root;
Corentin Charyef343492011-02-26 10:20:39 +0100170 u32 method_id;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100171 u32 dev_id;
172 u32 ctrl_param;
173};
174
Corentin Charya7ce3f02011-02-26 10:20:33 +0100175struct asus_rfkill {
176 struct asus_wmi *asus;
177 struct rfkill *rfkill;
178 u32 dev_id;
179};
180
Corentin Charye12e6d92011-02-26 10:20:31 +0100181struct asus_wmi {
Corentin Chary1d070f892011-02-26 10:20:36 +0100182 int dsts_id;
Corentin Chary46dbca82011-02-26 10:20:38 +0100183 int spec;
184 int sfun;
Corentin Chary1d070f892011-02-26 10:20:36 +0100185
Yong Wang81248882010-04-11 09:26:33 +0800186 struct input_dev *inputdev;
Yong Wang3d7b1652010-04-11 09:27:54 +0800187 struct backlight_device *backlight_device;
Corentin Charye07babd2011-02-26 10:20:42 +0100188 struct device *hwmon_device;
Corentin Chary27c136c2010-11-29 08:14:05 +0100189 struct platform_device *platform_device;
Corentin Chary084fca62010-11-29 08:14:06 +0100190
191 struct led_classdev tpd_led;
192 int tpd_led_wk;
Corentin Charye9809c02011-07-01 11:34:31 +0200193 struct led_classdev kbd_led;
194 int kbd_led_wk;
Corentin Chary084fca62010-11-29 08:14:06 +0100195 struct workqueue_struct *led_workqueue;
196 struct work_struct tpd_led_work;
Corentin Charye9809c02011-07-01 11:34:31 +0200197 struct work_struct kbd_led_work;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100198
Corentin Charya7ce3f02011-02-26 10:20:33 +0100199 struct asus_rfkill wlan;
200 struct asus_rfkill bluetooth;
201 struct asus_rfkill wimax;
202 struct asus_rfkill wwan3g;
Corentin Chary43be8bd2011-07-01 11:34:40 +0200203 struct asus_rfkill gps;
Corentin Charya912d322011-07-01 11:34:41 +0200204 struct asus_rfkill uwb;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100205
Corentin Charyafa7c882011-02-06 13:28:28 +0100206 struct hotplug_slot *hotplug_slot;
207 struct mutex hotplug_lock;
Corentin Chary279f8f92011-02-06 13:28:29 +0100208 struct mutex wmi_lock;
209 struct workqueue_struct *hotplug_workqueue;
210 struct work_struct hotplug_work;
Corentin Charyafa7c882011-02-06 13:28:28 +0100211
Corentin Charye12e6d92011-02-26 10:20:31 +0100212 struct asus_wmi_debug debug;
213
214 struct asus_wmi_driver *driver;
Yong Wang81248882010-04-11 09:26:33 +0800215};
216
Corentin Charye12e6d92011-02-26 10:20:31 +0100217static int asus_wmi_input_init(struct asus_wmi *asus)
Yong Wangee027e42010-03-21 10:26:34 +0800218{
219 int err;
220
Corentin Charye12e6d92011-02-26 10:20:31 +0100221 asus->inputdev = input_allocate_device();
222 if (!asus->inputdev)
Yong Wangee027e42010-03-21 10:26:34 +0800223 return -ENOMEM;
224
Corentin Chary58a9f392011-03-30 16:32:32 +0200225 asus->inputdev->name = asus->driver->input_name;
226 asus->inputdev->phys = asus->driver->input_phys;
Corentin Charye12e6d92011-02-26 10:20:31 +0100227 asus->inputdev->id.bustype = BUS_HOST;
228 asus->inputdev->dev.parent = &asus->platform_device->dev;
Seth Forshee39bbde02011-07-04 09:49:20 +0200229 set_bit(EV_REP, asus->inputdev->evbit);
Yong Wangee027e42010-03-21 10:26:34 +0800230
Corentin Charye12e6d92011-02-26 10:20:31 +0100231 err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
Yong Wangee027e42010-03-21 10:26:34 +0800232 if (err)
233 goto err_free_dev;
234
Corentin Charye12e6d92011-02-26 10:20:31 +0100235 err = input_register_device(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800236 if (err)
237 goto err_free_keymap;
238
239 return 0;
240
241err_free_keymap:
Corentin Charye12e6d92011-02-26 10:20:31 +0100242 sparse_keymap_free(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800243err_free_dev:
Corentin Charye12e6d92011-02-26 10:20:31 +0100244 input_free_device(asus->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800245 return err;
246}
247
Corentin Charye12e6d92011-02-26 10:20:31 +0100248static void asus_wmi_input_exit(struct asus_wmi *asus)
Yong Wang81248882010-04-11 09:26:33 +0800249{
Corentin Charye12e6d92011-02-26 10:20:31 +0100250 if (asus->inputdev) {
251 sparse_keymap_free(asus->inputdev);
252 input_unregister_device(asus->inputdev);
Yong Wang81248882010-04-11 09:26:33 +0800253 }
254
Corentin Charye12e6d92011-02-26 10:20:31 +0100255 asus->inputdev = NULL;
Yong Wang81248882010-04-11 09:26:33 +0800256}
257
Corentin Charyd33da3b2011-02-26 10:20:35 +0100258static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
259 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800260{
Corentin Charyd33da3b2011-02-26 10:20:35 +0100261 struct bios_args args = {
262 .arg0 = arg0,
263 .arg1 = arg1,
264 };
265 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
Yong Wang3d7b1652010-04-11 09:27:54 +0800266 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
Yong Wang3d7b1652010-04-11 09:27:54 +0800267 acpi_status status;
Corentin Charyd33da3b2011-02-26 10:20:35 +0100268 union acpi_object *obj;
Yong Wang3d7b1652010-04-11 09:27:54 +0800269 u32 tmp;
270
Corentin Charyd33da3b2011-02-26 10:20:35 +0100271 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,
Corentin Charyafa7c882011-02-06 13:28:28 +0100272 &input, &output);
Yong Wang3d7b1652010-04-11 09:27:54 +0800273
274 if (ACPI_FAILURE(status))
Corentin Charyd33da3b2011-02-26 10:20:35 +0100275 goto exit;
Yong Wang3d7b1652010-04-11 09:27:54 +0800276
277 obj = (union acpi_object *)output.pointer;
278 if (obj && obj->type == ACPI_TYPE_INTEGER)
Corentin Charye12e6d92011-02-26 10:20:31 +0100279 tmp = (u32) obj->integer.value;
Yong Wang3d7b1652010-04-11 09:27:54 +0800280 else
281 tmp = 0;
282
Corentin Chary2a3f0062010-11-29 08:14:10 +0100283 if (retval)
284 *retval = tmp;
Yong Wang3d7b1652010-04-11 09:27:54 +0800285
286 kfree(obj);
287
Corentin Charyd33da3b2011-02-26 10:20:35 +0100288exit:
289 if (ACPI_FAILURE(status))
290 return -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +0800291
Corentin Charyd33da3b2011-02-26 10:20:35 +0100292 if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
293 return -ENODEV;
294
295 return 0;
Yong Wang3d7b1652010-04-11 09:27:54 +0800296}
297
Corentin Chary1d070f892011-02-26 10:20:36 +0100298static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
Corentin Charyd33da3b2011-02-26 10:20:35 +0100299{
Corentin Chary1d070f892011-02-26 10:20:36 +0100300 return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
Corentin Charyd33da3b2011-02-26 10:20:35 +0100301}
302
303static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
Corentin Chary1d070f892011-02-26 10:20:36 +0100304 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800305{
Corentin Charyd33da3b2011-02-26 10:20:35 +0100306 return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
307 ctrl_param, retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800308}
309
Corentin Chary5c956382011-02-06 13:28:31 +0100310/* Helper for special devices with magic return codes */
Corentin Chary1d070f892011-02-26 10:20:36 +0100311static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
312 u32 dev_id, u32 mask)
Corentin Chary5c956382011-02-06 13:28:31 +0100313{
314 u32 retval = 0;
Corentin Charyd33da3b2011-02-26 10:20:35 +0100315 int err;
Corentin Chary5c956382011-02-06 13:28:31 +0100316
Corentin Chary1d070f892011-02-26 10:20:36 +0100317 err = asus_wmi_get_devstate(asus, dev_id, &retval);
Corentin Chary5c956382011-02-06 13:28:31 +0100318
Corentin Charyd33da3b2011-02-26 10:20:35 +0100319 if (err < 0)
320 return err;
Corentin Chary5c956382011-02-06 13:28:31 +0100321
Corentin Charye12e6d92011-02-26 10:20:31 +0100322 if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))
Corentin Chary5c956382011-02-06 13:28:31 +0100323 return -ENODEV;
324
Corentin Charya75fe0d2011-02-26 10:20:34 +0100325 if (mask == ASUS_WMI_DSTS_STATUS_BIT) {
326 if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)
327 return -ENODEV;
328 }
329
Corentin Charyb7187262011-02-06 13:28:39 +0100330 return retval & mask;
331}
332
Corentin Chary1d070f892011-02-26 10:20:36 +0100333static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
Corentin Charyb7187262011-02-06 13:28:39 +0100334{
Corentin Chary1d070f892011-02-26 10:20:36 +0100335 return asus_wmi_get_devstate_bits(asus, dev_id,
336 ASUS_WMI_DSTS_STATUS_BIT);
Corentin Chary5c956382011-02-06 13:28:31 +0100337}
338
Corentin Chary084fca62010-11-29 08:14:06 +0100339/*
340 * LEDs
341 */
342/*
343 * These functions actually update the LED's, and are called from a
344 * workqueue. By doing this as separate work rather than when the LED
Corentin Charye12e6d92011-02-26 10:20:31 +0100345 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
Corentin Chary084fca62010-11-29 08:14:06 +0100346 * potentially bad time, such as a timer interrupt.
347 */
348static void tpd_led_update(struct work_struct *work)
349{
350 int ctrl_param;
Corentin Charye12e6d92011-02-26 10:20:31 +0100351 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100352
Corentin Charye12e6d92011-02-26 10:20:31 +0100353 asus = container_of(work, struct asus_wmi, tpd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100354
Corentin Charye12e6d92011-02-26 10:20:31 +0100355 ctrl_param = asus->tpd_led_wk;
356 asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100357}
358
359static void tpd_led_set(struct led_classdev *led_cdev,
360 enum led_brightness value)
361{
Corentin Charye12e6d92011-02-26 10:20:31 +0100362 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100363
Corentin Charye12e6d92011-02-26 10:20:31 +0100364 asus = container_of(led_cdev, struct asus_wmi, tpd_led);
Corentin Chary084fca62010-11-29 08:14:06 +0100365
Corentin Charye12e6d92011-02-26 10:20:31 +0100366 asus->tpd_led_wk = !!value;
367 queue_work(asus->led_workqueue, &asus->tpd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100368}
369
Corentin Charye12e6d92011-02-26 10:20:31 +0100370static int read_tpd_led_state(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100371{
Corentin Chary1d070f892011-02-26 10:20:36 +0100372 return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);
Corentin Chary084fca62010-11-29 08:14:06 +0100373}
374
375static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
376{
Corentin Charye12e6d92011-02-26 10:20:31 +0100377 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100378
Corentin Charye12e6d92011-02-26 10:20:31 +0100379 asus = container_of(led_cdev, struct asus_wmi, tpd_led);
Corentin Chary084fca62010-11-29 08:14:06 +0100380
Corentin Charye12e6d92011-02-26 10:20:31 +0100381 return read_tpd_led_state(asus);
Corentin Chary084fca62010-11-29 08:14:06 +0100382}
383
Corentin Charye9809c02011-07-01 11:34:31 +0200384static void kbd_led_update(struct work_struct *work)
Corentin Chary084fca62010-11-29 08:14:06 +0100385{
Corentin Charye9809c02011-07-01 11:34:31 +0200386 int ctrl_param = 0;
387 struct asus_wmi *asus;
Corentin Chary084fca62010-11-29 08:14:06 +0100388
Corentin Charye9809c02011-07-01 11:34:31 +0200389 asus = container_of(work, struct asus_wmi, kbd_led_work);
Corentin Chary084fca62010-11-29 08:14:06 +0100390
Corentin Charye9809c02011-07-01 11:34:31 +0200391 /*
392 * bits 0-2: level
393 * bit 7: light on/off
394 */
395 if (asus->kbd_led_wk > 0)
396 ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
Corentin Chary084fca62010-11-29 08:14:06 +0100397
Corentin Charye9809c02011-07-01 11:34:31 +0200398 asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
399}
Corentin Chary084fca62010-11-29 08:14:06 +0100400
Corentin Charye9809c02011-07-01 11:34:31 +0200401static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
402{
403 int retval;
404
405 /*
406 * bits 0-2: level
407 * bit 7: light on/off
408 * bit 8-10: environment (0: dark, 1: normal, 2: light)
409 * bit 17: status unknown
410 */
411 retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
412 0xFFFF);
413
Corentin Charyaf965e92011-07-01 11:34:34 +0200414 /* Unknown status is considered as off */
Corentin Charye9809c02011-07-01 11:34:31 +0200415 if (retval == 0x8000)
Corentin Charyaf965e92011-07-01 11:34:34 +0200416 retval = 0;
Corentin Charye9809c02011-07-01 11:34:31 +0200417
418 if (retval >= 0) {
419 if (level)
Corentin Charyc09b2232012-03-20 09:53:04 +0100420 *level = retval & 0x7F;
Corentin Charye9809c02011-07-01 11:34:31 +0200421 if (env)
422 *env = (retval >> 8) & 0x7F;
423 retval = 0;
Corentin Chary084fca62010-11-29 08:14:06 +0100424 }
425
Corentin Charye9809c02011-07-01 11:34:31 +0200426 return retval;
427}
428
429static void kbd_led_set(struct led_classdev *led_cdev,
430 enum led_brightness value)
431{
432 struct asus_wmi *asus;
433
434 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
435
436 if (value > asus->kbd_led.max_brightness)
437 value = asus->kbd_led.max_brightness;
438 else if (value < 0)
439 value = 0;
440
441 asus->kbd_led_wk = value;
442 queue_work(asus->led_workqueue, &asus->kbd_led_work);
443}
444
445static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
446{
447 struct asus_wmi *asus;
448 int retval, value;
449
450 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
451
452 retval = kbd_led_read(asus, &value, NULL);
453
454 if (retval < 0)
455 return retval;
456
457 return value;
Corentin Chary084fca62010-11-29 08:14:06 +0100458}
459
Corentin Charye12e6d92011-02-26 10:20:31 +0100460static void asus_wmi_led_exit(struct asus_wmi *asus)
Corentin Chary084fca62010-11-29 08:14:06 +0100461{
Axel Line9298022011-08-08 17:16:01 +0800462 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
463 led_classdev_unregister(&asus->kbd_led);
464 if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
Corentin Charye12e6d92011-02-26 10:20:31 +0100465 led_classdev_unregister(&asus->tpd_led);
466 if (asus->led_workqueue)
467 destroy_workqueue(asus->led_workqueue);
Corentin Chary084fca62010-11-29 08:14:06 +0100468}
469
Corentin Charye9809c02011-07-01 11:34:31 +0200470static int asus_wmi_led_init(struct asus_wmi *asus)
471{
472 int rv = 0;
473
474 asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
475 if (!asus->led_workqueue)
476 return -ENOMEM;
477
478 if (read_tpd_led_state(asus) >= 0) {
479 INIT_WORK(&asus->tpd_led_work, tpd_led_update);
480
481 asus->tpd_led.name = "asus::touchpad";
482 asus->tpd_led.brightness_set = tpd_led_set;
483 asus->tpd_led.brightness_get = tpd_led_get;
484 asus->tpd_led.max_brightness = 1;
485
486 rv = led_classdev_register(&asus->platform_device->dev,
487 &asus->tpd_led);
488 if (rv)
489 goto error;
490 }
491
492 if (kbd_led_read(asus, NULL, NULL) >= 0) {
493 INIT_WORK(&asus->kbd_led_work, kbd_led_update);
494
495 asus->kbd_led.name = "asus::kbd_backlight";
496 asus->kbd_led.brightness_set = kbd_led_set;
497 asus->kbd_led.brightness_get = kbd_led_get;
498 asus->kbd_led.max_brightness = 3;
499
500 rv = led_classdev_register(&asus->platform_device->dev,
501 &asus->kbd_led);
502 }
503
504error:
505 if (rv)
506 asus_wmi_led_exit(asus);
507
508 return rv;
509}
510
511
Corentin Chary084fca62010-11-29 08:14:06 +0100512/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100513 * PCI hotplug (for wlan rfkill)
514 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100515static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100516{
Corentin Chary1d070f892011-02-26 10:20:36 +0100517 int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100518
Corentin Chary5c956382011-02-06 13:28:31 +0100519 if (result < 0)
Corentin Charyafa7c882011-02-06 13:28:28 +0100520 return false;
Corentin Chary5c956382011-02-06 13:28:31 +0100521 return !result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100522}
523
Corentin Charye12e6d92011-02-26 10:20:31 +0100524static void asus_rfkill_hotplug(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100525{
526 struct pci_dev *dev;
527 struct pci_bus *bus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100528 bool blocked;
Corentin Charyafa7c882011-02-06 13:28:28 +0100529 bool absent;
530 u32 l;
531
Corentin Charye12e6d92011-02-26 10:20:31 +0100532 mutex_lock(&asus->wmi_lock);
533 blocked = asus_wlan_rfkill_blocked(asus);
534 mutex_unlock(&asus->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100535
Corentin Charye12e6d92011-02-26 10:20:31 +0100536 mutex_lock(&asus->hotplug_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100537
Corentin Charya7ce3f02011-02-26 10:20:33 +0100538 if (asus->wlan.rfkill)
539 rfkill_set_sw_state(asus->wlan.rfkill, blocked);
Corentin Chary279f8f92011-02-06 13:28:29 +0100540
Corentin Charye12e6d92011-02-26 10:20:31 +0100541 if (asus->hotplug_slot) {
Corentin Charyafa7c882011-02-06 13:28:28 +0100542 bus = pci_find_bus(0, 1);
543 if (!bus) {
Joe Perches5ad77dc2011-03-29 15:21:35 -0700544 pr_warn("Unable to find PCI bus 1?\n");
Corentin Charyafa7c882011-02-06 13:28:28 +0100545 goto out_unlock;
546 }
547
548 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
549 pr_err("Unable to read PCI config space?\n");
550 goto out_unlock;
551 }
552 absent = (l == 0xffffffff);
553
554 if (blocked != absent) {
Joe Perches5ad77dc2011-03-29 15:21:35 -0700555 pr_warn("BIOS says wireless lan is %s, "
556 "but the pci device is %s\n",
557 blocked ? "blocked" : "unblocked",
558 absent ? "absent" : "present");
559 pr_warn("skipped wireless hotplug as probably "
560 "inappropriate for this model\n");
Corentin Charyafa7c882011-02-06 13:28:28 +0100561 goto out_unlock;
562 }
563
564 if (!blocked) {
565 dev = pci_get_slot(bus, 0);
566 if (dev) {
567 /* Device already present */
568 pci_dev_put(dev);
569 goto out_unlock;
570 }
571 dev = pci_scan_single_device(bus, 0);
572 if (dev) {
573 pci_bus_assign_resources(bus);
574 if (pci_bus_add_device(dev))
575 pr_err("Unable to hotplug wifi\n");
576 }
577 } else {
578 dev = pci_get_slot(bus, 0);
579 if (dev) {
Yinghai Lu210647a2012-02-25 13:54:20 -0800580 pci_stop_and_remove_bus_device(dev);
Corentin Charyafa7c882011-02-06 13:28:28 +0100581 pci_dev_put(dev);
582 }
583 }
584 }
585
586out_unlock:
Corentin Charye12e6d92011-02-26 10:20:31 +0100587 mutex_unlock(&asus->hotplug_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100588}
589
Corentin Charye12e6d92011-02-26 10:20:31 +0100590static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)
Corentin Charyafa7c882011-02-06 13:28:28 +0100591{
Corentin Charye12e6d92011-02-26 10:20:31 +0100592 struct asus_wmi *asus = data;
Corentin Charyafa7c882011-02-06 13:28:28 +0100593
594 if (event != ACPI_NOTIFY_BUS_CHECK)
595 return;
596
Corentin Chary279f8f92011-02-06 13:28:29 +0100597 /*
Corentin Charye12e6d92011-02-26 10:20:31 +0100598 * We can't call directly asus_rfkill_hotplug because most
Corentin Chary279f8f92011-02-06 13:28:29 +0100599 * of the time WMBC is still being executed and not reetrant.
600 * There is currently no way to tell ACPICA that we want this
Corentin Charye12e6d92011-02-26 10:20:31 +0100601 * method to be serialized, we schedule a asus_rfkill_hotplug
Corentin Chary279f8f92011-02-06 13:28:29 +0100602 * call later, in a safer context.
603 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100604 queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
Corentin Charyafa7c882011-02-06 13:28:28 +0100605}
606
Corentin Charye12e6d92011-02-26 10:20:31 +0100607static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)
Corentin Charyafa7c882011-02-06 13:28:28 +0100608{
609 acpi_status status;
610 acpi_handle handle;
611
612 status = acpi_get_handle(NULL, node, &handle);
613
614 if (ACPI_SUCCESS(status)) {
615 status = acpi_install_notify_handler(handle,
616 ACPI_SYSTEM_NOTIFY,
Corentin Charye12e6d92011-02-26 10:20:31 +0100617 asus_rfkill_notify, asus);
Corentin Charyafa7c882011-02-06 13:28:28 +0100618 if (ACPI_FAILURE(status))
Joe Perches5ad77dc2011-03-29 15:21:35 -0700619 pr_warn("Failed to register notify on %s\n", node);
Corentin Charyafa7c882011-02-06 13:28:28 +0100620 } else
621 return -ENODEV;
622
623 return 0;
624}
625
Corentin Charye12e6d92011-02-26 10:20:31 +0100626static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
Corentin Charyafa7c882011-02-06 13:28:28 +0100627{
628 acpi_status status = AE_OK;
629 acpi_handle handle;
630
631 status = acpi_get_handle(NULL, node, &handle);
632
633 if (ACPI_SUCCESS(status)) {
634 status = acpi_remove_notify_handler(handle,
Corentin Charye12e6d92011-02-26 10:20:31 +0100635 ACPI_SYSTEM_NOTIFY,
636 asus_rfkill_notify);
Corentin Charyafa7c882011-02-06 13:28:28 +0100637 if (ACPI_FAILURE(status))
638 pr_err("Error removing rfkill notify handler %s\n",
Corentin Charye12e6d92011-02-26 10:20:31 +0100639 node);
Corentin Charyafa7c882011-02-06 13:28:28 +0100640 }
641}
642
Corentin Charye12e6d92011-02-26 10:20:31 +0100643static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
644 u8 *value)
Corentin Charyafa7c882011-02-06 13:28:28 +0100645{
Corentin Chary1d070f892011-02-26 10:20:36 +0100646 struct asus_wmi *asus = hotplug_slot->private;
647 int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100648
Corentin Chary5c956382011-02-06 13:28:31 +0100649 if (result < 0)
650 return result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100651
Corentin Chary5c956382011-02-06 13:28:31 +0100652 *value = !!result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100653 return 0;
654}
655
Corentin Charye12e6d92011-02-26 10:20:31 +0100656static void asus_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
Corentin Charyafa7c882011-02-06 13:28:28 +0100657{
658 kfree(hotplug_slot->info);
659 kfree(hotplug_slot);
660}
661
Corentin Charye12e6d92011-02-26 10:20:31 +0100662static struct hotplug_slot_ops asus_hotplug_slot_ops = {
Corentin Charyafa7c882011-02-06 13:28:28 +0100663 .owner = THIS_MODULE,
Corentin Charye12e6d92011-02-26 10:20:31 +0100664 .get_adapter_status = asus_get_adapter_status,
665 .get_power_status = asus_get_adapter_status,
Corentin Charyafa7c882011-02-06 13:28:28 +0100666};
667
Corentin Charye12e6d92011-02-26 10:20:31 +0100668static void asus_hotplug_work(struct work_struct *work)
Corentin Chary279f8f92011-02-06 13:28:29 +0100669{
Corentin Charye12e6d92011-02-26 10:20:31 +0100670 struct asus_wmi *asus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100671
Corentin Charye12e6d92011-02-26 10:20:31 +0100672 asus = container_of(work, struct asus_wmi, hotplug_work);
673 asus_rfkill_hotplug(asus);
Corentin Chary279f8f92011-02-06 13:28:29 +0100674}
675
Corentin Charye12e6d92011-02-26 10:20:31 +0100676static int asus_setup_pci_hotplug(struct asus_wmi *asus)
Corentin Charyafa7c882011-02-06 13:28:28 +0100677{
678 int ret = -ENOMEM;
679 struct pci_bus *bus = pci_find_bus(0, 1);
680
681 if (!bus) {
682 pr_err("Unable to find wifi PCI bus\n");
683 return -ENODEV;
684 }
685
Corentin Charye12e6d92011-02-26 10:20:31 +0100686 asus->hotplug_workqueue =
687 create_singlethread_workqueue("hotplug_workqueue");
688 if (!asus->hotplug_workqueue)
Corentin Chary279f8f92011-02-06 13:28:29 +0100689 goto error_workqueue;
690
Corentin Charye12e6d92011-02-26 10:20:31 +0100691 INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
Corentin Chary279f8f92011-02-06 13:28:29 +0100692
Corentin Charye12e6d92011-02-26 10:20:31 +0100693 asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
694 if (!asus->hotplug_slot)
Corentin Charyafa7c882011-02-06 13:28:28 +0100695 goto error_slot;
696
Corentin Charye12e6d92011-02-26 10:20:31 +0100697 asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
698 GFP_KERNEL);
699 if (!asus->hotplug_slot->info)
Corentin Charyafa7c882011-02-06 13:28:28 +0100700 goto error_info;
701
Corentin Charye12e6d92011-02-26 10:20:31 +0100702 asus->hotplug_slot->private = asus;
703 asus->hotplug_slot->release = &asus_cleanup_pci_hotplug;
704 asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
705 asus_get_adapter_status(asus->hotplug_slot,
706 &asus->hotplug_slot->info->adapter_status);
Corentin Charyafa7c882011-02-06 13:28:28 +0100707
Corentin Charye12e6d92011-02-26 10:20:31 +0100708 ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi");
Corentin Charyafa7c882011-02-06 13:28:28 +0100709 if (ret) {
710 pr_err("Unable to register hotplug slot - %d\n", ret);
711 goto error_register;
712 }
713
714 return 0;
715
716error_register:
Corentin Charye12e6d92011-02-26 10:20:31 +0100717 kfree(asus->hotplug_slot->info);
Corentin Charyafa7c882011-02-06 13:28:28 +0100718error_info:
Corentin Charye12e6d92011-02-26 10:20:31 +0100719 kfree(asus->hotplug_slot);
720 asus->hotplug_slot = NULL;
Corentin Charyafa7c882011-02-06 13:28:28 +0100721error_slot:
Corentin Charye12e6d92011-02-26 10:20:31 +0100722 destroy_workqueue(asus->hotplug_workqueue);
Corentin Chary279f8f92011-02-06 13:28:29 +0100723error_workqueue:
Corentin Charyafa7c882011-02-06 13:28:28 +0100724 return ret;
725}
726
727/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100728 * Rfkill devices
729 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100730static int asus_rfkill_set(void *data, bool blocked)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100731{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100732 struct asus_rfkill *priv = data;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100733 u32 ctrl_param = !blocked;
734
Corentin Charyd33da3b2011-02-26 10:20:35 +0100735 return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100736}
737
Corentin Charye12e6d92011-02-26 10:20:31 +0100738static void asus_rfkill_query(struct rfkill *rfkill, void *data)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100739{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100740 struct asus_rfkill *priv = data;
Corentin Chary5c956382011-02-06 13:28:31 +0100741 int result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100742
Corentin Chary1d070f892011-02-26 10:20:36 +0100743 result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100744
Corentin Chary5c956382011-02-06 13:28:31 +0100745 if (result < 0)
Corentin Charye12e6d92011-02-26 10:20:31 +0100746 return;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100747
Corentin Charya7ce3f02011-02-26 10:20:33 +0100748 rfkill_set_sw_state(priv->rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100749}
750
Corentin Charye12e6d92011-02-26 10:20:31 +0100751static int asus_rfkill_wlan_set(void *data, bool blocked)
Corentin Chary279f8f92011-02-06 13:28:29 +0100752{
Corentin Charya7ce3f02011-02-26 10:20:33 +0100753 struct asus_rfkill *priv = data;
754 struct asus_wmi *asus = priv->asus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100755 int ret;
756
757 /*
758 * This handler is enabled only if hotplug is enabled.
Corentin Charye12e6d92011-02-26 10:20:31 +0100759 * In this case, the asus_wmi_set_devstate() will
Corentin Chary279f8f92011-02-06 13:28:29 +0100760 * trigger a wmi notification and we need to wait
761 * this call to finish before being able to call
762 * any wmi method
763 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100764 mutex_lock(&asus->wmi_lock);
Corentin Charya7ce3f02011-02-26 10:20:33 +0100765 ret = asus_rfkill_set(data, blocked);
Corentin Charye12e6d92011-02-26 10:20:31 +0100766 mutex_unlock(&asus->wmi_lock);
Corentin Chary279f8f92011-02-06 13:28:29 +0100767 return ret;
768}
769
Corentin Charye12e6d92011-02-26 10:20:31 +0100770static const struct rfkill_ops asus_rfkill_wlan_ops = {
771 .set_block = asus_rfkill_wlan_set,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100772 .query = asus_rfkill_query,
Corentin Chary279f8f92011-02-06 13:28:29 +0100773};
774
Corentin Charye12e6d92011-02-26 10:20:31 +0100775static const struct rfkill_ops asus_rfkill_ops = {
776 .set_block = asus_rfkill_set,
777 .query = asus_rfkill_query,
Corentin Charyba48fdb2010-11-29 08:14:07 +0100778};
779
Corentin Charye12e6d92011-02-26 10:20:31 +0100780static int asus_new_rfkill(struct asus_wmi *asus,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100781 struct asus_rfkill *arfkill,
Corentin Charye12e6d92011-02-26 10:20:31 +0100782 const char *name, enum rfkill_type type, int dev_id)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100783{
Corentin Chary1d070f892011-02-26 10:20:36 +0100784 int result = asus_wmi_get_devstate_simple(asus, dev_id);
Corentin Charya7ce3f02011-02-26 10:20:33 +0100785 struct rfkill **rfkill = &arfkill->rfkill;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100786
Corentin Chary5c956382011-02-06 13:28:31 +0100787 if (result < 0)
788 return result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100789
Corentin Charya7ce3f02011-02-26 10:20:33 +0100790 arfkill->dev_id = dev_id;
791 arfkill->asus = asus;
792
AceLan Kaoc87992d2012-03-20 09:53:08 +0100793 if (dev_id == ASUS_WMI_DEVID_WLAN &&
794 asus->driver->quirks->hotplug_wireless)
Corentin Charye12e6d92011-02-26 10:20:31 +0100795 *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100796 &asus_rfkill_wlan_ops, arfkill);
Corentin Chary279f8f92011-02-06 13:28:29 +0100797 else
Corentin Charye12e6d92011-02-26 10:20:31 +0100798 *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
Corentin Charya7ce3f02011-02-26 10:20:33 +0100799 &asus_rfkill_ops, arfkill);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100800
801 if (!*rfkill)
802 return -EINVAL;
803
Corentin Chary5c956382011-02-06 13:28:31 +0100804 rfkill_init_sw_state(*rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100805 result = rfkill_register(*rfkill);
806 if (result) {
807 rfkill_destroy(*rfkill);
808 *rfkill = NULL;
809 return result;
810 }
811 return 0;
812}
813
Corentin Charye12e6d92011-02-26 10:20:31 +0100814static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100815{
Corentin Charye12e6d92011-02-26 10:20:31 +0100816 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
817 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
818 asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
Corentin Charya7ce3f02011-02-26 10:20:33 +0100819 if (asus->wlan.rfkill) {
820 rfkill_unregister(asus->wlan.rfkill);
821 rfkill_destroy(asus->wlan.rfkill);
822 asus->wlan.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100823 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100824 /*
825 * Refresh pci hotplug in case the rfkill state was changed after
Corentin Charye12e6d92011-02-26 10:20:31 +0100826 * asus_unregister_rfkill_notifier()
Corentin Charyafa7c882011-02-06 13:28:28 +0100827 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100828 asus_rfkill_hotplug(asus);
829 if (asus->hotplug_slot)
830 pci_hp_deregister(asus->hotplug_slot);
831 if (asus->hotplug_workqueue)
832 destroy_workqueue(asus->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +0100833
Corentin Charya7ce3f02011-02-26 10:20:33 +0100834 if (asus->bluetooth.rfkill) {
835 rfkill_unregister(asus->bluetooth.rfkill);
836 rfkill_destroy(asus->bluetooth.rfkill);
837 asus->bluetooth.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100838 }
Corentin Charya7ce3f02011-02-26 10:20:33 +0100839 if (asus->wimax.rfkill) {
840 rfkill_unregister(asus->wimax.rfkill);
841 rfkill_destroy(asus->wimax.rfkill);
842 asus->wimax.rfkill = NULL;
Corentin Chary2e9e1592011-02-06 13:28:37 +0100843 }
Corentin Charya7ce3f02011-02-26 10:20:33 +0100844 if (asus->wwan3g.rfkill) {
845 rfkill_unregister(asus->wwan3g.rfkill);
846 rfkill_destroy(asus->wwan3g.rfkill);
847 asus->wwan3g.rfkill = NULL;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100848 }
Corentin Chary43be8bd2011-07-01 11:34:40 +0200849 if (asus->gps.rfkill) {
850 rfkill_unregister(asus->gps.rfkill);
851 rfkill_destroy(asus->gps.rfkill);
852 asus->gps.rfkill = NULL;
853 }
Corentin Charya912d322011-07-01 11:34:41 +0200854 if (asus->uwb.rfkill) {
855 rfkill_unregister(asus->uwb.rfkill);
856 rfkill_destroy(asus->uwb.rfkill);
857 asus->uwb.rfkill = NULL;
858 }
Corentin Charyba48fdb2010-11-29 08:14:07 +0100859}
860
Corentin Charye12e6d92011-02-26 10:20:31 +0100861static int asus_wmi_rfkill_init(struct asus_wmi *asus)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100862{
863 int result = 0;
864
Corentin Charye12e6d92011-02-26 10:20:31 +0100865 mutex_init(&asus->hotplug_lock);
866 mutex_init(&asus->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100867
Corentin Charya7ce3f02011-02-26 10:20:33 +0100868 result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
869 RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100870
871 if (result && result != -ENODEV)
872 goto exit;
873
Corentin Charya7ce3f02011-02-26 10:20:33 +0100874 result = asus_new_rfkill(asus, &asus->bluetooth,
Corentin Charye12e6d92011-02-26 10:20:31 +0100875 "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
876 ASUS_WMI_DEVID_BLUETOOTH);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100877
878 if (result && result != -ENODEV)
879 goto exit;
880
Corentin Charya7ce3f02011-02-26 10:20:33 +0100881 result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
882 RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
Corentin Chary2e9e1592011-02-06 13:28:37 +0100883
884 if (result && result != -ENODEV)
885 goto exit;
886
Corentin Charya7ce3f02011-02-26 10:20:33 +0100887 result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
888 RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100889
890 if (result && result != -ENODEV)
891 goto exit;
892
Corentin Chary43be8bd2011-07-01 11:34:40 +0200893 result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
894 RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
895
896 if (result && result != -ENODEV)
897 goto exit;
898
Corentin Charya912d322011-07-01 11:34:41 +0200899 result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
900 RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
901
902 if (result && result != -ENODEV)
903 goto exit;
904
AceLan Kaoc87992d2012-03-20 09:53:08 +0100905 if (!asus->driver->quirks->hotplug_wireless)
Corentin Charyc14d4b82011-02-06 13:28:40 +0100906 goto exit;
907
Corentin Charye12e6d92011-02-26 10:20:31 +0100908 result = asus_setup_pci_hotplug(asus);
Corentin Charyafa7c882011-02-06 13:28:28 +0100909 /*
910 * If we get -EBUSY then something else is handling the PCI hotplug -
911 * don't fail in this case
912 */
913 if (result == -EBUSY)
914 result = 0;
915
Corentin Charye12e6d92011-02-26 10:20:31 +0100916 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
917 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
918 asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
Corentin Charyafa7c882011-02-06 13:28:28 +0100919 /*
920 * Refresh pci hotplug in case the rfkill state was changed during
921 * setup.
922 */
Corentin Charye12e6d92011-02-26 10:20:31 +0100923 asus_rfkill_hotplug(asus);
Corentin Charyafa7c882011-02-06 13:28:28 +0100924
Corentin Charyba48fdb2010-11-29 08:14:07 +0100925exit:
926 if (result && result != -ENODEV)
Corentin Charye12e6d92011-02-26 10:20:31 +0100927 asus_wmi_rfkill_exit(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100928
929 if (result == -ENODEV)
930 result = 0;
931
932 return result;
933}
934
935/*
Corentin Charye07babd2011-02-26 10:20:42 +0100936 * Hwmon device
937 */
938static ssize_t asus_hwmon_pwm1(struct device *dev,
Corentin Chary49979d02011-07-01 11:34:26 +0200939 struct device_attribute *attr,
940 char *buf)
Corentin Charye07babd2011-02-26 10:20:42 +0100941{
942 struct asus_wmi *asus = dev_get_drvdata(dev);
943 u32 value;
944 int err;
945
946 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
947
948 if (err < 0)
949 return err;
950
Corentin Chary49979d02011-07-01 11:34:26 +0200951 value &= 0xFF;
Corentin Charye07babd2011-02-26 10:20:42 +0100952
953 if (value == 1) /* Low Speed */
954 value = 85;
955 else if (value == 2)
956 value = 170;
957 else if (value == 3)
958 value = 255;
959 else if (value != 0) {
960 pr_err("Unknown fan speed %#x", value);
961 value = -1;
962 }
963
964 return sprintf(buf, "%d\n", value);
965}
966
Corentin Chary6118b8a2011-07-01 11:34:36 +0200967static ssize_t asus_hwmon_temp1(struct device *dev,
968 struct device_attribute *attr,
969 char *buf)
970{
971 struct asus_wmi *asus = dev_get_drvdata(dev);
972 u32 value;
973 int err;
974
975 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
976
977 if (err < 0)
978 return err;
979
980 value = KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
981
982 return sprintf(buf, "%d\n", value);
983}
984
Corentin Charye07babd2011-02-26 10:20:42 +0100985static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0);
Corentin Chary6118b8a2011-07-01 11:34:36 +0200986static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL, 0);
Corentin Charye07babd2011-02-26 10:20:42 +0100987
988static ssize_t
989show_name(struct device *dev, struct device_attribute *attr, char *buf)
990{
991 return sprintf(buf, "asus\n");
992}
993static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
994
995static struct attribute *hwmon_attributes[] = {
996 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Chary6118b8a2011-07-01 11:34:36 +0200997 &sensor_dev_attr_temp1_input.dev_attr.attr,
Corentin Charye07babd2011-02-26 10:20:42 +0100998 &sensor_dev_attr_name.dev_attr.attr,
999 NULL
1000};
1001
Al Viro587a1f12011-07-23 23:11:19 -04001002static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
Corentin Charye02431d2011-07-01 11:34:37 +02001003 struct attribute *attr, int idx)
Corentin Charye07babd2011-02-26 10:20:42 +01001004{
1005 struct device *dev = container_of(kobj, struct device, kobj);
1006 struct platform_device *pdev = to_platform_device(dev->parent);
1007 struct asus_wmi *asus = platform_get_drvdata(pdev);
1008 bool ok = true;
1009 int dev_id = -1;
1010 u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
1011
1012 if (attr == &sensor_dev_attr_pwm1.dev_attr.attr)
1013 dev_id = ASUS_WMI_DEVID_FAN_CTRL;
Corentin Charye02431d2011-07-01 11:34:37 +02001014 else if (attr == &sensor_dev_attr_temp1_input.dev_attr.attr)
1015 dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
Corentin Charye07babd2011-02-26 10:20:42 +01001016
1017 if (dev_id != -1) {
1018 int err = asus_wmi_get_devstate(asus, dev_id, &value);
1019
1020 if (err < 0)
Al Viroe772aed2011-07-23 20:59:40 -04001021 return 0; /* can't return negative here */
Corentin Charye07babd2011-02-26 10:20:42 +01001022 }
1023
1024 if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) {
1025 /*
1026 * We need to find a better way, probably using sfun,
1027 * bits or spec ...
1028 * Currently we disable it if:
1029 * - ASUS_WMI_UNSUPPORTED_METHOD is returned
1030 * - reverved bits are non-zero
1031 * - sfun and presence bit are not set
1032 */
Corentin Chary49979d02011-07-01 11:34:26 +02001033 if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
Corentin Charye07babd2011-02-26 10:20:42 +01001034 || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
1035 ok = false;
Corentin Charye02431d2011-07-01 11:34:37 +02001036 } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
1037 /* If value is zero, something is clearly wrong */
1038 if (value == 0)
1039 ok = false;
Corentin Charye07babd2011-02-26 10:20:42 +01001040 }
1041
1042 return ok ? attr->mode : 0;
1043}
1044
1045static struct attribute_group hwmon_attribute_group = {
1046 .is_visible = asus_hwmon_sysfs_is_visible,
1047 .attrs = hwmon_attributes
1048};
1049
1050static void asus_wmi_hwmon_exit(struct asus_wmi *asus)
1051{
1052 struct device *hwmon;
1053
1054 hwmon = asus->hwmon_device;
1055 if (!hwmon)
1056 return;
1057 sysfs_remove_group(&hwmon->kobj, &hwmon_attribute_group);
1058 hwmon_device_unregister(hwmon);
1059 asus->hwmon_device = NULL;
1060}
1061
1062static int asus_wmi_hwmon_init(struct asus_wmi *asus)
1063{
1064 struct device *hwmon;
1065 int result;
1066
1067 hwmon = hwmon_device_register(&asus->platform_device->dev);
1068 if (IS_ERR(hwmon)) {
1069 pr_err("Could not register asus hwmon device\n");
1070 return PTR_ERR(hwmon);
1071 }
Corentin Chary49979d02011-07-01 11:34:26 +02001072 dev_set_drvdata(hwmon, asus);
Corentin Charye07babd2011-02-26 10:20:42 +01001073 asus->hwmon_device = hwmon;
1074 result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group);
1075 if (result)
1076 asus_wmi_hwmon_exit(asus);
1077 return result;
1078}
1079
1080/*
Corentin Chary084fca62010-11-29 08:14:06 +01001081 * Backlight
1082 */
Corentin Chary1d070f892011-02-26 10:20:36 +01001083static int read_backlight_power(struct asus_wmi *asus)
Corentin Charyb7187262011-02-06 13:28:39 +01001084{
AceLan Kao6e0044b2012-03-20 09:53:09 +01001085 int ret;
1086 if (asus->driver->quirks->store_backlight_power)
1087 ret = !asus->driver->panel_power;
1088 else
1089 ret = asus_wmi_get_devstate_simple(asus,
1090 ASUS_WMI_DEVID_BACKLIGHT);
Corentin Charyb7187262011-02-06 13:28:39 +01001091
1092 if (ret < 0)
1093 return ret;
1094
1095 return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
1096}
1097
Corentin Chary8fbea012011-02-26 10:20:37 +01001098static int read_brightness_max(struct asus_wmi *asus)
1099{
1100 u32 retval;
1101 int err;
1102
1103 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
1104
1105 if (err < 0)
1106 return err;
1107
1108 retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;
1109 retval >>= 8;
1110
1111 if (!retval)
1112 return -ENODEV;
1113
1114 return retval;
1115}
1116
Yong Wang3d7b1652010-04-11 09:27:54 +08001117static int read_brightness(struct backlight_device *bd)
1118{
Corentin Chary1d070f892011-02-26 10:20:36 +01001119 struct asus_wmi *asus = bl_get_data(bd);
Dan Carpenter0986f252011-03-15 10:06:23 +03001120 u32 retval;
1121 int err;
Yong Wang3d7b1652010-04-11 09:27:54 +08001122
Corentin Chary1d070f892011-02-26 10:20:36 +01001123 err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +08001124
Corentin Charyd33da3b2011-02-26 10:20:35 +01001125 if (err < 0)
1126 return err;
1127
1128 return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
Yong Wang3d7b1652010-04-11 09:27:54 +08001129}
1130
AceLan Kaoc87992d2012-03-20 09:53:08 +01001131static u32 get_scalar_command(struct backlight_device *bd)
1132{
1133 struct asus_wmi *asus = bl_get_data(bd);
1134 u32 ctrl_param = 0;
1135
1136 if ((asus->driver->brightness < bd->props.brightness) ||
1137 bd->props.brightness == bd->props.max_brightness)
1138 ctrl_param = 0x00008001;
1139 else if ((asus->driver->brightness > bd->props.brightness) ||
1140 bd->props.brightness == 0)
1141 ctrl_param = 0x00008000;
1142
1143 asus->driver->brightness = bd->props.brightness;
1144
1145 return ctrl_param;
1146}
1147
Yong Wang3d7b1652010-04-11 09:27:54 +08001148static int update_bl_status(struct backlight_device *bd)
1149{
Corentin Chary1d070f892011-02-26 10:20:36 +01001150 struct asus_wmi *asus = bl_get_data(bd);
Corentin Charydfed65d2010-11-29 08:14:12 +01001151 u32 ctrl_param;
AceLan Kao6e0044b2012-03-20 09:53:09 +01001152 int power, err = 0;
Corentin Charyb7187262011-02-06 13:28:39 +01001153
Corentin Chary1d070f892011-02-26 10:20:36 +01001154 power = read_backlight_power(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01001155 if (power != -ENODEV && bd->props.power != power) {
1156 ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
Corentin Charyd33da3b2011-02-26 10:20:35 +01001157 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
1158 ctrl_param, NULL);
AceLan Kao6e0044b2012-03-20 09:53:09 +01001159 if (asus->driver->quirks->store_backlight_power)
1160 asus->driver->panel_power = bd->props.power;
AceLan Kao6e0044b2012-03-20 09:53:09 +01001161
Corentin Charyade28ab2012-03-20 09:53:14 +01001162 /* When using scalar brightness, updating the brightness
1163 * will mess with the backlight power */
1164 if (asus->driver->quirks->scalar_panel_brightness)
1165 return err;
Corentin Charyb7187262011-02-06 13:28:39 +01001166 }
Corentin Charyade28ab2012-03-20 09:53:14 +01001167
1168 if (asus->driver->quirks->scalar_panel_brightness)
1169 ctrl_param = get_scalar_command(bd);
1170 else
1171 ctrl_param = bd->props.brightness;
1172
1173 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
1174 ctrl_param, NULL);
1175
Corentin Chary8fbea012011-02-26 10:20:37 +01001176 return err;
Yong Wang3d7b1652010-04-11 09:27:54 +08001177}
1178
Corentin Charye12e6d92011-02-26 10:20:31 +01001179static const struct backlight_ops asus_wmi_bl_ops = {
Yong Wang3d7b1652010-04-11 09:27:54 +08001180 .get_brightness = read_brightness,
1181 .update_status = update_bl_status,
1182};
1183
Corentin Charye12e6d92011-02-26 10:20:31 +01001184static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)
Yong Wang3d7b1652010-04-11 09:27:54 +08001185{
Corentin Charye12e6d92011-02-26 10:20:31 +01001186 struct backlight_device *bd = asus->backlight_device;
Yong Wang3d7b1652010-04-11 09:27:54 +08001187 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +02001188 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +08001189
1190 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
1191 new = code - NOTIFY_BRNUP_MIN + 1;
1192 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
1193 new = code - NOTIFY_BRNDOWN_MIN;
1194
1195 bd->props.brightness = new;
1196 backlight_update_status(bd);
1197 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
1198
1199 return old;
1200}
1201
Corentin Charye12e6d92011-02-26 10:20:31 +01001202static int asus_wmi_backlight_init(struct asus_wmi *asus)
Yong Wang3d7b1652010-04-11 09:27:54 +08001203{
1204 struct backlight_device *bd;
1205 struct backlight_properties props;
Corentin Charyb7187262011-02-06 13:28:39 +01001206 int max;
1207 int power;
1208
Corentin Chary8fbea012011-02-26 10:20:37 +01001209 max = read_brightness_max(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01001210
Corentin Charyb7187262011-02-06 13:28:39 +01001211 if (max == -ENODEV)
1212 max = 0;
Corentin Chary8fbea012011-02-26 10:20:37 +01001213 else if (max < 0)
1214 return max;
1215
1216 power = read_backlight_power(asus);
1217
Corentin Charyb7187262011-02-06 13:28:39 +01001218 if (power == -ENODEV)
1219 power = FB_BLANK_UNBLANK;
Corentin Chary8fbea012011-02-26 10:20:37 +01001220 else if (power < 0)
1221 return power;
Yong Wang3d7b1652010-04-11 09:27:54 +08001222
1223 memset(&props, 0, sizeof(struct backlight_properties));
Axel Lin60cfa092011-06-29 11:43:30 +08001224 props.type = BACKLIGHT_PLATFORM;
Corentin Charyb7187262011-02-06 13:28:39 +01001225 props.max_brightness = max;
Corentin Charye12e6d92011-02-26 10:20:31 +01001226 bd = backlight_device_register(asus->driver->name,
1227 &asus->platform_device->dev, asus,
1228 &asus_wmi_bl_ops, &props);
Yong Wang3d7b1652010-04-11 09:27:54 +08001229 if (IS_ERR(bd)) {
1230 pr_err("Could not register backlight device\n");
1231 return PTR_ERR(bd);
1232 }
1233
Corentin Charye12e6d92011-02-26 10:20:31 +01001234 asus->backlight_device = bd;
Yong Wang3d7b1652010-04-11 09:27:54 +08001235
AceLan Kao6e0044b2012-03-20 09:53:09 +01001236 if (asus->driver->quirks->store_backlight_power)
1237 asus->driver->panel_power = power;
1238
Yong Wang3d7b1652010-04-11 09:27:54 +08001239 bd->props.brightness = read_brightness(bd);
Corentin Charyb7187262011-02-06 13:28:39 +01001240 bd->props.power = power;
Yong Wang3d7b1652010-04-11 09:27:54 +08001241 backlight_update_status(bd);
1242
AceLan Kaoc87992d2012-03-20 09:53:08 +01001243 asus->driver->brightness = bd->props.brightness;
1244
Yong Wang3d7b1652010-04-11 09:27:54 +08001245 return 0;
1246}
1247
Corentin Charye12e6d92011-02-26 10:20:31 +01001248static void asus_wmi_backlight_exit(struct asus_wmi *asus)
Yong Wang3d7b1652010-04-11 09:27:54 +08001249{
Corentin Charye12e6d92011-02-26 10:20:31 +01001250 if (asus->backlight_device)
1251 backlight_device_unregister(asus->backlight_device);
Yong Wang3d7b1652010-04-11 09:27:54 +08001252
Corentin Charye12e6d92011-02-26 10:20:31 +01001253 asus->backlight_device = NULL;
Yong Wang3d7b1652010-04-11 09:27:54 +08001254}
1255
Corentin Charye12e6d92011-02-26 10:20:31 +01001256static void asus_wmi_notify(u32 value, void *context)
Yong Wang3d7b1652010-04-11 09:27:54 +08001257{
Corentin Charye12e6d92011-02-26 10:20:31 +01001258 struct asus_wmi *asus = context;
Yong Wang3d7b1652010-04-11 09:27:54 +08001259 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
1260 union acpi_object *obj;
1261 acpi_status status;
1262 int code;
1263 int orig_code;
Seth Forsheec4453f62011-07-01 11:34:27 +02001264 unsigned int key_value = 1;
1265 bool autorelease = 1;
Yong Wang3d7b1652010-04-11 09:27:54 +08001266
1267 status = wmi_get_event_data(value, &response);
1268 if (status != AE_OK) {
1269 pr_err("bad event status 0x%x\n", status);
1270 return;
1271 }
1272
1273 obj = (union acpi_object *)response.pointer;
1274
Corentin Chary57ab7da2011-02-26 10:20:32 +01001275 if (!obj || obj->type != ACPI_TYPE_INTEGER)
1276 goto exit;
Yong Wang3d7b1652010-04-11 09:27:54 +08001277
Corentin Chary57ab7da2011-02-26 10:20:32 +01001278 code = obj->integer.value;
1279 orig_code = code;
Yong Wang3d7b1652010-04-11 09:27:54 +08001280
Seth Forsheec4453f62011-07-01 11:34:27 +02001281 if (asus->driver->key_filter) {
1282 asus->driver->key_filter(asus->driver, &code, &key_value,
1283 &autorelease);
1284 if (code == ASUS_WMI_KEY_IGNORE)
1285 goto exit;
1286 }
1287
Corentin Chary57ab7da2011-02-26 10:20:32 +01001288 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
1289 code = NOTIFY_BRNUP_MIN;
1290 else if (code >= NOTIFY_BRNDOWN_MIN &&
1291 code <= NOTIFY_BRNDOWN_MAX)
1292 code = NOTIFY_BRNDOWN_MIN;
Yong Wang3d7b1652010-04-11 09:27:54 +08001293
Corentin Chary57ab7da2011-02-26 10:20:32 +01001294 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
1295 if (!acpi_video_backlight_support())
1296 asus_wmi_backlight_notify(asus, orig_code);
Seth Forsheec4453f62011-07-01 11:34:27 +02001297 } else if (!sparse_keymap_report_event(asus->inputdev, code,
1298 key_value, autorelease))
Corentin Chary57ab7da2011-02-26 10:20:32 +01001299 pr_info("Unknown key %x pressed\n", code);
Yong Wang3d7b1652010-04-11 09:27:54 +08001300
Corentin Chary57ab7da2011-02-26 10:20:32 +01001301exit:
Yong Wang3d7b1652010-04-11 09:27:54 +08001302 kfree(obj);
1303}
1304
Corentin Chary9e1565b2011-02-06 13:28:36 +01001305/*
1306 * Sys helpers
1307 */
1308static int parse_arg(const char *buf, unsigned long count, int *val)
1309{
1310 if (!count)
1311 return 0;
1312 if (sscanf(buf, "%i", val) != 1)
1313 return -EINVAL;
1314 return count;
1315}
1316
Corentin Chary1d070f892011-02-26 10:20:36 +01001317static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
1318 const char *buf, size_t count)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001319{
Corentin Chary9e1565b2011-02-06 13:28:36 +01001320 u32 retval;
Corentin Charyd33da3b2011-02-26 10:20:35 +01001321 int rv, err, value;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001322
Corentin Chary1d070f892011-02-26 10:20:36 +01001323 value = asus_wmi_get_devstate_simple(asus, devid);
Corentin Charye12e6d92011-02-26 10:20:31 +01001324 if (value == -ENODEV) /* Check device presence */
Corentin Chary9e1565b2011-02-06 13:28:36 +01001325 return value;
1326
1327 rv = parse_arg(buf, count, &value);
Corentin Charyd33da3b2011-02-26 10:20:35 +01001328 err = asus_wmi_set_devstate(devid, value, &retval);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001329
Corentin Charyd33da3b2011-02-26 10:20:35 +01001330 if (err < 0)
1331 return err;
1332
Corentin Chary9e1565b2011-02-06 13:28:36 +01001333 return rv;
1334}
1335
Corentin Chary1d070f892011-02-26 10:20:36 +01001336static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001337{
Corentin Chary1d070f892011-02-26 10:20:36 +01001338 int value = asus_wmi_get_devstate_simple(asus, devid);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001339
1340 if (value < 0)
1341 return value;
1342
1343 return sprintf(buf, "%d\n", value);
1344}
1345
Corentin Charye12e6d92011-02-26 10:20:31 +01001346#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001347 static ssize_t show_##_name(struct device *dev, \
1348 struct device_attribute *attr, \
1349 char *buf) \
1350 { \
Corentin Chary1d070f892011-02-26 10:20:36 +01001351 struct asus_wmi *asus = dev_get_drvdata(dev); \
1352 \
1353 return show_sys_wmi(asus, _cm, buf); \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001354 } \
1355 static ssize_t store_##_name(struct device *dev, \
1356 struct device_attribute *attr, \
1357 const char *buf, size_t count) \
1358 { \
Corentin Chary1d070f892011-02-26 10:20:36 +01001359 struct asus_wmi *asus = dev_get_drvdata(dev); \
1360 \
1361 return store_sys_wmi(asus, _cm, buf, count); \
Corentin Chary9e1565b2011-02-06 13:28:36 +01001362 } \
1363 static struct device_attribute dev_attr_##_name = { \
1364 .attr = { \
1365 .name = __stringify(_name), \
1366 .mode = _mode }, \
1367 .show = show_##_name, \
1368 .store = store_##_name, \
1369 }
1370
Corentin Charye12e6d92011-02-26 10:20:31 +01001371ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
1372ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
1373ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
AceLan Kaoc0b91b62012-06-13 09:32:07 +02001374ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001375
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -07001376static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
1377 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001378{
Corentin Chary3df5fda2011-07-01 11:34:38 +02001379 int value, rv;
Chris Bagwell7f80d732010-10-11 18:47:18 -05001380
1381 if (!count || sscanf(buf, "%i", &value) != 1)
1382 return -EINVAL;
1383 if (value < 0 || value > 2)
1384 return -EINVAL;
1385
Corentin Chary3df5fda2011-07-01 11:34:38 +02001386 rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
1387 if (rv < 0)
1388 return rv;
1389
1390 return count;
Chris Bagwell7f80d732010-10-11 18:47:18 -05001391}
1392
1393static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
1394
Corentin Chary4e37b422010-11-29 08:14:08 +01001395static struct attribute *platform_attributes[] = {
1396 &dev_attr_cpufv.attr,
Corentin Chary9e1565b2011-02-06 13:28:36 +01001397 &dev_attr_camera.attr,
1398 &dev_attr_cardr.attr,
Corentin Chary4615bb62011-02-06 13:28:42 +01001399 &dev_attr_touchpad.attr,
AceLan Kaoc0b91b62012-06-13 09:32:07 +02001400 &dev_attr_lid_resume.attr,
Corentin Chary4e37b422010-11-29 08:14:08 +01001401 NULL
1402};
1403
Al Viro587a1f12011-07-23 23:11:19 -04001404static umode_t asus_sysfs_is_visible(struct kobject *kobj,
Corentin Charye12e6d92011-02-26 10:20:31 +01001405 struct attribute *attr, int idx)
Corentin Chary9e1565b2011-02-06 13:28:36 +01001406{
Corentin Chary1d070f892011-02-26 10:20:36 +01001407 struct device *dev = container_of(kobj, struct device, kobj);
1408 struct platform_device *pdev = to_platform_device(dev);
1409 struct asus_wmi *asus = platform_get_drvdata(pdev);
1410 bool ok = true;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001411 int devid = -1;
1412
1413 if (attr == &dev_attr_camera.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01001414 devid = ASUS_WMI_DEVID_CAMERA;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001415 else if (attr == &dev_attr_cardr.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01001416 devid = ASUS_WMI_DEVID_CARDREADER;
Corentin Chary4615bb62011-02-06 13:28:42 +01001417 else if (attr == &dev_attr_touchpad.attr)
Corentin Charye12e6d92011-02-26 10:20:31 +01001418 devid = ASUS_WMI_DEVID_TOUCHPAD;
AceLan Kaoc0b91b62012-06-13 09:32:07 +02001419 else if (attr == &dev_attr_lid_resume.attr)
1420 devid = ASUS_WMI_DEVID_LID_RESUME;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001421
1422 if (devid != -1)
Corentin Chary1d070f892011-02-26 10:20:36 +01001423 ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001424
Corentin Chary1d070f892011-02-26 10:20:36 +01001425 return ok ? attr->mode : 0;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001426}
1427
Corentin Chary4e37b422010-11-29 08:14:08 +01001428static struct attribute_group platform_attribute_group = {
Corentin Charye12e6d92011-02-26 10:20:31 +01001429 .is_visible = asus_sysfs_is_visible,
1430 .attrs = platform_attributes
Corentin Chary4e37b422010-11-29 08:14:08 +01001431};
1432
Corentin Charye12e6d92011-02-26 10:20:31 +01001433static void asus_wmi_sysfs_exit(struct platform_device *device)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001434{
Corentin Chary4e37b422010-11-29 08:14:08 +01001435 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001436}
1437
Corentin Charye12e6d92011-02-26 10:20:31 +01001438static int asus_wmi_sysfs_init(struct platform_device *device)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001439{
Corentin Chary4e37b422010-11-29 08:14:08 +01001440 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001441}
1442
Corentin Chary27c136c2010-11-29 08:14:05 +01001443/*
1444 * Platform device
1445 */
Joe Perches39ddf3b2011-03-29 15:21:32 -07001446static int asus_wmi_platform_init(struct asus_wmi *asus)
Corentin Chary27c136c2010-11-29 08:14:05 +01001447{
Corentin Chary46dbca82011-02-26 10:20:38 +01001448 int rv;
1449
1450 /* INIT enable hotkeys on some models */
1451 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
1452 pr_info("Initialization: %#x", rv);
1453
1454 /* We don't know yet what to do with this version... */
1455 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
Corentin Chary57d5c8e2011-07-01 11:34:30 +02001456 pr_info("BIOS WMI version: %d.%d", rv >> 16, rv & 0xFF);
Corentin Chary46dbca82011-02-26 10:20:38 +01001457 asus->spec = rv;
1458 }
1459
1460 /*
1461 * The SFUN method probably allows the original driver to get the list
1462 * of features supported by a given model. For now, 0x0100 or 0x0800
1463 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
1464 * The significance of others is yet to be found.
1465 */
1466 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
1467 pr_info("SFUN value: %#x", rv);
1468 asus->sfun = rv;
1469 }
1470
Corentin Chary1d070f892011-02-26 10:20:36 +01001471 /*
1472 * Eee PC and Notebooks seems to have different method_id for DSTS,
1473 * but it may also be related to the BIOS's SPEC.
1474 * Note, on most Eeepc, there is no way to check if a method exist
1475 * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
1476 * but once again, SPEC may probably be used for that kind of things.
1477 */
1478 if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
1479 asus->dsts_id = ASUS_WMI_METHODID_DSTS;
Alex Hung63a78bb2012-06-20 11:47:35 +08001480 else
Corentin Chary1d070f892011-02-26 10:20:36 +01001481 asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
1482
Corentin Charyfddbfed2011-07-01 11:34:39 +02001483 /* CWAP allow to define the behavior of the Fn+F2 key,
1484 * this method doesn't seems to be present on Eee PCs */
Corentin Chary6a2bccc2012-03-20 09:53:10 +01001485 if (asus->driver->quirks->wapf >= 0)
Corentin Charyfddbfed2011-07-01 11:34:39 +02001486 asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
Corentin Chary6a2bccc2012-03-20 09:53:10 +01001487 asus->driver->quirks->wapf, NULL);
Corentin Charyfddbfed2011-07-01 11:34:39 +02001488
Corentin Charye12e6d92011-02-26 10:20:31 +01001489 return asus_wmi_sysfs_init(asus->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +01001490}
1491
Corentin Charye12e6d92011-02-26 10:20:31 +01001492static void asus_wmi_platform_exit(struct asus_wmi *asus)
Corentin Chary27c136c2010-11-29 08:14:05 +01001493{
Corentin Charye12e6d92011-02-26 10:20:31 +01001494 asus_wmi_sysfs_exit(asus->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +01001495}
1496
1497/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001498 * debugfs
1499 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001500struct asus_wmi_debugfs_node {
1501 struct asus_wmi *asus;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001502 char *name;
Corentin Charye12e6d92011-02-26 10:20:31 +01001503 int (*show) (struct seq_file *m, void *data);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001504};
1505
1506static int show_dsts(struct seq_file *m, void *data)
1507{
Corentin Charye12e6d92011-02-26 10:20:31 +01001508 struct asus_wmi *asus = m->private;
Corentin Charyd33da3b2011-02-26 10:20:35 +01001509 int err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001510 u32 retval = -1;
1511
Corentin Chary1d070f892011-02-26 10:20:36 +01001512 err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001513
Corentin Charyd33da3b2011-02-26 10:20:35 +01001514 if (err < 0)
1515 return err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001516
Corentin Charyef343492011-02-26 10:20:39 +01001517 seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001518
1519 return 0;
1520}
1521
1522static int show_devs(struct seq_file *m, void *data)
1523{
Corentin Charye12e6d92011-02-26 10:20:31 +01001524 struct asus_wmi *asus = m->private;
Corentin Charyd33da3b2011-02-26 10:20:35 +01001525 int err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001526 u32 retval = -1;
1527
Corentin Charyd33da3b2011-02-26 10:20:35 +01001528 err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,
1529 &retval);
1530
1531 if (err < 0)
1532 return err;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001533
Corentin Charyef343492011-02-26 10:20:39 +01001534 seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id,
Corentin Charye12e6d92011-02-26 10:20:31 +01001535 asus->debug.ctrl_param, retval);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001536
1537 return 0;
1538}
1539
Corentin Charyef343492011-02-26 10:20:39 +01001540static int show_call(struct seq_file *m, void *data)
1541{
1542 struct asus_wmi *asus = m->private;
1543 struct bios_args args = {
1544 .arg0 = asus->debug.dev_id,
1545 .arg1 = asus->debug.ctrl_param,
1546 };
1547 struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
1548 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
1549 union acpi_object *obj;
1550 acpi_status status;
1551
1552 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
1553 1, asus->debug.method_id,
1554 &input, &output);
1555
1556 if (ACPI_FAILURE(status))
1557 return -EIO;
1558
1559 obj = (union acpi_object *)output.pointer;
1560 if (obj && obj->type == ACPI_TYPE_INTEGER)
1561 seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id,
1562 asus->debug.dev_id, asus->debug.ctrl_param,
1563 (u32) obj->integer.value);
1564 else
1565 seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id,
1566 asus->debug.dev_id, asus->debug.ctrl_param,
Dan Carpentera1d60862011-03-15 10:07:37 +03001567 obj ? obj->type : -1);
Corentin Charyef343492011-02-26 10:20:39 +01001568
1569 kfree(obj);
1570
1571 return 0;
1572}
1573
Corentin Charye12e6d92011-02-26 10:20:31 +01001574static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {
1575 {NULL, "devs", show_devs},
1576 {NULL, "dsts", show_dsts},
Corentin Charyef343492011-02-26 10:20:39 +01001577 {NULL, "call", show_call},
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001578};
1579
Corentin Charye12e6d92011-02-26 10:20:31 +01001580static int asus_wmi_debugfs_open(struct inode *inode, struct file *file)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001581{
Corentin Charye12e6d92011-02-26 10:20:31 +01001582 struct asus_wmi_debugfs_node *node = inode->i_private;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001583
Corentin Charye12e6d92011-02-26 10:20:31 +01001584 return single_open(file, node->show, node->asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001585}
1586
Corentin Charye12e6d92011-02-26 10:20:31 +01001587static const struct file_operations asus_wmi_debugfs_io_ops = {
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001588 .owner = THIS_MODULE,
Corentin Charye12e6d92011-02-26 10:20:31 +01001589 .open = asus_wmi_debugfs_open,
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001590 .read = seq_read,
1591 .llseek = seq_lseek,
1592 .release = single_release,
1593};
1594
Corentin Charye12e6d92011-02-26 10:20:31 +01001595static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001596{
Corentin Charye12e6d92011-02-26 10:20:31 +01001597 debugfs_remove_recursive(asus->debug.root);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001598}
1599
Corentin Charye12e6d92011-02-26 10:20:31 +01001600static int asus_wmi_debugfs_init(struct asus_wmi *asus)
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001601{
1602 struct dentry *dent;
1603 int i;
1604
Corentin Charye12e6d92011-02-26 10:20:31 +01001605 asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
1606 if (!asus->debug.root) {
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001607 pr_err("failed to create debugfs directory");
1608 goto error_debugfs;
1609 }
1610
Corentin Charyef343492011-02-26 10:20:39 +01001611 dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR,
1612 asus->debug.root, &asus->debug.method_id);
1613 if (!dent)
1614 goto error_debugfs;
1615
Corentin Charye12e6d92011-02-26 10:20:31 +01001616 dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR,
1617 asus->debug.root, &asus->debug.dev_id);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001618 if (!dent)
1619 goto error_debugfs;
1620
Corentin Charye12e6d92011-02-26 10:20:31 +01001621 dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR,
1622 asus->debug.root, &asus->debug.ctrl_param);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001623 if (!dent)
1624 goto error_debugfs;
1625
Corentin Charye12e6d92011-02-26 10:20:31 +01001626 for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
1627 struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001628
Corentin Charye12e6d92011-02-26 10:20:31 +01001629 node->asus = asus;
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001630 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
Corentin Charye12e6d92011-02-26 10:20:31 +01001631 asus->debug.root, node,
1632 &asus_wmi_debugfs_io_ops);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001633 if (!dent) {
1634 pr_err("failed to create debug file: %s\n", node->name);
1635 goto error_debugfs;
1636 }
1637 }
1638
1639 return 0;
1640
1641error_debugfs:
Corentin Charye12e6d92011-02-26 10:20:31 +01001642 asus_wmi_debugfs_exit(asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001643 return -ENOMEM;
1644}
1645
1646/*
Corentin Chary27c136c2010-11-29 08:14:05 +01001647 * WMI Driver
1648 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001649static int asus_wmi_add(struct platform_device *pdev)
Corentin Charyafa7c882011-02-06 13:28:28 +01001650{
Corentin Charye12e6d92011-02-26 10:20:31 +01001651 struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
1652 struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
1653 struct asus_wmi *asus;
Yong Wangee027e42010-03-21 10:26:34 +08001654 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01001655 int err;
Yong Wangee027e42010-03-21 10:26:34 +08001656
Corentin Charye12e6d92011-02-26 10:20:31 +01001657 asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
1658 if (!asus)
Corentin Charya04ce292011-02-06 13:28:33 +01001659 return -ENOMEM;
1660
Corentin Charye12e6d92011-02-26 10:20:31 +01001661 asus->driver = wdrv;
1662 asus->platform_device = pdev;
1663 wdrv->platform_device = pdev;
1664 platform_set_drvdata(asus->platform_device, asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01001665
AceLan Kaoc87992d2012-03-20 09:53:08 +01001666 if (wdrv->detect_quirks)
1667 wdrv->detect_quirks(asus->driver);
Corentin Charyafa7c882011-02-06 13:28:28 +01001668
Corentin Charye12e6d92011-02-26 10:20:31 +01001669 err = asus_wmi_platform_init(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01001670 if (err)
1671 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08001672
Corentin Charye12e6d92011-02-26 10:20:31 +01001673 err = asus_wmi_input_init(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08001674 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001675 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08001676
Corentin Charye07babd2011-02-26 10:20:42 +01001677 err = asus_wmi_hwmon_init(asus);
1678 if (err)
1679 goto fail_hwmon;
1680
Corentin Charye12e6d92011-02-26 10:20:31 +01001681 err = asus_wmi_led_init(asus);
Corentin Chary084fca62010-11-29 08:14:06 +01001682 if (err)
1683 goto fail_leds;
1684
Corentin Charye12e6d92011-02-26 10:20:31 +01001685 err = asus_wmi_rfkill_init(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001686 if (err)
1687 goto fail_rfkill;
1688
AceLan Kao272c77d2012-06-13 09:32:06 +02001689 if (asus->driver->quirks->wmi_backlight_power)
1690 acpi_video_dmi_promote_vendor();
Yong Wang3d7b1652010-04-11 09:27:54 +08001691 if (!acpi_video_backlight_support()) {
AceLan Kao272c77d2012-06-13 09:32:06 +02001692#ifdef CONFIG_ACPI_VIDEO
1693 pr_info("Disabling ACPI video driver\n");
1694 acpi_video_unregister();
1695#endif
Corentin Charye12e6d92011-02-26 10:20:31 +01001696 err = asus_wmi_backlight_init(asus);
Corentin Charyb7187262011-02-06 13:28:39 +01001697 if (err && err != -ENODEV)
Corentin Chary27c136c2010-11-29 08:14:05 +01001698 goto fail_backlight;
Yong Wang3d7b1652010-04-11 09:27:54 +08001699 } else
1700 pr_info("Backlight controlled by ACPI video driver\n");
Yong Wang45f2c692010-04-11 09:27:19 +08001701
Corentin Charye12e6d92011-02-26 10:20:31 +01001702 status = wmi_install_notify_handler(asus->driver->event_guid,
1703 asus_wmi_notify, asus);
Yong Wang45f2c692010-04-11 09:27:19 +08001704 if (ACPI_FAILURE(status)) {
Corentin Charye12e6d92011-02-26 10:20:31 +01001705 pr_err("Unable to register notify handler - %d\n", status);
Yong Wang45f2c692010-04-11 09:27:19 +08001706 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01001707 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08001708 }
1709
Corentin Charye12e6d92011-02-26 10:20:31 +01001710 err = asus_wmi_debugfs_init(asus);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001711 if (err)
1712 goto fail_debugfs;
1713
Corentin Charya04ce292011-02-06 13:28:33 +01001714 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08001715
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001716fail_debugfs:
Corentin Charye12e6d92011-02-26 10:20:31 +01001717 wmi_remove_notify_handler(asus->driver->event_guid);
Corentin Chary27c136c2010-11-29 08:14:05 +01001718fail_wmi_handler:
Corentin Charye12e6d92011-02-26 10:20:31 +01001719 asus_wmi_backlight_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01001720fail_backlight:
Corentin Charye12e6d92011-02-26 10:20:31 +01001721 asus_wmi_rfkill_exit(asus);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001722fail_rfkill:
Corentin Charye12e6d92011-02-26 10:20:31 +01001723 asus_wmi_led_exit(asus);
Corentin Chary084fca62010-11-29 08:14:06 +01001724fail_leds:
Corentin Charye07babd2011-02-26 10:20:42 +01001725 asus_wmi_hwmon_exit(asus);
1726fail_hwmon:
Corentin Charye12e6d92011-02-26 10:20:31 +01001727 asus_wmi_input_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01001728fail_input:
Corentin Charye12e6d92011-02-26 10:20:31 +01001729 asus_wmi_platform_exit(asus);
Corentin Chary27c136c2010-11-29 08:14:05 +01001730fail_platform:
Corentin Charye12e6d92011-02-26 10:20:31 +01001731 kfree(asus);
Corentin Charya04ce292011-02-06 13:28:33 +01001732 return err;
Yong Wang45f2c692010-04-11 09:27:19 +08001733}
1734
Corentin Charye12e6d92011-02-26 10:20:31 +01001735static int asus_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08001736{
Corentin Charye12e6d92011-02-26 10:20:31 +01001737 struct asus_wmi *asus;
Yong Wang45f2c692010-04-11 09:27:19 +08001738
Corentin Charye12e6d92011-02-26 10:20:31 +01001739 asus = platform_get_drvdata(device);
1740 wmi_remove_notify_handler(asus->driver->event_guid);
1741 asus_wmi_backlight_exit(asus);
1742 asus_wmi_input_exit(asus);
Corentin Charye07babd2011-02-26 10:20:42 +01001743 asus_wmi_hwmon_exit(asus);
Corentin Charye12e6d92011-02-26 10:20:31 +01001744 asus_wmi_led_exit(asus);
1745 asus_wmi_rfkill_exit(asus);
1746 asus_wmi_debugfs_exit(asus);
1747 asus_wmi_platform_exit(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08001748
Corentin Charye12e6d92011-02-26 10:20:31 +01001749 kfree(asus);
Yong Wang45f2c692010-04-11 09:27:19 +08001750 return 0;
1751}
1752
Corentin Chary0773d7f2011-02-06 13:28:32 +01001753/*
1754 * Platform driver - hibernate/resume callbacks
1755 */
Corentin Charye12e6d92011-02-26 10:20:31 +01001756static int asus_hotk_thaw(struct device *device)
Corentin Chary0773d7f2011-02-06 13:28:32 +01001757{
Corentin Charye12e6d92011-02-26 10:20:31 +01001758 struct asus_wmi *asus = dev_get_drvdata(device);
Corentin Chary0773d7f2011-02-06 13:28:32 +01001759
Corentin Charya7ce3f02011-02-26 10:20:33 +01001760 if (asus->wlan.rfkill) {
Corentin Chary0773d7f2011-02-06 13:28:32 +01001761 bool wlan;
1762
1763 /*
1764 * Work around bios bug - acpi _PTS turns off the wireless led
1765 * during suspend. Normally it restores it on resume, but
1766 * we should kick it ourselves in case hibernation is aborted.
1767 */
Corentin Chary1d070f892011-02-26 10:20:36 +01001768 wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
Corentin Charye12e6d92011-02-26 10:20:31 +01001769 asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
Corentin Chary0773d7f2011-02-06 13:28:32 +01001770 }
1771
1772 return 0;
1773}
1774
Corentin Charye12e6d92011-02-26 10:20:31 +01001775static int asus_hotk_restore(struct device *device)
Corentin Chary0773d7f2011-02-06 13:28:32 +01001776{
Corentin Charye12e6d92011-02-26 10:20:31 +01001777 struct asus_wmi *asus = dev_get_drvdata(device);
Corentin Chary0773d7f2011-02-06 13:28:32 +01001778 int bl;
1779
1780 /* Refresh both wlan rfkill state and pci hotplug */
Corentin Charya7ce3f02011-02-26 10:20:33 +01001781 if (asus->wlan.rfkill)
Corentin Charye12e6d92011-02-26 10:20:31 +01001782 asus_rfkill_hotplug(asus);
Corentin Chary0773d7f2011-02-06 13:28:32 +01001783
Corentin Charya7ce3f02011-02-26 10:20:33 +01001784 if (asus->bluetooth.rfkill) {
Corentin Chary1d070f892011-02-26 10:20:36 +01001785 bl = !asus_wmi_get_devstate_simple(asus,
1786 ASUS_WMI_DEVID_BLUETOOTH);
Corentin Charya7ce3f02011-02-26 10:20:33 +01001787 rfkill_set_sw_state(asus->bluetooth.rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01001788 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01001789 if (asus->wimax.rfkill) {
Corentin Chary1d070f892011-02-26 10:20:36 +01001790 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);
Corentin Charya7ce3f02011-02-26 10:20:33 +01001791 rfkill_set_sw_state(asus->wimax.rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01001792 }
Corentin Charya7ce3f02011-02-26 10:20:33 +01001793 if (asus->wwan3g.rfkill) {
Corentin Chary1d070f892011-02-26 10:20:36 +01001794 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
Corentin Charya7ce3f02011-02-26 10:20:33 +01001795 rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
Corentin Chary0773d7f2011-02-06 13:28:32 +01001796 }
Corentin Chary43be8bd2011-07-01 11:34:40 +02001797 if (asus->gps.rfkill) {
1798 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
1799 rfkill_set_sw_state(asus->gps.rfkill, bl);
1800 }
Corentin Charya912d322011-07-01 11:34:41 +02001801 if (asus->uwb.rfkill) {
1802 bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
1803 rfkill_set_sw_state(asus->uwb.rfkill, bl);
1804 }
Corentin Chary0773d7f2011-02-06 13:28:32 +01001805
1806 return 0;
1807}
1808
Corentin Charye12e6d92011-02-26 10:20:31 +01001809static const struct dev_pm_ops asus_pm_ops = {
1810 .thaw = asus_hotk_thaw,
1811 .restore = asus_hotk_restore,
Corentin Chary0773d7f2011-02-06 13:28:32 +01001812};
1813
Corentin Charye12e6d92011-02-26 10:20:31 +01001814static int asus_wmi_probe(struct platform_device *pdev)
Corentin Charyd358cb52010-11-29 08:14:14 +01001815{
Corentin Charye12e6d92011-02-26 10:20:31 +01001816 struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
1817 struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
1818 int ret;
Corentin Charyd358cb52010-11-29 08:14:14 +01001819
Corentin Charye12e6d92011-02-26 10:20:31 +01001820 if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
Joe Perches5ad77dc2011-03-29 15:21:35 -07001821 pr_warn("Management GUID not found\n");
Yong Wangee027e42010-03-21 10:26:34 +08001822 return -ENODEV;
1823 }
1824
Corentin Charye12e6d92011-02-26 10:20:31 +01001825 if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
Joe Perches5ad77dc2011-03-29 15:21:35 -07001826 pr_warn("Event GUID not found\n");
Corentin Charyd358cb52010-11-29 08:14:14 +01001827 return -ENODEV;
1828 }
1829
Corentin Charye12e6d92011-02-26 10:20:31 +01001830 if (wdrv->probe) {
1831 ret = wdrv->probe(pdev);
1832 if (ret)
1833 return ret;
1834 }
1835
1836 return asus_wmi_add(pdev);
Corentin Charya04ce292011-02-06 13:28:33 +01001837}
Yong Wangee027e42010-03-21 10:26:34 +08001838
Corentin Charye12e6d92011-02-26 10:20:31 +01001839static bool used;
Yong Wangee027e42010-03-21 10:26:34 +08001840
Corentin Chary8fe8c252011-07-01 11:34:32 +02001841int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
Corentin Charya04ce292011-02-06 13:28:33 +01001842{
Corentin Charye12e6d92011-02-26 10:20:31 +01001843 struct platform_driver *platform_driver;
1844 struct platform_device *platform_device;
1845
1846 if (used)
1847 return -EBUSY;
1848
1849 platform_driver = &driver->platform_driver;
1850 platform_driver->remove = asus_wmi_remove;
1851 platform_driver->driver.owner = driver->owner;
1852 platform_driver->driver.name = driver->name;
1853 platform_driver->driver.pm = &asus_pm_ops;
1854
1855 platform_device = platform_create_bundle(platform_driver,
1856 asus_wmi_probe,
Corentin Charya04ce292011-02-06 13:28:33 +01001857 NULL, 0, NULL, 0);
1858 if (IS_ERR(platform_device))
1859 return PTR_ERR(platform_device);
Corentin Charye12e6d92011-02-26 10:20:31 +01001860
1861 used = true;
1862 return 0;
1863}
1864EXPORT_SYMBOL_GPL(asus_wmi_register_driver);
1865
1866void asus_wmi_unregister_driver(struct asus_wmi_driver *driver)
1867{
1868 platform_device_unregister(driver->platform_device);
1869 platform_driver_unregister(&driver->platform_driver);
1870 used = false;
1871}
1872EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
1873
1874static int __init asus_wmi_init(void)
1875{
1876 if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
1877 pr_info("Asus Management GUID not found");
1878 return -ENODEV;
1879 }
1880
1881 pr_info("ASUS WMI generic driver loaded");
Yong Wangee027e42010-03-21 10:26:34 +08001882 return 0;
1883}
1884
Corentin Charye12e6d92011-02-26 10:20:31 +01001885static void __exit asus_wmi_exit(void)
Yong Wangee027e42010-03-21 10:26:34 +08001886{
Corentin Charye12e6d92011-02-26 10:20:31 +01001887 pr_info("ASUS WMI generic driver unloaded");
Yong Wangee027e42010-03-21 10:26:34 +08001888}
1889
Corentin Charye12e6d92011-02-26 10:20:31 +01001890module_init(asus_wmi_init);
1891module_exit(asus_wmi_exit);