asus-wmi.c 37.1 KB
Newer Older
1
/*
2
 * Asus PC WMI hotkey driver
3 4
 *
 * Copyright(C) 2010 Intel Corporation.
C
Corentin Chary 已提交
5
 * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * Portions based on wistron_btns.c:
 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

27 28
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

29 30 31 32
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
T
Tejun Heo 已提交
33
#include <linux/slab.h>
34 35
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
Y
Yong Wang 已提交
36 37
#include <linux/fb.h>
#include <linux/backlight.h>
38
#include <linux/leds.h>
39
#include <linux/rfkill.h>
40 41
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
C
Corentin Chary 已提交
42 43
#include <linux/debugfs.h>
#include <linux/seq_file.h>
44
#include <linux/platform_device.h>
45 46 47
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

48
#include "asus-wmi.h"
49

50 51 52
MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>, "
	      "Yong Wang <yong.y.wang@intel.com>");
MODULE_DESCRIPTION("Asus Generic WMI Driver");
53 54
MODULE_LICENSE("GPL");

55 56
#define to_platform_driver(drv)					\
	(container_of((drv), struct platform_driver, driver))
57

58 59
#define to_asus_wmi_driver(pdrv)					\
	(container_of((pdrv), struct asus_wmi_driver, platform_driver))
60

61
#define ASUS_WMI_MGMT_GUID	"97845ED0-4E6D-11DE-8A39-0800200C9A66"
62

C
Corentin Chary 已提交
63 64 65 66
#define NOTIFY_BRNUP_MIN		0x11
#define NOTIFY_BRNUP_MAX		0x1f
#define NOTIFY_BRNDOWN_MIN		0x20
#define NOTIFY_BRNDOWN_MAX		0x2e
67

C
Corentin Chary 已提交
68
/* WMI Methods */
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
#define ASUS_WMI_METHODID_SPEC	        0x43455053 /* BIOS SPECification */
#define ASUS_WMI_METHODID_SFBD		0x44424653 /* Set First Boot Device */
#define ASUS_WMI_METHODID_GLCD		0x44434C47 /* Get LCD status */
#define ASUS_WMI_METHODID_GPID		0x44495047 /* Get Panel ID?? (Resol) */
#define ASUS_WMI_METHODID_QMOD		0x444F4D51 /* Quiet MODe */
#define ASUS_WMI_METHODID_SPLV		0x4C425053 /* Set Panel Light Value */
#define ASUS_WMI_METHODID_SFUN		0x4E554653 /* FUNCtionalities */
#define ASUS_WMI_METHODID_SDSP		0x50534453 /* Set DiSPlay output */
#define ASUS_WMI_METHODID_GDSP		0x50534447 /* Get DiSPlay output */
#define ASUS_WMI_METHODID_DEVP		0x50564544 /* DEVice Policy */
#define ASUS_WMI_METHODID_OSVR		0x5256534F /* OS VeRsion */
#define ASUS_WMI_METHODID_DSTS		0x53544344 /* Device STatuS */
#define ASUS_WMI_METHODID_DSTS2		0x53545344 /* Device STatuS #2*/
#define ASUS_WMI_METHODID_BSTS		0x53545342 /* Bios STatuS ? */
#define ASUS_WMI_METHODID_DEVS		0x53564544 /* DEVice Set */
#define ASUS_WMI_METHODID_CFVS		0x53564643 /* CPU Frequency Volt Set */
#define ASUS_WMI_METHODID_KBFT		0x5446424B /* KeyBoard FilTer */
#define ASUS_WMI_METHODID_INIT		0x54494E49 /* INITialize */
#define ASUS_WMI_METHODID_HKEY		0x59454B48 /* Hot KEY ?? */
Y
Yong Wang 已提交
88

89 90
#define ASUS_WMI_UNSUPPORTED_METHOD	0xFFFFFFFE

C
Corentin Chary 已提交
91
/* Wireless */
92 93
#define ASUS_WMI_DEVID_HW_SWITCH	0x00010001
#define ASUS_WMI_DEVID_WIRELESS_LED	0x00010002
94 95
#define ASUS_WMI_DEVID_WLAN		0x00010011
#define ASUS_WMI_DEVID_BLUETOOTH	0x00010013
96
#define ASUS_WMI_DEVID_GPS		0x00010015
97 98
#define ASUS_WMI_DEVID_WIMAX		0x00010017
#define ASUS_WMI_DEVID_WWAN3G		0x00010019
99 100 101 102
#define ASUS_WMI_DEVID_UWB		0x00010021

/* Leds */
/* 0x000200XX and 0x000400XX */
C
Corentin Chary 已提交
103 104

/* Backlight and Brightness */
105 106
#define ASUS_WMI_DEVID_BACKLIGHT	0x00050011
#define ASUS_WMI_DEVID_BRIGHTNESS	0x00050012
107 108
#define ASUS_WMI_DEVID_KBD_BACKLIGHT	0x00050021
#define ASUS_WMI_DEVID_LIGHT_SENSOR	0x00050022 /* ?? */
C
Corentin Chary 已提交
109 110

/* Misc */
111
#define ASUS_WMI_DEVID_CAMERA		0x00060013
C
Corentin Chary 已提交
112 113

/* Storage */
114
#define ASUS_WMI_DEVID_CARDREADER	0x00080013
C
Corentin Chary 已提交
115 116

/* Input */
C
Corentin Chary 已提交
117
#define ASUS_WMI_DEVID_TOUCHPAD		0x00100011
118
#define ASUS_WMI_DEVID_TOUCHPAD_LED	0x00100012
Y
Yong Wang 已提交
119

120 121 122 123 124 125 126
/* Fan, Thermal */
#define ASUS_WMI_DEVID_THERMAL_CTRL	0x00110011
#define ASUS_WMI_DEVID_FAN_CTRL		0x00110012

/* Power */
#define ASUS_WMI_DEVID_PROCESSOR_STATE	0x00120012

C
Corentin Chary 已提交
127
/* DSTS masks */
128
#define ASUS_WMI_DSTS_STATUS_BIT	0x00000001
129
#define ASUS_WMI_DSTS_UNKNOWN_BIT	0x00000002
130
#define ASUS_WMI_DSTS_PRESENCE_BIT	0x00010000
131 132
#define ASUS_WMI_DSTS_USER_BIT		0x00020000
#define ASUS_WMI_DSTS_BIOS_BIT		0x00040000
133 134
#define ASUS_WMI_DSTS_BRIGHTNESS_MASK	0x000000FF
#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK	0x0000FF00
135

Y
Yong Wang 已提交
136
struct bios_args {
137 138 139
	u32 arg0;
	u32 arg1;
} __packed;
Y
Yong Wang 已提交
140

C
Corentin Chary 已提交
141
/*
142
 * <platform>/    - debugfs root directory
C
Corentin Chary 已提交
143 144
 *   dev_id      - current dev_id
 *   ctrl_param  - current ctrl_param
145
 *   method_id   - current method_id
C
Corentin Chary 已提交
146 147
 *   devs        - call DEVS(dev_id, ctrl_param) and print result
 *   dsts        - call DSTS(dev_id)  and print result
148
 *   call        - call method_id(dev_id, ctrl_param) and print result
C
Corentin Chary 已提交
149
 */
150
struct asus_wmi_debug {
C
Corentin Chary 已提交
151
	struct dentry *root;
152
	u32 method_id;
C
Corentin Chary 已提交
153 154 155 156
	u32 dev_id;
	u32 ctrl_param;
};

157 158 159 160 161 162
struct asus_rfkill {
	struct asus_wmi *asus;
	struct rfkill *rfkill;
	u32 dev_id;
};

163
struct asus_wmi {
164
	int dsts_id;
165 166
	int spec;
	int sfun;
167

168
	struct input_dev *inputdev;
Y
Yong Wang 已提交
169
	struct backlight_device *backlight_device;
170
	struct platform_device *platform_device;
171 172 173 174 175

	struct led_classdev tpd_led;
	int tpd_led_wk;
	struct workqueue_struct *led_workqueue;
	struct work_struct tpd_led_work;
176

177 178 179 180
	struct asus_rfkill wlan;
	struct asus_rfkill bluetooth;
	struct asus_rfkill wimax;
	struct asus_rfkill wwan3g;
C
Corentin Chary 已提交
181

182 183
	struct hotplug_slot *hotplug_slot;
	struct mutex hotplug_lock;
184 185 186
	struct mutex wmi_lock;
	struct workqueue_struct *hotplug_workqueue;
	struct work_struct hotplug_work;
187

188 189 190
	struct asus_wmi_debug debug;

	struct asus_wmi_driver *driver;
191 192
};

193
static int asus_wmi_input_init(struct asus_wmi *asus)
194 195 196
{
	int err;

197 198
	asus->inputdev = input_allocate_device();
	if (!asus->inputdev)
199 200
		return -ENOMEM;

201 202 203 204
	asus->inputdev->name = asus->driver->input_phys;
	asus->inputdev->phys = asus->driver->input_name;
	asus->inputdev->id.bustype = BUS_HOST;
	asus->inputdev->dev.parent = &asus->platform_device->dev;
205

206
	err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
207 208 209
	if (err)
		goto err_free_dev;

210
	err = input_register_device(asus->inputdev);
211 212 213 214 215 216
	if (err)
		goto err_free_keymap;

	return 0;

err_free_keymap:
217
	sparse_keymap_free(asus->inputdev);
218
err_free_dev:
219
	input_free_device(asus->inputdev);
220 221 222
	return err;
}

223
static void asus_wmi_input_exit(struct asus_wmi *asus)
224
{
225 226 227
	if (asus->inputdev) {
		sparse_keymap_free(asus->inputdev);
		input_unregister_device(asus->inputdev);
228 229
	}

230
	asus->inputdev = NULL;
231 232
}

233 234
static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
				    u32 *retval)
Y
Yong Wang 已提交
235
{
236 237 238 239 240
	struct bios_args args = {
		.arg0 = arg0,
		.arg1 = arg1,
	};
	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
Y
Yong Wang 已提交
241 242
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	acpi_status status;
243
	union acpi_object *obj;
Y
Yong Wang 已提交
244 245
	u32 tmp;

246
	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,
247
				     &input, &output);
Y
Yong Wang 已提交
248 249

	if (ACPI_FAILURE(status))
250
		goto exit;
Y
Yong Wang 已提交
251 252 253

	obj = (union acpi_object *)output.pointer;
	if (obj && obj->type == ACPI_TYPE_INTEGER)
254
		tmp = (u32) obj->integer.value;
Y
Yong Wang 已提交
255 256 257
	else
		tmp = 0;

258 259
	if (retval)
		*retval = tmp;
Y
Yong Wang 已提交
260 261 262

	kfree(obj);

263 264 265 266 267 268
exit:
	if (ACPI_FAILURE(status))
		return -EIO;

	if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
		return -ENODEV;
Y
Yong Wang 已提交
269

270
	return 0;
Y
Yong Wang 已提交
271 272
}

273
static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
Y
Yong Wang 已提交
274
{
275
	return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
276
}
Y
Yong Wang 已提交
277

278
static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
279
				 u32 *retval)
280 281 282
{
	return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
					ctrl_param, retval);
Y
Yong Wang 已提交
283 284
}

285
/* Helper for special devices with magic return codes */
286 287
static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
				      u32 dev_id, u32 mask)
288 289
{
	u32 retval = 0;
290
	int err;
291

292
	err = asus_wmi_get_devstate(asus, dev_id, &retval);
293

294 295
	if (err < 0)
		return err;
296

297
	if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT))
298 299
		return -ENODEV;

300 301 302 303 304
	if (mask == ASUS_WMI_DSTS_STATUS_BIT) {
		if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT)
			return -ENODEV;
	}

305 306 307
	return retval & mask;
}

308
static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
309
{
310 311
	return asus_wmi_get_devstate_bits(asus, dev_id,
					  ASUS_WMI_DSTS_STATUS_BIT);
312 313
}

314 315 316 317 318 319
/*
 * LEDs
 */
/*
 * These functions actually update the LED's, and are called from a
 * workqueue. By doing this as separate work rather than when the LED
320
 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
321 322 323 324 325
 * potentially bad time, such as a timer interrupt.
 */
static void tpd_led_update(struct work_struct *work)
{
	int ctrl_param;
326
	struct asus_wmi *asus;
327

328
	asus = container_of(work, struct asus_wmi, tpd_led_work);
329

330 331
	ctrl_param = asus->tpd_led_wk;
	asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
332 333 334 335 336
}

static void tpd_led_set(struct led_classdev *led_cdev,
			enum led_brightness value)
{
337
	struct asus_wmi *asus;
338

339
	asus = container_of(led_cdev, struct asus_wmi, tpd_led);
340

341 342
	asus->tpd_led_wk = !!value;
	queue_work(asus->led_workqueue, &asus->tpd_led_work);
343 344
}

345
static int read_tpd_led_state(struct asus_wmi *asus)
346
{
347
	return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED);
348 349 350 351
}

static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
{
352
	struct asus_wmi *asus;
353

354
	asus = container_of(led_cdev, struct asus_wmi, tpd_led);
355

356
	return read_tpd_led_state(asus);
357 358
}

359
static int asus_wmi_led_init(struct asus_wmi *asus)
360 361 362
{
	int rv;

363
	if (read_tpd_led_state(asus) < 0)
364 365
		return 0;

366 367
	asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
	if (!asus->led_workqueue)
368
		return -ENOMEM;
369
	INIT_WORK(&asus->tpd_led_work, tpd_led_update);
370

371 372 373 374
	asus->tpd_led.name = "asus::touchpad";
	asus->tpd_led.brightness_set = tpd_led_set;
	asus->tpd_led.brightness_get = tpd_led_get;
	asus->tpd_led.max_brightness = 1;
375

376
	rv = led_classdev_register(&asus->platform_device->dev, &asus->tpd_led);
377
	if (rv) {
378
		destroy_workqueue(asus->led_workqueue);
379 380 381 382 383 384
		return rv;
	}

	return 0;
}

385
static void asus_wmi_led_exit(struct asus_wmi *asus)
386
{
387 388 389 390
	if (asus->tpd_led.dev)
		led_classdev_unregister(&asus->tpd_led);
	if (asus->led_workqueue)
		destroy_workqueue(asus->led_workqueue);
391 392
}

393 394 395
/*
 * PCI hotplug (for wlan rfkill)
 */
396
static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus)
397
{
398
	int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
399

400
	if (result < 0)
401
		return false;
402
	return !result;
403 404
}

405
static void asus_rfkill_hotplug(struct asus_wmi *asus)
406 407 408
{
	struct pci_dev *dev;
	struct pci_bus *bus;
409
	bool blocked;
410 411 412
	bool absent;
	u32 l;

413 414 415
	mutex_lock(&asus->wmi_lock);
	blocked = asus_wlan_rfkill_blocked(asus);
	mutex_unlock(&asus->wmi_lock);
416

417
	mutex_lock(&asus->hotplug_lock);
418

419 420
	if (asus->wlan.rfkill)
		rfkill_set_sw_state(asus->wlan.rfkill, blocked);
421

422
	if (asus->hotplug_slot) {
423 424 425 426 427 428 429 430 431 432 433 434 435 436
		bus = pci_find_bus(0, 1);
		if (!bus) {
			pr_warning("Unable to find PCI bus 1?\n");
			goto out_unlock;
		}

		if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
			pr_err("Unable to read PCI config space?\n");
			goto out_unlock;
		}
		absent = (l == 0xffffffff);

		if (blocked != absent) {
			pr_warning("BIOS says wireless lan is %s, "
437 438 439
				   "but the pci device is %s\n",
				   blocked ? "blocked" : "unblocked",
				   absent ? "absent" : "present");
440
			pr_warning("skipped wireless hotplug as probably "
441
				   "inappropriate for this model\n");
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
			goto out_unlock;
		}

		if (!blocked) {
			dev = pci_get_slot(bus, 0);
			if (dev) {
				/* Device already present */
				pci_dev_put(dev);
				goto out_unlock;
			}
			dev = pci_scan_single_device(bus, 0);
			if (dev) {
				pci_bus_assign_resources(bus);
				if (pci_bus_add_device(dev))
					pr_err("Unable to hotplug wifi\n");
			}
		} else {
			dev = pci_get_slot(bus, 0);
			if (dev) {
				pci_remove_bus_device(dev);
				pci_dev_put(dev);
			}
		}
	}

out_unlock:
468
	mutex_unlock(&asus->hotplug_lock);
469 470
}

471
static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data)
472
{
473
	struct asus_wmi *asus = data;
474 475 476 477

	if (event != ACPI_NOTIFY_BUS_CHECK)
		return;

478
	/*
479
	 * We can't call directly asus_rfkill_hotplug because most
480 481
	 * of the time WMBC is still being executed and not reetrant.
	 * There is currently no way to tell ACPICA that  we want this
482
	 * method to be serialized, we schedule a asus_rfkill_hotplug
483 484
	 * call later, in a safer context.
	 */
485
	queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
486 487
}

488
static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node)
489 490 491 492 493 494 495 496 497
{
	acpi_status status;
	acpi_handle handle;

	status = acpi_get_handle(NULL, node, &handle);

	if (ACPI_SUCCESS(status)) {
		status = acpi_install_notify_handler(handle,
						     ACPI_SYSTEM_NOTIFY,
498
						     asus_rfkill_notify, asus);
499 500 501 502 503 504 505 506
		if (ACPI_FAILURE(status))
			pr_warning("Failed to register notify on %s\n", node);
	} else
		return -ENODEV;

	return 0;
}

507
static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
508 509 510 511 512 513 514 515
{
	acpi_status status = AE_OK;
	acpi_handle handle;

	status = acpi_get_handle(NULL, node, &handle);

	if (ACPI_SUCCESS(status)) {
		status = acpi_remove_notify_handler(handle,
516 517
						    ACPI_SYSTEM_NOTIFY,
						    asus_rfkill_notify);
518 519
		if (ACPI_FAILURE(status))
			pr_err("Error removing rfkill notify handler %s\n",
520
			       node);
521 522 523
	}
}

524 525
static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
				   u8 *value)
526
{
527 528
	struct asus_wmi *asus = hotplug_slot->private;
	int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
529

530 531
	if (result < 0)
		return result;
532

533
	*value = !!result;
534 535 536
	return 0;
}

537
static void asus_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
538 539 540 541 542
{
	kfree(hotplug_slot->info);
	kfree(hotplug_slot);
}

543
static struct hotplug_slot_ops asus_hotplug_slot_ops = {
544
	.owner = THIS_MODULE,
545 546
	.get_adapter_status = asus_get_adapter_status,
	.get_power_status = asus_get_adapter_status,
547 548
};

549
static void asus_hotplug_work(struct work_struct *work)
550
{
551
	struct asus_wmi *asus;
552

553 554
	asus = container_of(work, struct asus_wmi, hotplug_work);
	asus_rfkill_hotplug(asus);
555 556
}

557
static int asus_setup_pci_hotplug(struct asus_wmi *asus)
558 559 560 561 562 563 564 565 566
{
	int ret = -ENOMEM;
	struct pci_bus *bus = pci_find_bus(0, 1);

	if (!bus) {
		pr_err("Unable to find wifi PCI bus\n");
		return -ENODEV;
	}

567 568 569
	asus->hotplug_workqueue =
	    create_singlethread_workqueue("hotplug_workqueue");
	if (!asus->hotplug_workqueue)
570 571
		goto error_workqueue;

572
	INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
573

574 575
	asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
	if (!asus->hotplug_slot)
576 577
		goto error_slot;

578 579 580
	asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
					   GFP_KERNEL);
	if (!asus->hotplug_slot->info)
581 582
		goto error_info;

583 584 585 586 587
	asus->hotplug_slot->private = asus;
	asus->hotplug_slot->release = &asus_cleanup_pci_hotplug;
	asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
	asus_get_adapter_status(asus->hotplug_slot,
				&asus->hotplug_slot->info->adapter_status);
588

589
	ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi");
590 591 592 593 594 595 596 597
	if (ret) {
		pr_err("Unable to register hotplug slot - %d\n", ret);
		goto error_register;
	}

	return 0;

error_register:
598
	kfree(asus->hotplug_slot->info);
599
error_info:
600 601
	kfree(asus->hotplug_slot);
	asus->hotplug_slot = NULL;
602
error_slot:
603
	destroy_workqueue(asus->hotplug_workqueue);
604
error_workqueue:
605 606 607
	return ret;
}

608 609 610
/*
 * Rfkill devices
 */
611
static int asus_rfkill_set(void *data, bool blocked)
612
{
613
	struct asus_rfkill *priv = data;
614 615
	u32 ctrl_param = !blocked;

616
	return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL);
617 618
}

619
static void asus_rfkill_query(struct rfkill *rfkill, void *data)
620
{
621
	struct asus_rfkill *priv = data;
622
	int result;
623

624
	result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id);
625

626
	if (result < 0)
627
		return;
628

629
	rfkill_set_sw_state(priv->rfkill, !result);
630 631
}

632
static int asus_rfkill_wlan_set(void *data, bool blocked)
633
{
634 635
	struct asus_rfkill *priv = data;
	struct asus_wmi *asus = priv->asus;
636 637 638 639
	int ret;

	/*
	 * This handler is enabled only if hotplug is enabled.
640
	 * In this case, the asus_wmi_set_devstate() will
641 642 643 644
	 * trigger a wmi notification and we need to wait
	 * this call to finish before being able to call
	 * any wmi method
	 */
645
	mutex_lock(&asus->wmi_lock);
646
	ret = asus_rfkill_set(data, blocked);
647
	mutex_unlock(&asus->wmi_lock);
648 649 650
	return ret;
}

651 652
static const struct rfkill_ops asus_rfkill_wlan_ops = {
	.set_block = asus_rfkill_wlan_set,
653
	.query = asus_rfkill_query,
654 655
};

656 657 658
static const struct rfkill_ops asus_rfkill_ops = {
	.set_block = asus_rfkill_set,
	.query = asus_rfkill_query,
659 660
};

661
static int asus_new_rfkill(struct asus_wmi *asus,
662
			   struct asus_rfkill *arfkill,
663
			   const char *name, enum rfkill_type type, int dev_id)
664
{
665
	int result = asus_wmi_get_devstate_simple(asus, dev_id);
666
	struct rfkill **rfkill = &arfkill->rfkill;
667

668 669
	if (result < 0)
		return result;
670

671 672 673
	arfkill->dev_id = dev_id;
	arfkill->asus = asus;

674 675
	if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless)
		*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
676
				       &asus_rfkill_wlan_ops, arfkill);
677
	else
678
		*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
679
				       &asus_rfkill_ops, arfkill);
680 681 682 683

	if (!*rfkill)
		return -EINVAL;

684
	rfkill_init_sw_state(*rfkill, !result);
685 686 687 688 689 690 691 692 693
	result = rfkill_register(*rfkill);
	if (result) {
		rfkill_destroy(*rfkill);
		*rfkill = NULL;
		return result;
	}
	return 0;
}

694
static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
695
{
696 697 698
	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
	asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
699 700 701 702
	if (asus->wlan.rfkill) {
		rfkill_unregister(asus->wlan.rfkill);
		rfkill_destroy(asus->wlan.rfkill);
		asus->wlan.rfkill = NULL;
703
	}
704 705
	/*
	 * Refresh pci hotplug in case the rfkill state was changed after
706
	 * asus_unregister_rfkill_notifier()
707
	 */
708 709 710 711 712 713
	asus_rfkill_hotplug(asus);
	if (asus->hotplug_slot)
		pci_hp_deregister(asus->hotplug_slot);
	if (asus->hotplug_workqueue)
		destroy_workqueue(asus->hotplug_workqueue);

714 715 716 717
	if (asus->bluetooth.rfkill) {
		rfkill_unregister(asus->bluetooth.rfkill);
		rfkill_destroy(asus->bluetooth.rfkill);
		asus->bluetooth.rfkill = NULL;
718
	}
719 720 721 722
	if (asus->wimax.rfkill) {
		rfkill_unregister(asus->wimax.rfkill);
		rfkill_destroy(asus->wimax.rfkill);
		asus->wimax.rfkill = NULL;
C
Corentin Chary 已提交
723
	}
724 725 726 727
	if (asus->wwan3g.rfkill) {
		rfkill_unregister(asus->wwan3g.rfkill);
		rfkill_destroy(asus->wwan3g.rfkill);
		asus->wwan3g.rfkill = NULL;
728 729 730
	}
}

731
static int asus_wmi_rfkill_init(struct asus_wmi *asus)
732 733 734
{
	int result = 0;

735 736
	mutex_init(&asus->hotplug_lock);
	mutex_init(&asus->wmi_lock);
737

738 739
	result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
				 RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
740 741 742 743

	if (result && result != -ENODEV)
		goto exit;

744
	result = asus_new_rfkill(asus, &asus->bluetooth,
745 746
				 "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
				 ASUS_WMI_DEVID_BLUETOOTH);
747 748 749 750

	if (result && result != -ENODEV)
		goto exit;

751 752
	result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
				 RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
C
Corentin Chary 已提交
753 754 755 756

	if (result && result != -ENODEV)
		goto exit;

757 758
	result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
				 RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
759 760 761 762

	if (result && result != -ENODEV)
		goto exit;

763
	if (!asus->driver->hotplug_wireless)
764 765
		goto exit;

766
	result = asus_setup_pci_hotplug(asus);
767 768 769 770 771 772 773
	/*
	 * If we get -EBUSY then something else is handling the PCI hotplug -
	 * don't fail in this case
	 */
	if (result == -EBUSY)
		result = 0;

774 775 776
	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
	asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7");
777 778 779 780
	/*
	 * Refresh pci hotplug in case the rfkill state was changed during
	 * setup.
	 */
781
	asus_rfkill_hotplug(asus);
782

783 784
exit:
	if (result && result != -ENODEV)
785
		asus_wmi_rfkill_exit(asus);
786 787 788 789 790 791 792

	if (result == -ENODEV)
		result = 0;

	return result;
}

793 794 795
/*
 * Backlight
 */
796
static int read_backlight_power(struct asus_wmi *asus)
797
{
798
	int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT);
799 800 801 802 803 804 805

	if (ret < 0)
		return ret;

	return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
}

806
static int read_brightness_max(struct asus_wmi *asus)
Y
Yong Wang 已提交
807
{
808
	u32 retval;
809
	int err;
Y
Yong Wang 已提交
810

811
	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);
Y
Yong Wang 已提交
812

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
	if (err < 0)
		return err;

	retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK;
	retval >>= 8;

	if (!retval)
		return -ENODEV;

	return retval;
}

static int read_brightness(struct backlight_device *bd)
{
	struct asus_wmi *asus = bl_get_data(bd);
	u32 retval, err;

	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval);

832 833 834 835
	if (err < 0)
		return err;

	return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
Y
Yong Wang 已提交
836 837 838 839
}

static int update_bl_status(struct backlight_device *bd)
{
840
	struct asus_wmi *asus = bl_get_data(bd);
841
	u32 ctrl_param;
842
	int power, err;
Y
Yong Wang 已提交
843 844 845

	ctrl_param = bd->props.brightness;

846 847
	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
				    ctrl_param, NULL);
Y
Yong Wang 已提交
848

849 850
	if (err < 0)
		return err;
851

852
	power = read_backlight_power(asus);
853 854
	if (power != -ENODEV && bd->props.power != power) {
		ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
855 856
		err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
					    ctrl_param, NULL);
857
	}
858
	return err;
Y
Yong Wang 已提交
859 860
}

861
static const struct backlight_ops asus_wmi_bl_ops = {
Y
Yong Wang 已提交
862 863 864 865
	.get_brightness = read_brightness,
	.update_status = update_bl_status,
};

866
static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code)
Y
Yong Wang 已提交
867
{
868
	struct backlight_device *bd = asus->backlight_device;
Y
Yong Wang 已提交
869
	int old = bd->props.brightness;
870
	int new = old;
Y
Yong Wang 已提交
871 872 873 874 875 876 877 878 879 880 881 882 883

	if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
		new = code - NOTIFY_BRNUP_MIN + 1;
	else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
		new = code - NOTIFY_BRNDOWN_MIN;

	bd->props.brightness = new;
	backlight_update_status(bd);
	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);

	return old;
}

884
static int asus_wmi_backlight_init(struct asus_wmi *asus)
Y
Yong Wang 已提交
885 886 887
{
	struct backlight_device *bd;
	struct backlight_properties props;
888 889 890
	int max;
	int power;

891
	max = read_brightness_max(asus);
892 893 894

	if (max == -ENODEV)
		max = 0;
895 896 897 898 899
	else if (max < 0)
		return max;

	power = read_backlight_power(asus);

900 901
	if (power == -ENODEV)
		power = FB_BLANK_UNBLANK;
902 903
	else if (power < 0)
		return power;
Y
Yong Wang 已提交
904 905

	memset(&props, 0, sizeof(struct backlight_properties));
906
	props.max_brightness = max;
907 908 909
	bd = backlight_device_register(asus->driver->name,
				       &asus->platform_device->dev, asus,
				       &asus_wmi_bl_ops, &props);
Y
Yong Wang 已提交
910 911 912 913 914
	if (IS_ERR(bd)) {
		pr_err("Could not register backlight device\n");
		return PTR_ERR(bd);
	}

915
	asus->backlight_device = bd;
Y
Yong Wang 已提交
916 917

	bd->props.brightness = read_brightness(bd);
918
	bd->props.power = power;
Y
Yong Wang 已提交
919 920 921 922 923
	backlight_update_status(bd);

	return 0;
}

924
static void asus_wmi_backlight_exit(struct asus_wmi *asus)
Y
Yong Wang 已提交
925
{
926 927
	if (asus->backlight_device)
		backlight_device_unregister(asus->backlight_device);
Y
Yong Wang 已提交
928

929
	asus->backlight_device = NULL;
Y
Yong Wang 已提交
930 931
}

932
static void asus_wmi_notify(u32 value, void *context)
Y
Yong Wang 已提交
933
{
934
	struct asus_wmi *asus = context;
Y
Yong Wang 已提交
935 936 937 938 939 940 941 942 943 944 945 946 947 948
	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
	acpi_status status;
	int code;
	int orig_code;

	status = wmi_get_event_data(value, &response);
	if (status != AE_OK) {
		pr_err("bad event status 0x%x\n", status);
		return;
	}

	obj = (union acpi_object *)response.pointer;

C
Corentin Chary 已提交
949 950
	if (!obj || obj->type != ACPI_TYPE_INTEGER)
		goto exit;
Y
Yong Wang 已提交
951

C
Corentin Chary 已提交
952 953
	code = obj->integer.value;
	orig_code = code;
Y
Yong Wang 已提交
954

C
Corentin Chary 已提交
955 956 957 958 959
	if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
		code = NOTIFY_BRNUP_MIN;
	else if (code >= NOTIFY_BRNDOWN_MIN &&
		 code <= NOTIFY_BRNDOWN_MAX)
		code = NOTIFY_BRNDOWN_MIN;
Y
Yong Wang 已提交
960

C
Corentin Chary 已提交
961 962 963 964 965
	if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
		if (!acpi_video_backlight_support())
			asus_wmi_backlight_notify(asus, orig_code);
	} else if (!sparse_keymap_report_event(asus->inputdev, code, 1, true))
		pr_info("Unknown key %x pressed\n", code);
Y
Yong Wang 已提交
966

C
Corentin Chary 已提交
967
exit:
Y
Yong Wang 已提交
968 969 970
	kfree(obj);
}

971 972 973 974 975 976 977 978 979 980 981 982
/*
 * Sys helpers
 */
static int parse_arg(const char *buf, unsigned long count, int *val)
{
	if (!count)
		return 0;
	if (sscanf(buf, "%i", val) != 1)
		return -EINVAL;
	return count;
}

983 984
static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
			     const char *buf, size_t count)
985 986
{
	u32 retval;
987
	int rv, err, value;
988

989
	value = asus_wmi_get_devstate_simple(asus, devid);
990
	if (value == -ENODEV)	/* Check device presence */
991 992 993
		return value;

	rv = parse_arg(buf, count, &value);
994 995 996 997
	err = asus_wmi_set_devstate(devid, value, &retval);

	if (err < 0)
		return err;
998 999 1000 1001

	return rv;
}

1002
static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
1003
{
1004
	int value = asus_wmi_get_devstate_simple(asus, devid);
1005 1006 1007 1008 1009 1010 1011

	if (value < 0)
		return value;

	return sprintf(buf, "%d\n", value);
}

1012
#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm)			\
1013 1014 1015 1016
	static ssize_t show_##_name(struct device *dev,			\
				    struct device_attribute *attr,	\
				    char *buf)				\
	{								\
1017 1018 1019
		struct asus_wmi *asus = dev_get_drvdata(dev);		\
									\
		return show_sys_wmi(asus, _cm, buf);			\
1020 1021 1022 1023 1024
	}								\
	static ssize_t store_##_name(struct device *dev,		\
				     struct device_attribute *attr,	\
				     const char *buf, size_t count)	\
	{								\
1025 1026 1027
		struct asus_wmi *asus = dev_get_drvdata(dev);		\
									\
		return store_sys_wmi(asus, _cm, buf, count);		\
1028 1029 1030 1031 1032 1033 1034 1035 1036
	}								\
	static struct device_attribute dev_attr_##_name = {		\
		.attr = {						\
			.name = __stringify(_name),			\
			.mode = _mode },				\
		.show   = show_##_name,					\
		.store  = store_##_name,				\
	}

1037 1038 1039
ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
1040

D
Dmitry Torokhov 已提交
1041 1042
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
			   const char *buf, size_t count)
1043 1044 1045 1046 1047 1048 1049 1050
{
	int value;

	if (!count || sscanf(buf, "%i", &value) != 1)
		return -EINVAL;
	if (value < 0 || value > 2)
		return -EINVAL;

1051
	return asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
1052 1053 1054 1055
}

static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);

1056 1057
static struct attribute *platform_attributes[] = {
	&dev_attr_cpufv.attr,
1058 1059
	&dev_attr_camera.attr,
	&dev_attr_cardr.attr,
1060
	&dev_attr_touchpad.attr,
1061 1062 1063
	NULL
};

1064 1065
static mode_t asus_sysfs_is_visible(struct kobject *kobj,
				    struct attribute *attr, int idx)
1066
{
1067 1068 1069 1070
	struct device *dev = container_of(kobj, struct device, kobj);
	struct platform_device *pdev = to_platform_device(dev);
	struct asus_wmi *asus = platform_get_drvdata(pdev);
	bool ok = true;
1071 1072 1073
	int devid = -1;

	if (attr == &dev_attr_camera.attr)
1074
		devid = ASUS_WMI_DEVID_CAMERA;
1075
	else if (attr == &dev_attr_cardr.attr)
1076
		devid = ASUS_WMI_DEVID_CARDREADER;
1077
	else if (attr == &dev_attr_touchpad.attr)
1078
		devid = ASUS_WMI_DEVID_TOUCHPAD;
1079 1080

	if (devid != -1)
1081
		ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
1082

1083
	return ok ? attr->mode : 0;
1084 1085
}

1086
static struct attribute_group platform_attribute_group = {
1087 1088
	.is_visible = asus_sysfs_is_visible,
	.attrs = platform_attributes
1089 1090
};

1091
static void asus_wmi_sysfs_exit(struct platform_device *device)
1092
{
1093
	sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
1094 1095
}

1096
static int asus_wmi_sysfs_init(struct platform_device *device)
1097
{
1098
	return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
1099 1100
}

1101 1102 1103
/*
 * Platform device
 */
1104
static int __init asus_wmi_platform_init(struct asus_wmi *asus)
1105
{
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
	int rv;

	/* INIT enable hotkeys on some models */
	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
		pr_info("Initialization: %#x", rv);

	/* We don't know yet what to do with this version... */
	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
		pr_info("BIOS WMI version: %d.%d", rv >> 8, rv & 0xFF);
		asus->spec = rv;
	}

	/*
	 * The SFUN method probably allows the original driver to get the list
	 * of features supported by a given model. For now, 0x0100 or 0x0800
	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
	 * The significance of others is yet to be found.
	 */
	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
		pr_info("SFUN value: %#x", rv);
		asus->sfun = rv;
	}

1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
	/*
	 * Eee PC and Notebooks seems to have different method_id for DSTS,
	 * but it may also be related to the BIOS's SPEC.
	 * Note, on most Eeepc, there is no way to check if a method exist
	 * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
	 * but once again, SPEC may probably be used for that kind of things.
	 */
	if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
		asus->dsts_id = ASUS_WMI_METHODID_DSTS;
	else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL))
		asus->dsts_id = ASUS_WMI_METHODID_DSTS2;

	if (!asus->dsts_id) {
		pr_err("Can't find DSTS");
		return -ENODEV;
	}

1146
	return asus_wmi_sysfs_init(asus->platform_device);
1147 1148
}

1149
static void asus_wmi_platform_exit(struct asus_wmi *asus)
1150
{
1151
	asus_wmi_sysfs_exit(asus->platform_device);
1152 1153
}

C
Corentin Chary 已提交
1154 1155 1156
/*
 * debugfs
 */
1157 1158
struct asus_wmi_debugfs_node {
	struct asus_wmi *asus;
C
Corentin Chary 已提交
1159
	char *name;
1160
	int (*show) (struct seq_file *m, void *data);
C
Corentin Chary 已提交
1161 1162 1163 1164
};

static int show_dsts(struct seq_file *m, void *data)
{
1165
	struct asus_wmi *asus = m->private;
1166
	int err;
C
Corentin Chary 已提交
1167 1168
	u32 retval = -1;

1169
	err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval);
C
Corentin Chary 已提交
1170

1171 1172
	if (err < 0)
		return err;
C
Corentin Chary 已提交
1173

1174
	seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval);
C
Corentin Chary 已提交
1175 1176 1177 1178 1179 1180

	return 0;
}

static int show_devs(struct seq_file *m, void *data)
{
1181
	struct asus_wmi *asus = m->private;
1182
	int err;
C
Corentin Chary 已提交
1183 1184
	u32 retval = -1;

1185 1186 1187 1188 1189
	err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param,
				    &retval);

	if (err < 0)
		return err;
C
Corentin Chary 已提交
1190

1191
	seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id,
1192
		   asus->debug.ctrl_param, retval);
C
Corentin Chary 已提交
1193 1194 1195 1196

	return 0;
}

1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
static int show_call(struct seq_file *m, void *data)
{
	struct asus_wmi *asus = m->private;
	struct bios_args args = {
		.arg0 = asus->debug.dev_id,
		.arg1 = asus->debug.ctrl_param,
	};
	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
	acpi_status status;

	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
				     1, asus->debug.method_id,
				     &input, &output);

	if (ACPI_FAILURE(status))
		return -EIO;

	obj = (union acpi_object *)output.pointer;
	if (obj && obj->type == ACPI_TYPE_INTEGER)
		seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id,
			   asus->debug.dev_id, asus->debug.ctrl_param,
			   (u32) obj->integer.value);
	else
		seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id,
			   asus->debug.dev_id, asus->debug.ctrl_param,
			   obj->type);

	kfree(obj);

	return 0;
}

1231 1232 1233
static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = {
	{NULL, "devs", show_devs},
	{NULL, "dsts", show_dsts},
1234
	{NULL, "call", show_call},
C
Corentin Chary 已提交
1235 1236
};

1237
static int asus_wmi_debugfs_open(struct inode *inode, struct file *file)
C
Corentin Chary 已提交
1238
{
1239
	struct asus_wmi_debugfs_node *node = inode->i_private;
C
Corentin Chary 已提交
1240

1241
	return single_open(file, node->show, node->asus);
C
Corentin Chary 已提交
1242 1243
}

1244
static const struct file_operations asus_wmi_debugfs_io_ops = {
C
Corentin Chary 已提交
1245
	.owner = THIS_MODULE,
1246
	.open = asus_wmi_debugfs_open,
C
Corentin Chary 已提交
1247 1248 1249 1250 1251
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};

1252
static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
C
Corentin Chary 已提交
1253
{
1254
	debugfs_remove_recursive(asus->debug.root);
C
Corentin Chary 已提交
1255 1256
}

1257
static int asus_wmi_debugfs_init(struct asus_wmi *asus)
C
Corentin Chary 已提交
1258 1259 1260 1261
{
	struct dentry *dent;
	int i;

1262 1263
	asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
	if (!asus->debug.root) {
C
Corentin Chary 已提交
1264 1265 1266 1267
		pr_err("failed to create debugfs directory");
		goto error_debugfs;
	}

1268 1269 1270 1271 1272
	dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR,
				  asus->debug.root, &asus->debug.method_id);
	if (!dent)
		goto error_debugfs;

1273 1274
	dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR,
				  asus->debug.root, &asus->debug.dev_id);
C
Corentin Chary 已提交
1275 1276 1277
	if (!dent)
		goto error_debugfs;

1278 1279
	dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR,
				  asus->debug.root, &asus->debug.ctrl_param);
C
Corentin Chary 已提交
1280 1281 1282
	if (!dent)
		goto error_debugfs;

1283 1284
	for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
		struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
C
Corentin Chary 已提交
1285

1286
		node->asus = asus;
C
Corentin Chary 已提交
1287
		dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
1288 1289
					   asus->debug.root, node,
					   &asus_wmi_debugfs_io_ops);
C
Corentin Chary 已提交
1290 1291 1292 1293 1294 1295 1296 1297 1298
		if (!dent) {
			pr_err("failed to create debug file: %s\n", node->name);
			goto error_debugfs;
		}
	}

	return 0;

error_debugfs:
1299
	asus_wmi_debugfs_exit(asus);
C
Corentin Chary 已提交
1300 1301 1302
	return -ENOMEM;
}

1303 1304 1305
/*
 * WMI Driver
 */
1306
static int asus_wmi_add(struct platform_device *pdev)
1307
{
1308 1309 1310
	struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
	struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
	struct asus_wmi *asus;
1311
	acpi_status status;
1312
	int err;
1313

1314 1315
	asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
	if (!asus)
1316 1317
		return -ENOMEM;

1318 1319 1320 1321
	asus->driver = wdrv;
	asus->platform_device = pdev;
	wdrv->platform_device = pdev;
	platform_set_drvdata(asus->platform_device, asus);
1322

1323 1324
	if (wdrv->quirks)
		wdrv->quirks(asus->driver);
1325

1326
	err = asus_wmi_platform_init(asus);
1327 1328
	if (err)
		goto fail_platform;
1329

1330
	err = asus_wmi_input_init(asus);
1331
	if (err)
1332
		goto fail_input;
Y
Yong Wang 已提交
1333

1334
	err = asus_wmi_led_init(asus);
1335 1336 1337
	if (err)
		goto fail_leds;

1338
	err = asus_wmi_rfkill_init(asus);
1339 1340 1341
	if (err)
		goto fail_rfkill;

Y
Yong Wang 已提交
1342
	if (!acpi_video_backlight_support()) {
1343
		err = asus_wmi_backlight_init(asus);
1344
		if (err && err != -ENODEV)
1345
			goto fail_backlight;
Y
Yong Wang 已提交
1346 1347
	} else
		pr_info("Backlight controlled by ACPI video driver\n");
1348

1349 1350
	status = wmi_install_notify_handler(asus->driver->event_guid,
					    asus_wmi_notify, asus);
1351
	if (ACPI_FAILURE(status)) {
1352
		pr_err("Unable to register notify handler - %d\n", status);
1353
		err = -ENODEV;
1354
		goto fail_wmi_handler;
1355 1356
	}

1357
	err = asus_wmi_debugfs_init(asus);
C
Corentin Chary 已提交
1358 1359 1360
	if (err)
		goto fail_debugfs;

1361
	return 0;
1362

C
Corentin Chary 已提交
1363
fail_debugfs:
1364
	wmi_remove_notify_handler(asus->driver->event_guid);
1365
fail_wmi_handler:
1366
	asus_wmi_backlight_exit(asus);
1367
fail_backlight:
1368
	asus_wmi_rfkill_exit(asus);
1369
fail_rfkill:
1370
	asus_wmi_led_exit(asus);
1371
fail_leds:
1372
	asus_wmi_input_exit(asus);
1373
fail_input:
1374
	asus_wmi_platform_exit(asus);
1375
fail_platform:
1376
	kfree(asus);
1377
	return err;
1378 1379
}

1380
static int asus_wmi_remove(struct platform_device *device)
1381
{
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
	struct asus_wmi *asus;

	asus = platform_get_drvdata(device);
	wmi_remove_notify_handler(asus->driver->event_guid);
	asus_wmi_backlight_exit(asus);
	asus_wmi_input_exit(asus);
	asus_wmi_led_exit(asus);
	asus_wmi_rfkill_exit(asus);
	asus_wmi_debugfs_exit(asus);
	asus_wmi_platform_exit(asus);

	kfree(asus);
1394 1395 1396
	return 0;
}

1397 1398 1399
/*
 * Platform driver - hibernate/resume callbacks
 */
1400
static int asus_hotk_thaw(struct device *device)
1401
{
1402
	struct asus_wmi *asus = dev_get_drvdata(device);
1403

1404
	if (asus->wlan.rfkill) {
1405 1406 1407 1408 1409 1410 1411
		bool wlan;

		/*
		 * Work around bios bug - acpi _PTS turns off the wireless led
		 * during suspend.  Normally it restores it on resume, but
		 * we should kick it ourselves in case hibernation is aborted.
		 */
1412
		wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
1413
		asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
1414 1415 1416 1417 1418
	}

	return 0;
}

1419
static int asus_hotk_restore(struct device *device)
1420
{
1421
	struct asus_wmi *asus = dev_get_drvdata(device);
1422 1423 1424
	int bl;

	/* Refresh both wlan rfkill state and pci hotplug */
1425
	if (asus->wlan.rfkill)
1426
		asus_rfkill_hotplug(asus);
1427

1428
	if (asus->bluetooth.rfkill) {
1429 1430
		bl = !asus_wmi_get_devstate_simple(asus,
						   ASUS_WMI_DEVID_BLUETOOTH);
1431
		rfkill_set_sw_state(asus->bluetooth.rfkill, bl);
C
Corentin Chary 已提交
1432
	}
1433
	if (asus->wimax.rfkill) {
1434
		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX);
1435
		rfkill_set_sw_state(asus->wimax.rfkill, bl);
C
Corentin Chary 已提交
1436
	}
1437
	if (asus->wwan3g.rfkill) {
1438
		bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
1439
		rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
1440 1441 1442 1443 1444
	}

	return 0;
}

1445 1446 1447
static const struct dev_pm_ops asus_pm_ops = {
	.thaw = asus_hotk_thaw,
	.restore = asus_hotk_restore,
1448 1449
};

1450
static int asus_wmi_probe(struct platform_device *pdev)
1451
{
1452 1453 1454
	struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
	struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv);
	int ret;
1455

1456 1457
	if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
		pr_warning("Management GUID not found\n");
1458 1459 1460
		return -ENODEV;
	}

1461 1462
	if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
		pr_warning("Event GUID not found\n");
1463 1464 1465
		return -ENODEV;
	}

1466 1467 1468 1469 1470 1471 1472
	if (wdrv->probe) {
		ret = wdrv->probe(pdev);
		if (ret)
			return ret;
	}

	return asus_wmi_add(pdev);
1473
}
1474

1475
static bool used;
1476

1477
int asus_wmi_register_driver(struct asus_wmi_driver *driver)
1478
{
1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492
	struct platform_driver *platform_driver;
	struct platform_device *platform_device;

	if (used)
		return -EBUSY;

	platform_driver = &driver->platform_driver;
	platform_driver->remove = asus_wmi_remove;
	platform_driver->driver.owner = driver->owner;
	platform_driver->driver.name = driver->name;
	platform_driver->driver.pm = &asus_pm_ops;

	platform_device = platform_create_bundle(platform_driver,
						 asus_wmi_probe,
1493 1494 1495
						 NULL, 0, NULL, 0);
	if (IS_ERR(platform_device))
		return PTR_ERR(platform_device);
1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517

	used = true;
	return 0;
}
EXPORT_SYMBOL_GPL(asus_wmi_register_driver);

void asus_wmi_unregister_driver(struct asus_wmi_driver *driver)
{
	platform_device_unregister(driver->platform_device);
	platform_driver_unregister(&driver->platform_driver);
	used = false;
}
EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);

static int __init asus_wmi_init(void)
{
	if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
		pr_info("Asus Management GUID not found");
		return -ENODEV;
	}

	pr_info("ASUS WMI generic driver loaded");
1518 1519 1520
	return 0;
}

1521
static void __exit asus_wmi_exit(void)
1522
{
1523
	pr_info("ASUS WMI generic driver unloaded");
1524 1525
}

1526 1527
module_init(asus_wmi_init);
module_exit(asus_wmi_exit);