hp-wmi.c 22.1 KB
Newer Older
1 2 3 4
/*
 * HP WMI hotkeys
 *
 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5
 * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
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
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
32
#include <linux/slab.h>
33 34
#include <linux/types.h>
#include <linux/input.h>
35
#include <linux/input/sparse-keymap.h>
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
#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
54
#define HPWMI_HARDWARE_QUERY 0x4
55
#define HPWMI_WIRELESS_QUERY 0x5
56
#define HPWMI_HOTKEY_QUERY 0xc
57
#define HPWMI_WIRELESS2_QUERY 0x1b
58

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

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

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

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

struct bios_return {
	u32 sigpass;
	u32 return_code;
};

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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
enum hp_wireless2_bits {
	HPWMI_POWER_STATE	= 0x01,
	HPWMI_POWER_SOFT	= 0x02,
	HPWMI_POWER_BIOS	= 0x04,
	HPWMI_POWER_HARD	= 0x08,
};

#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
			 != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)

struct bios_rfkill2_device_state {
	u8 radio_type;
	u8 bus_type;
	u16 vendor_id;
	u16 product_id;
	u16 subsys_vendor_id;
	u16 subsys_product_id;
	u8 rfkill_id;
	u8 power;
	u8 unknown[4];
};

/* 7 devices fit into the 128 byte buffer */
#define HPWMI_MAX_RFKILL2_DEVICES	7

struct bios_rfkill2_state {
	u8 unknown[7];
	u8 count;
	u8 pad[8];
	struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
};

132 133 134 135 136 137 138 139 140 141
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 }
142 143 144 145 146 147 148 149 150
};

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;

151 152 153 154 155 156 157 158 159
struct rfkill2_device {
	u8 id;
	int num;
	struct rfkill *rfkill;
};

static int rfkill2_count;
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];

160
static const struct dev_pm_ops hp_wmi_pm_ops = {
F
Frans Pop 已提交
161 162 163 164
	.resume  = hp_wmi_resume_handler,
	.restore  = hp_wmi_resume_handler,
};

165 166
static struct platform_driver hp_wmi_driver = {
	.driver = {
F
Frans Pop 已提交
167 168 169
		.name = "hp-wmi",
		.owner = THIS_MODULE,
		.pm = &hp_wmi_pm_ops,
170 171 172 173 174
	},
	.probe = hp_wmi_bios_setup,
	.remove = hp_wmi_bios_remove,
};

175 176 177 178 179 180
/*
 * 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
181 182
 * insize:	Size of input buffer
 * outsize:	Size of output buffer
183 184 185 186 187 188 189 190 191 192
 *
 * 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);
193
 *       ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128)
194
 */
195 196
static int hp_wmi_perform_query(int query, int write, void *buffer,
				int insize, int outsize)
197
{
198 199
	struct bios_return *bios_return;
	int actual_outsize;
200 201 202 203 204
	union acpi_object *obj;
	struct bios_args args = {
		.signature = 0x55434553,
		.command = write ? 0x2 : 0x1,
		.commandtype = query,
205 206
		.datasize = insize,
		.data = 0,
207 208 209
	};
	struct acpi_buffer input = { sizeof(struct bios_args), &args };
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
E
Eric Dumazet 已提交
210
	u32 rc;
211

212 213 214 215
	if (WARN_ON(insize > sizeof(args.data)))
		return -EINVAL;
	memcpy(&args.data, buffer, insize);

216
	wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
217 218 219

	obj = output.pointer;

T
Thomas Renninger 已提交
220
	if (!obj)
221
		return -EINVAL;
T
Thomas Renninger 已提交
222 223 224 225
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return -EINVAL;
	}
226

227
	bios_return = (struct bios_return *)obj->buffer.pointer;
E
Eric Dumazet 已提交
228
	rc = bios_return->return_code;
229

E
Eric Dumazet 已提交
230 231 232
	if (rc) {
		if (rc != HPWMI_RET_UNKNOWN_CMDTYPE)
			pr_warn("query 0x%x returned error 0x%x\n", query, rc);
233
		kfree(obj);
E
Eric Dumazet 已提交
234
		return rc;
235 236
	}

237 238 239 240 241
	if (!outsize) {
		/* ignore output data */
		kfree(obj);
		return 0;
	}
242

243 244 245
	actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return)));
	memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
	memset(buffer + actual_outsize, 0, outsize - actual_outsize);
246
	kfree(obj);
247
	return 0;
248 249 250 251
}

static int hp_wmi_display_state(void)
{
M
Matthew Garrett 已提交
252 253
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
254
				       sizeof(state), sizeof(state));
255 256 257
	if (ret)
		return -EINVAL;
	return state;
258 259 260 261
}

static int hp_wmi_hddtemp_state(void)
{
M
Matthew Garrett 已提交
262 263
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
264
				       sizeof(state), sizeof(state));
265 266 267
	if (ret)
		return -EINVAL;
	return state;
268 269 270 271
}

static int hp_wmi_als_state(void)
{
M
Matthew Garrett 已提交
272 273
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
274
				       sizeof(state), sizeof(state));
275 276 277
	if (ret)
		return -EINVAL;
	return state;
278 279 280 281
}

static int hp_wmi_dock_state(void)
{
M
Matthew Garrett 已提交
282 283
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
284
				       sizeof(state), sizeof(state));
285

286 287
	if (ret)
		return -EINVAL;
288

289
	return state & 0x1;
290 291
}

292
static int hp_wmi_tablet_state(void)
293
{
M
Matthew Garrett 已提交
294 295
	int state = 0;
	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
296
				       sizeof(state), sizeof(state));
297
	if (ret)
298 299
		return ret;

300
	return (state & 0x4) ? 1 : 0;
301 302
}

J
Johannes Berg 已提交
303
static int hp_wmi_set_block(void *data, bool blocked)
304
{
A
Alan Jenkins 已提交
305 306
	enum hp_wmi_radio r = (enum hp_wmi_radio) data;
	int query = BIT(r + 8) | ((!blocked) << r);
307
	int ret;
308

309
	ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
310
				   &query, sizeof(query), 0);
311 312 313
	if (ret)
		return -EINVAL;
	return 0;
314 315
}

J
Johannes Berg 已提交
316 317 318
static const struct rfkill_ops hp_wmi_rfkill_ops = {
	.set_block = hp_wmi_set_block,
};
319

A
Alan Jenkins 已提交
320
static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
321
{
M
Matthew Garrett 已提交
322
	int wireless = 0;
323 324
	int mask;
	hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
325 326
			     &wireless, sizeof(wireless),
			     sizeof(wireless));
327 328 329
	/* TBD: Pass error */

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

A
Alan Jenkins 已提交
331
	if (wireless & mask)
J
Johannes Berg 已提交
332
		return false;
333
	else
J
Johannes Berg 已提交
334
		return true;
335 336
}

A
Alan Jenkins 已提交
337
static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
338
{
M
Matthew Garrett 已提交
339
	int wireless = 0;
340 341
	int mask;
	hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
342 343
			     &wireless, sizeof(wireless),
			     sizeof(wireless));
344 345 346
	/* TBD: Pass error */

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

A
Alan Jenkins 已提交
348
	if (wireless & mask)
J
Johannes Berg 已提交
349
		return false;
350
	else
J
Johannes Berg 已提交
351
		return true;
352 353
}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
static int hp_wmi_rfkill2_set_block(void *data, bool blocked)
{
	int rfkill_id = (int)(long)data;
	char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked };

	if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
				   buffer, sizeof(buffer), 0))
		return -EINVAL;
	return 0;
}

static const struct rfkill_ops hp_wmi_rfkill2_ops = {
	.set_block = hp_wmi_rfkill2_set_block,
};

static int hp_wmi_rfkill2_refresh(void)
{
	int err, i;
	struct bios_rfkill2_state state;

	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
				   0, sizeof(state));
	if (err)
		return err;

	for (i = 0; i < rfkill2_count; i++) {
		int num = rfkill2[i].num;
		struct bios_rfkill2_device_state *devstate;
		devstate = &state.device[num];

		if (num >= state.count ||
		    devstate->rfkill_id != rfkill2[i].id) {
386
			pr_warn("power configuration of the wireless devices unexpectedly changed\n");
387 388 389 390 391 392 393 394 395 396 397
			continue;
		}

		rfkill_set_states(rfkill2[i].rfkill,
				  IS_SWBLOCKED(devstate->power),
				  IS_HWBLOCKED(devstate->power));
	}

	return 0;
}

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
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);
}

434 435 436 437 438 439 440 441 442
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);
}

443 444 445 446
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 已提交
447
	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
448
				       sizeof(tmp), sizeof(tmp));
449 450 451
	if (ret)
		return -EINVAL;

452 453 454 455 456 457 458
	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);
459
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
460

A
Adrian Bunk 已提交
461
static void hp_wmi_notify(u32 value, void *context)
462 463 464
{
	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
465
	u32 event_id, event_data;
M
Matthew Garrett 已提交
466
	int key_code = 0, ret;
467
	u32 *location;
468
	acpi_status status;
469

470 471
	status = wmi_get_event_data(value, &response);
	if (status != AE_OK) {
472
		pr_info("bad event status 0x%x\n", status);
473 474
		return;
	}
475 476 477

	obj = (union acpi_object *)response.pointer;

478 479 480
	if (!obj)
		return;
	if (obj->type != ACPI_TYPE_BUFFER) {
481
		pr_info("Unknown response received %d\n", obj->type);
T
Thomas Renninger 已提交
482
		kfree(obj);
A
Alan Jenkins 已提交
483 484 485
		return;
	}

486 487 488 489 490 491 492 493 494 495 496 497
	/*
	 * 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 {
498
		pr_info("Unknown buffer length %d\n", obj->buffer.length);
499 500 501
		kfree(obj);
		return;
	}
T
Thomas Renninger 已提交
502
	kfree(obj);
503 504

	switch (event_id) {
505
	case HPWMI_DOCK_EVENT:
A
Alan Jenkins 已提交
506 507 508 509 510
		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);
511
		break;
512 513 514 515
	case HPWMI_PARK_HDD:
		break;
	case HPWMI_SMART_ADAPTER:
		break;
516
	case HPWMI_BEZEL_BUTTON:
517
		ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
M
Matthew Garrett 已提交
518
					   &key_code,
519
					   sizeof(key_code),
520 521 522
					   sizeof(key_code));
		if (ret)
			break;
523 524 525

		if (!sparse_keymap_report_event(hp_wmi_input_dev,
						key_code, 1, true))
526
			pr_info("Unknown key code - 0x%x\n", key_code);
527 528
		break;
	case HPWMI_WIRELESS:
529 530 531 532 533
		if (rfkill2_count) {
			hp_wmi_rfkill2_refresh();
			break;
		}

A
Alan Jenkins 已提交
534 535 536 537 538 539 540 541 542 543 544 545
		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));
546
		break;
547
	case HPWMI_CPU_BATTERY_THROTTLE:
548
		pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
549 550 551
		break;
	case HPWMI_LOCK_SWITCH:
		break;
552
	default:
553
		pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
554 555
		break;
	}
556 557 558 559
}

static int __init hp_wmi_input_setup(void)
{
560
	acpi_status status;
561 562 563
	int err;

	hp_wmi_input_dev = input_allocate_device();
564 565
	if (!hp_wmi_input_dev)
		return -ENOMEM;
566 567 568 569 570

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

571 572 573 574 575 576 577
	__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;
578 579 580 581 582 583 584

	/* 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);

585 586 587 588
	status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
	if (ACPI_FAILURE(status)) {
		err = -EIO;
		goto err_free_keymap;
589 590
	}

591 592 593 594
	err = input_register_device(hp_wmi_input_dev);
	if (err)
		goto err_uninstall_notifier;

595
	return 0;
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610

 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);
611 612 613 614 615 616 617 618
}

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);
619
	device_remove_file(&device->dev, &dev_attr_tablet);
620 621
}

622
static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
623 624
{
	int err;
M
Matthew Garrett 已提交
625
	int wireless = 0;
626

M
Matthew Garrett 已提交
627
	err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless,
628
				   sizeof(wireless), sizeof(wireless));
629 630
	if (err)
		return err;
631

632
	if (wireless & 0x1) {
J
Johannes Berg 已提交
633 634 635
		wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
					   RFKILL_TYPE_WLAN,
					   &hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
636 637 638 639 640
					   (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));
641 642
		err = rfkill_register(wifi_rfkill);
		if (err)
J
Johannes Berg 已提交
643
			goto register_wifi_error;
644 645 646
	}

	if (wireless & 0x2) {
J
Johannes Berg 已提交
647 648 649
		bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
						RFKILL_TYPE_BLUETOOTH,
						&hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
650 651 652 653 654
						(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));
655
		err = rfkill_register(bluetooth_rfkill);
656
		if (err)
657
			goto register_bluetooth_error;
658 659 660
	}

	if (wireless & 0x4) {
J
Johannes Berg 已提交
661 662 663
		wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
					   RFKILL_TYPE_WWAN,
					   &hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
664 665 666 667 668
					   (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));
669 670 671
		err = rfkill_register(wwan_rfkill);
		if (err)
			goto register_wwan_err;
672
	}
673 674

	return 0;
675
register_wwan_err:
J
Johannes Berg 已提交
676
	rfkill_destroy(wwan_rfkill);
677
	wwan_rfkill = NULL;
678 679
	if (bluetooth_rfkill)
		rfkill_unregister(bluetooth_rfkill);
680
register_bluetooth_error:
J
Johannes Berg 已提交
681
	rfkill_destroy(bluetooth_rfkill);
682
	bluetooth_rfkill = NULL;
683 684
	if (wifi_rfkill)
		rfkill_unregister(wifi_rfkill);
J
Johannes Berg 已提交
685 686
register_wifi_error:
	rfkill_destroy(wifi_rfkill);
687
	wifi_rfkill = NULL;
688 689 690
	return err;
}

691 692 693 694 695 696 697 698 699 700
static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
{
	int err, i;
	struct bios_rfkill2_state state;
	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
				   0, sizeof(state));
	if (err)
		return err;

	if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
701
		pr_warn("unable to parse 0x1b query output\n");
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
		return -EINVAL;
	}

	for (i = 0; i < state.count; i++) {
		struct rfkill *rfkill;
		enum rfkill_type type;
		char *name;
		switch (state.device[i].radio_type) {
		case HPWMI_WIFI:
			type = RFKILL_TYPE_WLAN;
			name = "hp-wifi";
			break;
		case HPWMI_BLUETOOTH:
			type = RFKILL_TYPE_BLUETOOTH;
			name = "hp-bluetooth";
			break;
		case HPWMI_WWAN:
			type = RFKILL_TYPE_WWAN;
			name = "hp-wwan";
			break;
		default:
723 724
			pr_warn("unknown device type 0x%x\n",
				state.device[i].radio_type);
725 726 727 728
			continue;
		}

		if (!state.device[i].vendor_id) {
729 730
			pr_warn("zero device %d while %d reported\n",
				i, state.count);
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
			continue;
		}

		rfkill = rfkill_alloc(name, &device->dev, type,
				      &hp_wmi_rfkill2_ops, (void *)(long)i);
		if (!rfkill) {
			err = -ENOMEM;
			goto fail;
		}

		rfkill2[rfkill2_count].id = state.device[i].rfkill_id;
		rfkill2[rfkill2_count].num = i;
		rfkill2[rfkill2_count].rfkill = rfkill;

		rfkill_init_sw_state(rfkill,
				     IS_SWBLOCKED(state.device[i].power));
		rfkill_set_hw_state(rfkill,
				    IS_HWBLOCKED(state.device[i].power));

		if (!(state.device[i].power & HPWMI_POWER_BIOS))
751
			pr_info("device %s blocked by BIOS\n", name);
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770

		err = rfkill_register(rfkill);
		if (err) {
			rfkill_destroy(rfkill);
			goto fail;
		}

		rfkill2_count++;
	}

	return 0;
fail:
	for (; rfkill2_count > 0; rfkill2_count--) {
		rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill);
		rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill);
	}
	return err;
}

771 772 773 774
static int __devinit hp_wmi_bios_setup(struct platform_device *device)
{
	int err;

775 776 777 778
	/* clear detected rfkill devices */
	wifi_rfkill = NULL;
	bluetooth_rfkill = NULL;
	wwan_rfkill = NULL;
779
	rfkill2_count = 0;
780

781 782
	if (hp_wmi_rfkill_setup(device))
		hp_wmi_rfkill2_setup(device);
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800

	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);
	if (err)
		goto add_sysfs_error;
	err = device_create_file(&device->dev, &dev_attr_tablet);
	if (err)
		goto add_sysfs_error;
	return 0;

801 802 803 804 805 806 807
add_sysfs_error:
	cleanup_sysfs(device);
	return err;
}

static int __exit hp_wmi_bios_remove(struct platform_device *device)
{
808
	int i;
809 810
	cleanup_sysfs(device);

811 812 813 814 815
	for (i = 0; i < rfkill2_count; i++) {
		rfkill_unregister(rfkill2[i].rfkill);
		rfkill_destroy(rfkill2[i].rfkill);
	}

J
Johannes Berg 已提交
816
	if (wifi_rfkill) {
817
		rfkill_unregister(wifi_rfkill);
J
Johannes Berg 已提交
818 819 820
		rfkill_destroy(wifi_rfkill);
	}
	if (bluetooth_rfkill) {
821
		rfkill_unregister(bluetooth_rfkill);
822
		rfkill_destroy(bluetooth_rfkill);
J
Johannes Berg 已提交
823 824
	}
	if (wwan_rfkill) {
825
		rfkill_unregister(wwan_rfkill);
J
Johannes Berg 已提交
826 827
		rfkill_destroy(wwan_rfkill);
	}
828 829 830 831

	return 0;
}

F
Frans Pop 已提交
832
static int hp_wmi_resume_handler(struct device *device)
833 834
{
	/*
835 836
	 * Hardware state may have changed while suspended, so trigger
	 * input events for the current state. As this is a switch,
837 838 839
	 * the input layer will only actually pass it on if the state
	 * changed.
	 */
840 841 842 843 844 845 846
	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);
	}
847

848 849 850
	if (rfkill2_count)
		hp_wmi_rfkill2_refresh();

A
Alan Jenkins 已提交
851 852 853 854 855 856 857 858 859 860 861 862 863
	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));

864 865 866
	return 0;
}

867 868 869
static int __init hp_wmi_init(void)
{
	int err;
870 871
	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
872

873
	if (event_capable) {
874
		err = hp_wmi_input_setup();
875
		if (err)
876
			return err;
877 878
	}

879
	if (bios_capable) {
880 881
		err = platform_driver_register(&hp_wmi_driver);
		if (err)
882
			goto err_driver_reg;
883 884
		hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
		if (!hp_wmi_platform_dev) {
885 886
			err = -ENOMEM;
			goto err_device_alloc;
887
		}
888 889 890
		err = platform_device_add(hp_wmi_platform_dev);
		if (err)
			goto err_device_add;
891 892
	}

893 894 895
	if (!bios_capable && !event_capable)
		return -ENODEV;

896
	return 0;
897 898 899 900 901 902

err_device_add:
	platform_device_put(hp_wmi_platform_dev);
err_device_alloc:
	platform_driver_unregister(&hp_wmi_driver);
err_driver_reg:
903 904
	if (event_capable)
		hp_wmi_input_destroy();
905 906

	return err;
907 908 909 910
}

static void __exit hp_wmi_exit(void)
{
911 912 913
	if (wmi_has_guid(HPWMI_EVENT_GUID))
		hp_wmi_input_destroy();

914
	if (hp_wmi_platform_dev) {
915
		platform_device_unregister(hp_wmi_platform_dev);
916 917 918 919 920 921
		platform_driver_unregister(&hp_wmi_driver);
	}
}

module_init(hp_wmi_init);
module_exit(hp_wmi_exit);