hp-wmi.c 17.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * HP WMI hotkeys
 *
 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
 *
 * 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
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
29
#include <linux/slab.h>
30 31
#include <linux/types.h>
#include <linux/input.h>
32
#include <linux/input/sparse-keymap.h>
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
#include <linux/string.h>

MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
MODULE_LICENSE("GPL");

MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");

#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"

#define HPWMI_DISPLAY_QUERY 0x1
#define HPWMI_HDDTEMP_QUERY 0x2
#define HPWMI_ALS_QUERY 0x3
51
#define HPWMI_HARDWARE_QUERY 0x4
52
#define HPWMI_WIRELESS_QUERY 0x5
53
#define HPWMI_HOTKEY_QUERY 0xc
54

55
#define PREFIX "HP WMI: "
56
#define UNIMP "Unimplemented "
57

A
Alan Jenkins 已提交
58 59 60 61 62 63
enum hp_wmi_radio {
	HPWMI_WIFI = 0,
	HPWMI_BLUETOOTH = 1,
	HPWMI_WWAN = 2,
};

64 65
enum hp_wmi_event_ids {
	HPWMI_DOCK_EVENT = 1,
66 67
	HPWMI_PARK_HDD = 2,
	HPWMI_SMART_ADAPTER = 3,
68 69
	HPWMI_BEZEL_BUTTON = 4,
	HPWMI_WIRELESS = 5,
70 71
	HPWMI_CPU_BATTERY_THROTTLE = 6,
	HPWMI_LOCK_SWITCH = 7,
72 73
};

74
static int __devinit hp_wmi_bios_setup(struct platform_device *device);
75
static int __exit hp_wmi_bios_remove(struct platform_device *device);
F
Frans Pop 已提交
76
static int hp_wmi_resume_handler(struct device *device);
77 78 79 80 81 82

struct bios_args {
	u32 signature;
	u32 command;
	u32 commandtype;
	u32 datasize;
M
Matthew Garrett 已提交
83
	u32 data;
84 85 86 87 88
};

struct bios_return {
	u32 sigpass;
	u32 return_code;
M
Matthew Garrett 已提交
89
	u32 value;
90 91
};

92 93 94 95 96 97 98
enum hp_return_value {
	HPWMI_RET_WRONG_SIGNATURE	= 0x02,
	HPWMI_RET_UNKNOWN_COMMAND	= 0x03,
	HPWMI_RET_UNKNOWN_CMDTYPE	= 0x04,
	HPWMI_RET_INVALID_PARAMETERS	= 0x05,
};

99 100 101 102 103 104 105 106 107 108
static const struct key_entry hp_wmi_keymap[] = {
	{ KE_KEY, 0x02,   { KEY_BRIGHTNESSUP } },
	{ KE_KEY, 0x03,   { KEY_BRIGHTNESSDOWN } },
	{ KE_KEY, 0x20e6, { KEY_PROG1 } },
	{ KE_KEY, 0x20e8, { KEY_MEDIA } },
	{ KE_KEY, 0x2142, { KEY_MEDIA } },
	{ KE_KEY, 0x213b, { KEY_INFO } },
	{ KE_KEY, 0x2169, { KEY_DIRECTION } },
	{ KE_KEY, 0x231b, { KEY_HELP } },
	{ KE_END, 0 }
109 110 111 112 113 114 115 116 117
};

static struct input_dev *hp_wmi_input_dev;
static struct platform_device *hp_wmi_platform_dev;

static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;

118
static const struct dev_pm_ops hp_wmi_pm_ops = {
F
Frans Pop 已提交
119 120 121 122
	.resume  = hp_wmi_resume_handler,
	.restore  = hp_wmi_resume_handler,
};

123 124
static struct platform_driver hp_wmi_driver = {
	.driver = {
F
Frans Pop 已提交
125 126 127
		.name = "hp-wmi",
		.owner = THIS_MODULE,
		.pm = &hp_wmi_pm_ops,
128 129 130 131 132
	},
	.probe = hp_wmi_bios_setup,
	.remove = hp_wmi_bios_remove,
};

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
/*
 * hp_wmi_perform_query
 *
 * query:	The commandtype -> What should be queried
 * write:	The command -> 0 read, 1 write, 3 ODM specific
 * buffer:	Buffer used as input and/or output
 * buffersize:	Size of buffer
 *
 * returns zero on success
 *         an HP WMI query specific error code (which is positive)
 *         -EINVAL if the query was not successful at all
 *         -EINVAL if the output buffer size exceeds buffersize
 *
 * Note: The buffersize must at least be the maximum of the input and output
 *       size. E.g. Battery info query (0x7) is defined to have 1 byte input
 *       and 128 byte output. The caller would do:
 *       buffer = kzalloc(128, GFP_KERNEL);
 *       ret = hp_wmi_perform_query(0x7, 0, buffer, 128)
 */
M
Matthew Garrett 已提交
152
static int hp_wmi_perform_query(int query, int write, u32 *buffer,
153
				int buffersize)
154 155 156 157 158 159 160
{
	struct bios_return bios_return;
	union acpi_object *obj;
	struct bios_args args = {
		.signature = 0x55434553,
		.command = write ? 0x2 : 0x1,
		.commandtype = query,
161
		.datasize = buffersize,
M
Matthew Garrett 已提交
162
		.data = *buffer,
163 164 165 166
	};
	struct acpi_buffer input = { sizeof(struct bios_args), &args };
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };

167
	wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
168 169 170

	obj = output.pointer;

T
Thomas Renninger 已提交
171
	if (!obj)
172
		return -EINVAL;
T
Thomas Renninger 已提交
173 174 175 176
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return -EINVAL;
	}
177 178

	bios_return = *((struct bios_return *)obj->buffer.pointer);
179

180 181 182 183 184 185 186 187 188
	if (bios_return.return_code) {
		if (bios_return.return_code != HPWMI_RET_UNKNOWN_CMDTYPE)
			printk(KERN_WARNING PREFIX "query 0x%x returned "
						   "error 0x%x\n",
			       query, bios_return.return_code);
		kfree(obj);
		return bios_return.return_code;
	}

M
Matthew Garrett 已提交
189
	memcpy(buffer, &bios_return.value, sizeof(bios_return.value));
190 191

	kfree(obj);
192
	return 0;
193 194 195 196
}

static int hp_wmi_display_state(void)
{
M
Matthew Garrett 已提交
197 198
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
199 200 201 202
				       sizeof(state));
	if (ret)
		return -EINVAL;
	return state;
203 204 205 206
}

static int hp_wmi_hddtemp_state(void)
{
M
Matthew Garrett 已提交
207 208
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
209 210 211 212
				       sizeof(state));
	if (ret)
		return -EINVAL;
	return state;
213 214 215 216
}

static int hp_wmi_als_state(void)
{
M
Matthew Garrett 已提交
217 218
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
219 220 221 222
				       sizeof(state));
	if (ret)
		return -EINVAL;
	return state;
223 224 225 226
}

static int hp_wmi_dock_state(void)
{
M
Matthew Garrett 已提交
227 228
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
229
				       sizeof(state));
230

231 232
	if (ret)
		return -EINVAL;
233

234
	return state & 0x1;
235 236
}

237
static int hp_wmi_tablet_state(void)
238
{
M
Matthew Garrett 已提交
239 240
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
241 242
				       sizeof(state));
	if (ret)
243 244
		return ret;

245
	return (state & 0x4) ? 1 : 0;
246 247
}

J
Johannes Berg 已提交
248
static int hp_wmi_set_block(void *data, bool blocked)
249
{
A
Alan Jenkins 已提交
250 251
	enum hp_wmi_radio r = (enum hp_wmi_radio) data;
	int query = BIT(r + 8) | ((!blocked) << r);
252
	int ret;
253

254
	ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
M
Matthew Garrett 已提交
255
				   &query, sizeof(query));
256 257 258
	if (ret)
		return -EINVAL;
	return 0;
259 260
}

J
Johannes Berg 已提交
261 262 263
static const struct rfkill_ops hp_wmi_rfkill_ops = {
	.set_block = hp_wmi_set_block,
};
264

A
Alan Jenkins 已提交
265
static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
266
{
M
Matthew Garrett 已提交
267
	int wireless = 0;
268 269
	int mask;
	hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
M
Matthew Garrett 已提交
270
			     &wireless, sizeof(wireless));
271 272 273
	/* TBD: Pass error */

	mask = 0x200 << (r * 8);
274

A
Alan Jenkins 已提交
275
	if (wireless & mask)
J
Johannes Berg 已提交
276
		return false;
277
	else
J
Johannes Berg 已提交
278
		return true;
279 280
}

A
Alan Jenkins 已提交
281
static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
282
{
M
Matthew Garrett 已提交
283
	int wireless = 0;
284 285
	int mask;
	hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
M
Matthew Garrett 已提交
286
			     &wireless, sizeof(wireless));
287 288 289
	/* TBD: Pass error */

	mask = 0x800 << (r * 8);
290

A
Alan Jenkins 已提交
291
	if (wireless & mask)
J
Johannes Berg 已提交
292
		return false;
293
	else
J
Johannes Berg 已提交
294
		return true;
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
}

static ssize_t show_display(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	int value = hp_wmi_display_state();
	if (value < 0)
		return -EINVAL;
	return sprintf(buf, "%d\n", value);
}

static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	int value = hp_wmi_hddtemp_state();
	if (value < 0)
		return -EINVAL;
	return sprintf(buf, "%d\n", value);
}

static ssize_t show_als(struct device *dev, struct device_attribute *attr,
			char *buf)
{
	int value = hp_wmi_als_state();
	if (value < 0)
		return -EINVAL;
	return sprintf(buf, "%d\n", value);
}

static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
			 char *buf)
{
	int value = hp_wmi_dock_state();
	if (value < 0)
		return -EINVAL;
	return sprintf(buf, "%d\n", value);
}

333 334 335 336 337 338 339 340 341
static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
			 char *buf)
{
	int value = hp_wmi_tablet_state();
	if (value < 0)
		return -EINVAL;
	return sprintf(buf, "%d\n", value);
}

342 343 344 345
static ssize_t set_als(struct device *dev, struct device_attribute *attr,
		       const char *buf, size_t count)
{
	u32 tmp = simple_strtoul(buf, NULL, 10);
M
Matthew Garrett 已提交
346
	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
347 348 349 350
				       sizeof(tmp));
	if (ret)
		return -EINVAL;

351 352 353 354 355 356 357
	return count;
}

static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
358
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
359

A
Adrian Bunk 已提交
360
static void hp_wmi_notify(u32 value, void *context)
361 362 363
{
	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
364
	u32 event_id, event_data;
M
Matthew Garrett 已提交
365
	int key_code = 0, ret;
366
	u32 *location;
367
	acpi_status status;
368

369 370
	status = wmi_get_event_data(value, &response);
	if (status != AE_OK) {
371
		printk(KERN_INFO PREFIX "bad event status 0x%x\n", status);
372 373
		return;
	}
374 375 376

	obj = (union acpi_object *)response.pointer;

377 378 379
	if (!obj)
		return;
	if (obj->type != ACPI_TYPE_BUFFER) {
380 381
		printk(KERN_INFO "hp-wmi: Unknown response received %d\n",
		       obj->type);
T
Thomas Renninger 已提交
382
		kfree(obj);
A
Alan Jenkins 已提交
383 384 385
		return;
	}

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
	/*
	 * Depending on ACPI version the concatenation of id and event data
	 * inside _WED function will result in a 8 or 16 byte buffer.
	 */
	location = (u32 *)obj->buffer.pointer;
	if (obj->buffer.length == 8) {
		event_id = *location;
		event_data = *(location + 1);
	} else if (obj->buffer.length == 16) {
		event_id = *location;
		event_data = *(location + 2);
	} else {
		printk(KERN_INFO "hp-wmi: Unknown buffer length %d\n",
		       obj->buffer.length);
		kfree(obj);
		return;
	}
T
Thomas Renninger 已提交
403
	kfree(obj);
404 405

	switch (event_id) {
406
	case HPWMI_DOCK_EVENT:
A
Alan Jenkins 已提交
407 408 409 410 411
		input_report_switch(hp_wmi_input_dev, SW_DOCK,
				    hp_wmi_dock_state());
		input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
				    hp_wmi_tablet_state());
		input_sync(hp_wmi_input_dev);
412
		break;
413 414 415 416
	case HPWMI_PARK_HDD:
		break;
	case HPWMI_SMART_ADAPTER:
		break;
417
	case HPWMI_BEZEL_BUTTON:
418
		ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
M
Matthew Garrett 已提交
419
					   &key_code,
420 421 422
					   sizeof(key_code));
		if (ret)
			break;
423 424 425

		if (!sparse_keymap_report_event(hp_wmi_input_dev,
						key_code, 1, true))
426
			printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
427
			       key_code);
428 429
		break;
	case HPWMI_WIRELESS:
A
Alan Jenkins 已提交
430 431 432 433 434 435 436 437 438 439 440 441
		if (wifi_rfkill)
			rfkill_set_states(wifi_rfkill,
					  hp_wmi_get_sw_state(HPWMI_WIFI),
					  hp_wmi_get_hw_state(HPWMI_WIFI));
		if (bluetooth_rfkill)
			rfkill_set_states(bluetooth_rfkill,
					  hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
					  hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
		if (wwan_rfkill)
			rfkill_set_states(wwan_rfkill,
					  hp_wmi_get_sw_state(HPWMI_WWAN),
					  hp_wmi_get_hw_state(HPWMI_WWAN));
442
		break;
443 444 445 446 447 448
	case HPWMI_CPU_BATTERY_THROTTLE:
		printk(KERN_INFO PREFIX UNIMP "CPU throttle because of 3 Cell"
		       " battery event detected\n");
		break;
	case HPWMI_LOCK_SWITCH:
		break;
449
	default:
450 451
		printk(KERN_INFO PREFIX "Unknown event_id - %d - 0x%x\n",
		       event_id, event_data);
452 453
		break;
	}
454 455 456 457
}

static int __init hp_wmi_input_setup(void)
{
458
	acpi_status status;
459 460 461
	int err;

	hp_wmi_input_dev = input_allocate_device();
462 463
	if (!hp_wmi_input_dev)
		return -ENOMEM;
464 465 466 467 468

	hp_wmi_input_dev->name = "HP WMI hotkeys";
	hp_wmi_input_dev->phys = "wmi/input0";
	hp_wmi_input_dev->id.bustype = BUS_HOST;

469 470 471 472 473 474 475
	__set_bit(EV_SW, hp_wmi_input_dev->evbit);
	__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
	__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);

	err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
	if (err)
		goto err_free_dev;
476 477 478 479 480 481 482

	/* Set initial hardware state */
	input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
	input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
			    hp_wmi_tablet_state());
	input_sync(hp_wmi_input_dev);

483 484 485 486
	status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
	if (ACPI_FAILURE(status)) {
		err = -EIO;
		goto err_free_keymap;
487 488
	}

489 490 491 492
	err = input_register_device(hp_wmi_input_dev);
	if (err)
		goto err_uninstall_notifier;

493
	return 0;
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508

 err_uninstall_notifier:
	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
 err_free_keymap:
	sparse_keymap_free(hp_wmi_input_dev);
 err_free_dev:
	input_free_device(hp_wmi_input_dev);
	return err;
}

static void hp_wmi_input_destroy(void)
{
	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
	sparse_keymap_free(hp_wmi_input_dev);
	input_unregister_device(hp_wmi_input_dev);
509 510 511 512 513 514 515 516
}

static void cleanup_sysfs(struct platform_device *device)
{
	device_remove_file(&device->dev, &dev_attr_display);
	device_remove_file(&device->dev, &dev_attr_hddtemp);
	device_remove_file(&device->dev, &dev_attr_als);
	device_remove_file(&device->dev, &dev_attr_dock);
517
	device_remove_file(&device->dev, &dev_attr_tablet);
518 519
}

520
static int __devinit hp_wmi_bios_setup(struct platform_device *device)
521 522
{
	int err;
M
Matthew Garrett 已提交
523
	int wireless = 0;
524

M
Matthew Garrett 已提交
525
	err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless,
526 527 528
				   sizeof(wireless));
	if (err)
		return err;
529 530 531 532 533 534 535 536 537 538 539

	err = device_create_file(&device->dev, &dev_attr_display);
	if (err)
		goto add_sysfs_error;
	err = device_create_file(&device->dev, &dev_attr_hddtemp);
	if (err)
		goto add_sysfs_error;
	err = device_create_file(&device->dev, &dev_attr_als);
	if (err)
		goto add_sysfs_error;
	err = device_create_file(&device->dev, &dev_attr_dock);
540 541 542
	if (err)
		goto add_sysfs_error;
	err = device_create_file(&device->dev, &dev_attr_tablet);
543 544 545
	if (err)
		goto add_sysfs_error;

546
	if (wireless & 0x1) {
J
Johannes Berg 已提交
547 548 549
		wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
					   RFKILL_TYPE_WLAN,
					   &hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
550 551 552 553 554
					   (void *) HPWMI_WIFI);
		rfkill_init_sw_state(wifi_rfkill,
				     hp_wmi_get_sw_state(HPWMI_WIFI));
		rfkill_set_hw_state(wifi_rfkill,
				    hp_wmi_get_hw_state(HPWMI_WIFI));
555 556
		err = rfkill_register(wifi_rfkill);
		if (err)
J
Johannes Berg 已提交
557
			goto register_wifi_error;
558 559 560
	}

	if (wireless & 0x2) {
J
Johannes Berg 已提交
561 562 563
		bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
						RFKILL_TYPE_BLUETOOTH,
						&hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
564 565 566 567 568
						(void *) HPWMI_BLUETOOTH);
		rfkill_init_sw_state(bluetooth_rfkill,
				     hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
		rfkill_set_hw_state(bluetooth_rfkill,
				    hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
569
		err = rfkill_register(bluetooth_rfkill);
570
		if (err)
571
			goto register_bluetooth_error;
572 573 574
	}

	if (wireless & 0x4) {
J
Johannes Berg 已提交
575 576 577
		wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
					   RFKILL_TYPE_WWAN,
					   &hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
578 579 580 581 582
					   (void *) HPWMI_WWAN);
		rfkill_init_sw_state(wwan_rfkill,
				     hp_wmi_get_sw_state(HPWMI_WWAN));
		rfkill_set_hw_state(wwan_rfkill,
				    hp_wmi_get_hw_state(HPWMI_WWAN));
583 584 585
		err = rfkill_register(wwan_rfkill);
		if (err)
			goto register_wwan_err;
586
	}
587 588

	return 0;
589
register_wwan_err:
J
Johannes Berg 已提交
590
	rfkill_destroy(wwan_rfkill);
591 592
	if (bluetooth_rfkill)
		rfkill_unregister(bluetooth_rfkill);
593
register_bluetooth_error:
J
Johannes Berg 已提交
594
	rfkill_destroy(bluetooth_rfkill);
595 596
	if (wifi_rfkill)
		rfkill_unregister(wifi_rfkill);
J
Johannes Berg 已提交
597 598
register_wifi_error:
	rfkill_destroy(wifi_rfkill);
599 600 601 602 603 604 605 606 607
add_sysfs_error:
	cleanup_sysfs(device);
	return err;
}

static int __exit hp_wmi_bios_remove(struct platform_device *device)
{
	cleanup_sysfs(device);

J
Johannes Berg 已提交
608
	if (wifi_rfkill) {
609
		rfkill_unregister(wifi_rfkill);
J
Johannes Berg 已提交
610 611 612
		rfkill_destroy(wifi_rfkill);
	}
	if (bluetooth_rfkill) {
613
		rfkill_unregister(bluetooth_rfkill);
614
		rfkill_destroy(bluetooth_rfkill);
J
Johannes Berg 已提交
615 616
	}
	if (wwan_rfkill) {
617
		rfkill_unregister(wwan_rfkill);
J
Johannes Berg 已提交
618 619
		rfkill_destroy(wwan_rfkill);
	}
620 621 622 623

	return 0;
}

F
Frans Pop 已提交
624
static int hp_wmi_resume_handler(struct device *device)
625 626
{
	/*
627 628
	 * Hardware state may have changed while suspended, so trigger
	 * input events for the current state. As this is a switch,
629 630 631
	 * the input layer will only actually pass it on if the state
	 * changed.
	 */
632 633 634 635 636 637 638
	if (hp_wmi_input_dev) {
		input_report_switch(hp_wmi_input_dev, SW_DOCK,
				    hp_wmi_dock_state());
		input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
				    hp_wmi_tablet_state());
		input_sync(hp_wmi_input_dev);
	}
639

A
Alan Jenkins 已提交
640 641 642 643 644 645 646 647 648 649 650 651 652
	if (wifi_rfkill)
		rfkill_set_states(wifi_rfkill,
				  hp_wmi_get_sw_state(HPWMI_WIFI),
				  hp_wmi_get_hw_state(HPWMI_WIFI));
	if (bluetooth_rfkill)
		rfkill_set_states(bluetooth_rfkill,
				  hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
				  hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
	if (wwan_rfkill)
		rfkill_set_states(wwan_rfkill,
				  hp_wmi_get_sw_state(HPWMI_WWAN),
				  hp_wmi_get_hw_state(HPWMI_WWAN));

653 654 655
	return 0;
}

656 657 658
static int __init hp_wmi_init(void)
{
	int err;
659 660
	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
661

662
	if (event_capable) {
663
		err = hp_wmi_input_setup();
664
		if (err)
665
			return err;
666 667
	}

668
	if (bios_capable) {
669 670
		err = platform_driver_register(&hp_wmi_driver);
		if (err)
671
			goto err_driver_reg;
672 673
		hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
		if (!hp_wmi_platform_dev) {
674 675
			err = -ENOMEM;
			goto err_device_alloc;
676
		}
677 678 679
		err = platform_device_add(hp_wmi_platform_dev);
		if (err)
			goto err_device_add;
680 681
	}

682 683 684
	if (!bios_capable && !event_capable)
		return -ENODEV;

685
	return 0;
686 687 688 689 690 691

err_device_add:
	platform_device_put(hp_wmi_platform_dev);
err_device_alloc:
	platform_driver_unregister(&hp_wmi_driver);
err_driver_reg:
692 693
	if (event_capable)
		hp_wmi_input_destroy();
694 695

	return err;
696 697 698 699
}

static void __exit hp_wmi_exit(void)
{
700 701 702
	if (wmi_has_guid(HPWMI_EVENT_GUID))
		hp_wmi_input_destroy();

703
	if (hp_wmi_platform_dev) {
704
		platform_device_unregister(hp_wmi_platform_dev);
705 706 707 708 709 710
		platform_driver_unregister(&hp_wmi_driver);
	}
}

module_init(hp_wmi_init);
module_exit(hp_wmi_exit);