hp-wmi.c 25.8 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
#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"

A
Alan Jenkins 已提交
51
enum hp_wmi_radio {
52 53 54 55
	HPWMI_WIFI	= 0x0,
	HPWMI_BLUETOOTH	= 0x1,
	HPWMI_WWAN	= 0x2,
	HPWMI_GPS	= 0x3,
A
Alan Jenkins 已提交
56 57
};

58
enum hp_wmi_event_ids {
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	HPWMI_DOCK_EVENT		= 0x01,
	HPWMI_PARK_HDD			= 0x02,
	HPWMI_SMART_ADAPTER		= 0x03,
	HPWMI_BEZEL_BUTTON		= 0x04,
	HPWMI_WIRELESS			= 0x05,
	HPWMI_CPU_BATTERY_THROTTLE	= 0x06,
	HPWMI_LOCK_SWITCH		= 0x07,
	HPWMI_LID_SWITCH		= 0x08,
	HPWMI_SCREEN_ROTATION		= 0x09,
	HPWMI_COOLSENSE_SYSTEM_MOBILE	= 0x0A,
	HPWMI_COOLSENSE_SYSTEM_HOT	= 0x0B,
	HPWMI_PROXIMITY_SENSOR		= 0x0C,
	HPWMI_BACKLIT_KB_BRIGHTNESS	= 0x0D,
	HPWMI_PEAKSHIFT_PERIOD		= 0x0F,
	HPWMI_BATTERY_CHARGE_PERIOD	= 0x10,
74 75
};

76 77 78 79 80
struct bios_args {
	u32 signature;
	u32 command;
	u32 commandtype;
	u32 datasize;
M
Matthew Garrett 已提交
81
	u32 data;
82 83
};

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
enum hp_wmi_commandtype {
	HPWMI_DISPLAY_QUERY		= 0x01,
	HPWMI_HDDTEMP_QUERY		= 0x02,
	HPWMI_ALS_QUERY			= 0x03,
	HPWMI_HARDWARE_QUERY		= 0x04,
	HPWMI_WIRELESS_QUERY		= 0x05,
	HPWMI_BATTERY_QUERY		= 0x07,
	HPWMI_BIOS_QUERY		= 0x09,
	HPWMI_FEATURE_QUERY		= 0x0b,
	HPWMI_HOTKEY_QUERY		= 0x0c,
	HPWMI_FEATURE2_QUERY		= 0x0d,
	HPWMI_WIRELESS2_QUERY		= 0x1b,
	HPWMI_POSTCODEERROR_QUERY	= 0x2a,
};

enum hp_wmi_command {
	HPWMI_READ	= 0x01,
	HPWMI_WRITE	= 0x02,
	HPWMI_ODM	= 0x03,
};

#define BIOS_ARGS_INIT(write, ctype, size)				\
	(struct bios_args)	{	.signature = 0x55434553,	\
					.command = (write) ? 0x2 : 0x1,	\
					.commandtype = (ctype),		\
					.datasize = (size),		\
					.data = 0 }

112 113 114 115 116
struct bios_return {
	u32 sigpass;
	u32 return_code;
};

117 118 119 120 121 122 123
enum hp_return_value {
	HPWMI_RET_WRONG_SIGNATURE	= 0x02,
	HPWMI_RET_UNKNOWN_COMMAND	= 0x03,
	HPWMI_RET_UNKNOWN_CMDTYPE	= 0x04,
	HPWMI_RET_INVALID_PARAMETERS	= 0x05,
};

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
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];
};

157 158 159 160 161 162 163
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 } },
164
	{ KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
165
	{ KE_KEY, 0x216a, { KEY_SETUP } },
166 167
	{ KE_KEY, 0x231b, { KEY_HELP } },
	{ KE_END, 0 }
168 169 170 171 172 173 174 175 176
};

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;

177 178 179 180 181 182 183 184 185
struct rfkill2_device {
	u8 id;
	int num;
	struct rfkill *rfkill;
};

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

186 187 188
/*
 * hp_wmi_perform_query
 *
189 190
 * query:	The commandtype (enum hp_wmi_commandtype)
 * write:	The command (enum hp_wmi_command)
191
 * buffer:	Buffer used as input and/or output
192 193
 * insize:	Size of input buffer
 * outsize:	Size of output buffer
194 195 196 197 198 199 200
 *
 * 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
201
 *       size. E.g. Battery info query is defined to have 1 byte input
202 203
 *       and 128 byte output. The caller would do:
 *       buffer = kzalloc(128, GFP_KERNEL);
204
 *       ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128)
205
 */
206 207
static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
				void *buffer, int insize, int outsize)
208
{
209 210
	struct bios_return *bios_return;
	int actual_outsize;
211 212 213
	union acpi_object *obj;
	struct bios_args args = {
		.signature = 0x55434553,
214
		.command = command,
215
		.commandtype = query,
216 217
		.datasize = insize,
		.data = 0,
218 219 220
	};
	struct acpi_buffer input = { sizeof(struct bios_args), &args };
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
E
Eric Dumazet 已提交
221
	u32 rc;
222

223 224 225 226
	if (WARN_ON(insize > sizeof(args.data)))
		return -EINVAL;
	memcpy(&args.data, buffer, insize);

227
	wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
228 229 230

	obj = output.pointer;

T
Thomas Renninger 已提交
231
	if (!obj)
232
		return -EINVAL;
T
Thomas Renninger 已提交
233 234 235 236
	else if (obj->type != ACPI_TYPE_BUFFER) {
		kfree(obj);
		return -EINVAL;
	}
237

238
	bios_return = (struct bios_return *)obj->buffer.pointer;
E
Eric Dumazet 已提交
239
	rc = bios_return->return_code;
240

E
Eric Dumazet 已提交
241 242 243
	if (rc) {
		if (rc != HPWMI_RET_UNKNOWN_CMDTYPE)
			pr_warn("query 0x%x returned error 0x%x\n", query, rc);
244
		kfree(obj);
E
Eric Dumazet 已提交
245
		return rc;
246 247
	}

248 249 250 251 252
	if (!outsize) {
		/* ignore output data */
		kfree(obj);
		return 0;
	}
253

254 255 256
	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);
257
	kfree(obj);
258
	return 0;
259 260 261 262
}

static int hp_wmi_display_state(void)
{
M
Matthew Garrett 已提交
263
	int state = 0;
264
	int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, HPWMI_READ, &state,
265
				       sizeof(state), sizeof(state));
266
	if (ret)
267
		return ret < 0 ? ret : -EINVAL;
268
	return state;
269 270 271 272
}

static int hp_wmi_hddtemp_state(void)
{
M
Matthew Garrett 已提交
273
	int state = 0;
274
	int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, HPWMI_READ, &state,
275
				       sizeof(state), sizeof(state));
276
	if (ret)
277
		return ret < 0 ? ret : -EINVAL;
278
	return state;
279 280 281 282
}

static int hp_wmi_als_state(void)
{
M
Matthew Garrett 已提交
283
	int state = 0;
284
	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_READ, &state,
285
				       sizeof(state), sizeof(state));
286
	if (ret)
287
		return ret < 0 ? ret : -EINVAL;
288
	return state;
289 290 291 292
}

static int hp_wmi_dock_state(void)
{
M
Matthew Garrett 已提交
293
	int state = 0;
294
	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &state,
295
				       sizeof(state), sizeof(state));
296

297
	if (ret)
298
		return ret < 0 ? ret : -EINVAL;
299

300
	return state & 0x1;
301 302
}

303
static int hp_wmi_tablet_state(void)
304
{
M
Matthew Garrett 已提交
305
	int state = 0;
306
	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &state,
307
				       sizeof(state), sizeof(state));
308
	if (ret)
309
		return ret < 0 ? ret : -EINVAL;
310

311
	return (state & 0x4) ? 1 : 0;
312 313
}

K
Kyle Evans 已提交
314
static int __init hp_wmi_bios_2008_later(void)
315 316
{
	int state = 0;
317
	int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state,
318
				       sizeof(state), sizeof(state));
K
Kyle Evans 已提交
319 320
	if (!ret)
		return 1;
321

K
Kyle Evans 已提交
322
	return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO;
323 324
}

K
Kyle Evans 已提交
325
static int __init hp_wmi_bios_2009_later(void)
326
{
K
Kyle Evans 已提交
327
	int state = 0;
328
	int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state,
K
Kyle Evans 已提交
329 330 331
				       sizeof(state), sizeof(state));
	if (!ret)
		return 1;
332

K
Kyle Evans 已提交
333 334
	return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO;
}
335

K
Kyle Evans 已提交
336 337 338
static int __init hp_wmi_enable_hotkeys(void)
{
	int value = 0x6e;
339
	int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value,
K
Kyle Evans 已提交
340
				       sizeof(value), 0);
341
	if (ret)
342
		return ret < 0 ? ret : -EINVAL;
343 344 345
	return 0;
}

J
Johannes Berg 已提交
346
static int hp_wmi_set_block(void *data, bool blocked)
347
{
A
Alan Jenkins 已提交
348 349
	enum hp_wmi_radio r = (enum hp_wmi_radio) data;
	int query = BIT(r + 8) | ((!blocked) << r);
350
	int ret;
351

352
	ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE,
353
				   &query, sizeof(query), 0);
354
	if (ret)
355
		return ret < 0 ? ret : -EINVAL;
356
	return 0;
357 358
}

J
Johannes Berg 已提交
359 360 361
static const struct rfkill_ops hp_wmi_rfkill_ops = {
	.set_block = hp_wmi_set_block,
};
362

A
Alan Jenkins 已提交
363
static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
364
{
365
	int mask = 0x200 << (r * 8);
M
Matthew Garrett 已提交
366
	int wireless = 0;
367

368
	hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_READ,
369 370
			     &wireless, sizeof(wireless),
			     sizeof(wireless));
371 372
	/* TBD: Pass error */

A
Alan Jenkins 已提交
373
	if (wireless & mask)
J
Johannes Berg 已提交
374
		return false;
375
	else
J
Johannes Berg 已提交
376
		return true;
377 378
}

A
Alan Jenkins 已提交
379
static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
380
{
381
	int mask = 0x800 << (r * 8);
M
Matthew Garrett 已提交
382
	int wireless = 0;
383

384
	hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_READ,
385 386
			     &wireless, sizeof(wireless),
			     sizeof(wireless));
387 388
	/* TBD: Pass error */

A
Alan Jenkins 已提交
389
	if (wireless & mask)
J
Johannes Berg 已提交
390
		return false;
391
	else
J
Johannes Berg 已提交
392
		return true;
393 394
}

395 396 397 398 399
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 };

400
	if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE,
401 402 403 404 405 406 407 408 409 410 411 412
				   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)
{
	struct bios_rfkill2_state state;
413
	int err, i;
414

415
	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
416 417 418 419 420 421 422 423 424 425 426
				   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) {
427
			pr_warn("power configuration of the wireless devices unexpectedly changed\n");
428 429 430 431 432 433 434 435 436 437 438
			continue;
		}

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

	return 0;
}

439 440 441
static int hp_wmi_post_code_state(void)
{
	int state = 0;
442
	int ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_READ, &state,
443 444
				       sizeof(state), sizeof(state));
	if (ret)
445
		return ret < 0 ? ret : -EINVAL;
446 447 448
	return state;
}

449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
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);
}

485 486 487 488 489 490 491 492 493
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);
}

494 495 496 497 498 499 500 501 502 503
static ssize_t show_postcode(struct device *dev, struct device_attribute *attr,
			 char *buf)
{
	/* Get the POST error code of previous boot failure. */
	int value = hp_wmi_post_code_state();
	if (value < 0)
		return -EINVAL;
	return sprintf(buf, "0x%x\n", value);
}

504 505 506 507
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);
508
	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
509
				       sizeof(tmp), sizeof(tmp));
510
	if (ret)
511
		return ret < 0 ? ret : -EINVAL;
512

513 514 515
	return count;
}

516 517 518
static ssize_t set_postcode(struct device *dev, struct device_attribute *attr,
		       const char *buf, size_t count)
{
519
	long unsigned int tmp2;
520 521 522 523 524 525 526 527 528
	int ret;
	u32 tmp;

	ret = kstrtoul(buf, 10, &tmp2);
	if (ret || tmp2 != 1)
		return -EINVAL;

	/* Clear the POST error code. It is kept until until cleared. */
	tmp = (u32) tmp2;
529
	ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
530 531
				       sizeof(tmp), sizeof(tmp));
	if (ret)
532
		return ret < 0 ? ret : -EINVAL;
533 534 535 536

	return count;
}

537 538 539 540
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);
541
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
542
static DEVICE_ATTR(postcode, S_IRUGO | S_IWUSR, show_postcode, set_postcode);
543

A
Adrian Bunk 已提交
544
static void hp_wmi_notify(u32 value, void *context)
545 546
{
	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
547
	u32 event_id, event_data;
548
	union acpi_object *obj;
M
Matthew Garrett 已提交
549
	int key_code = 0, ret;
550
	acpi_status status;
551
	u32 *location;
552

553 554
	status = wmi_get_event_data(value, &response);
	if (status != AE_OK) {
555
		pr_info("bad event status 0x%x\n", status);
556 557
		return;
	}
558 559 560

	obj = (union acpi_object *)response.pointer;

561 562 563
	if (!obj)
		return;
	if (obj->type != ACPI_TYPE_BUFFER) {
564
		pr_info("Unknown response received %d\n", obj->type);
T
Thomas Renninger 已提交
565
		kfree(obj);
A
Alan Jenkins 已提交
566 567 568
		return;
	}

569 570 571 572 573 574 575 576 577 578 579 580
	/*
	 * 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 {
581
		pr_info("Unknown buffer length %d\n", obj->buffer.length);
582 583 584
		kfree(obj);
		return;
	}
T
Thomas Renninger 已提交
585
	kfree(obj);
586 587

	switch (event_id) {
588
	case HPWMI_DOCK_EVENT:
589 590 591 592 593 594
		if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
			input_report_switch(hp_wmi_input_dev, SW_DOCK,
					    hp_wmi_dock_state());
		if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
					    hp_wmi_tablet_state());
A
Alan Jenkins 已提交
595
		input_sync(hp_wmi_input_dev);
596
		break;
597 598 599 600
	case HPWMI_PARK_HDD:
		break;
	case HPWMI_SMART_ADAPTER:
		break;
601
	case HPWMI_BEZEL_BUTTON:
602
		ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, HPWMI_READ,
M
Matthew Garrett 已提交
603
					   &key_code,
604
					   sizeof(key_code),
605 606 607
					   sizeof(key_code));
		if (ret)
			break;
608 609 610

		if (!sparse_keymap_report_event(hp_wmi_input_dev,
						key_code, 1, true))
611
			pr_info("Unknown key code - 0x%x\n", key_code);
612 613
		break;
	case HPWMI_WIRELESS:
614 615 616 617 618
		if (rfkill2_count) {
			hp_wmi_rfkill2_refresh();
			break;
		}

A
Alan Jenkins 已提交
619 620 621 622 623 624 625 626 627 628 629 630
		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));
631
		break;
632
	case HPWMI_CPU_BATTERY_THROTTLE:
633
		pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
634 635 636
		break;
	case HPWMI_LOCK_SWITCH:
		break;
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
	case HPWMI_LID_SWITCH:
		break;
	case HPWMI_SCREEN_ROTATION:
		break;
	case HPWMI_COOLSENSE_SYSTEM_MOBILE:
		break;
	case HPWMI_COOLSENSE_SYSTEM_HOT:
		break;
	case HPWMI_PROXIMITY_SENSOR:
		break;
	case HPWMI_BACKLIT_KB_BRIGHTNESS:
		break;
	case HPWMI_PEAKSHIFT_PERIOD:
		break;
	case HPWMI_BATTERY_CHARGE_PERIOD:
		break;
653
	default:
654
		pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
655 656
		break;
	}
657 658 659 660
}

static int __init hp_wmi_input_setup(void)
{
661
	acpi_status status;
662
	int err, val;
663 664

	hp_wmi_input_dev = input_allocate_device();
665 666
	if (!hp_wmi_input_dev)
		return -ENOMEM;
667 668 669 670 671

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

672
	__set_bit(EV_SW, hp_wmi_input_dev->evbit);
673 674 675 676 677 678 679 680 681 682 683 684 685 686

	/* Dock */
	val = hp_wmi_dock_state();
	if (!(val < 0)) {
		__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
		input_report_switch(hp_wmi_input_dev, SW_DOCK, val);
	}

	/* Tablet mode */
	val = hp_wmi_tablet_state();
	if (!(val < 0)) {
		__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
		input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val);
	}
687 688 689 690

	err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
	if (err)
		goto err_free_dev;
691 692 693 694

	/* Set initial hardware state */
	input_sync(hp_wmi_input_dev);

K
Kyle Evans 已提交
695
	if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later())
696 697
		hp_wmi_enable_hotkeys();

698 699 700
	status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
	if (ACPI_FAILURE(status)) {
		err = -EIO;
701
		goto err_free_dev;
702 703
	}

704 705 706 707
	err = input_register_device(hp_wmi_input_dev);
	if (err)
		goto err_uninstall_notifier;

708
	return 0;
709 710 711 712 713 714 715 716 717 718 719 720

 err_uninstall_notifier:
	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
 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);
	input_unregister_device(hp_wmi_input_dev);
721 722 723 724 725 726 727 728
}

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);
729
	device_remove_file(&device->dev, &dev_attr_tablet);
730
	device_remove_file(&device->dev, &dev_attr_postcode);
731 732
}

733
static int __init hp_wmi_rfkill_setup(struct platform_device *device)
734
{
735
	int err, wireless = 0;
736

737
	err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_READ, &wireless,
738
				   sizeof(wireless), sizeof(wireless));
739 740
	if (err)
		return err;
741

742
	err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless,
743 744 745 746
				   sizeof(wireless), 0);
	if (err)
		return err;

747
	if (wireless & 0x1) {
J
Johannes Berg 已提交
748 749 750
		wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
					   RFKILL_TYPE_WLAN,
					   &hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
751
					   (void *) HPWMI_WIFI);
752 753
		if (!wifi_rfkill)
			return -ENOMEM;
A
Alan Jenkins 已提交
754 755 756 757
		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));
758 759
		err = rfkill_register(wifi_rfkill);
		if (err)
J
Johannes Berg 已提交
760
			goto register_wifi_error;
761 762 763
	}

	if (wireless & 0x2) {
J
Johannes Berg 已提交
764 765 766
		bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
						RFKILL_TYPE_BLUETOOTH,
						&hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
767
						(void *) HPWMI_BLUETOOTH);
768 769
		if (!bluetooth_rfkill) {
			err = -ENOMEM;
770
			goto register_bluetooth_error;
771
		}
A
Alan Jenkins 已提交
772 773 774 775
		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));
776
		err = rfkill_register(bluetooth_rfkill);
777
		if (err)
778
			goto register_bluetooth_error;
779 780 781
	}

	if (wireless & 0x4) {
J
Johannes Berg 已提交
782 783 784
		wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
					   RFKILL_TYPE_WWAN,
					   &hp_wmi_rfkill_ops,
A
Alan Jenkins 已提交
785
					   (void *) HPWMI_WWAN);
786 787
		if (!wwan_rfkill) {
			err = -ENOMEM;
788
			goto register_wwan_error;
789
		}
A
Alan Jenkins 已提交
790 791 792 793
		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));
794 795
		err = rfkill_register(wwan_rfkill);
		if (err)
796
			goto register_wwan_error;
797
	}
798 799

	return 0;
800

801 802 803
register_wwan_error:
	rfkill_destroy(wwan_rfkill);
	wwan_rfkill = NULL;
804 805
	if (bluetooth_rfkill)
		rfkill_unregister(bluetooth_rfkill);
806
register_bluetooth_error:
J
Johannes Berg 已提交
807
	rfkill_destroy(bluetooth_rfkill);
808
	bluetooth_rfkill = NULL;
809 810
	if (wifi_rfkill)
		rfkill_unregister(wifi_rfkill);
J
Johannes Berg 已提交
811 812
register_wifi_error:
	rfkill_destroy(wifi_rfkill);
813
	wifi_rfkill = NULL;
814 815 816
	return err;
}

817
static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
818 819
{
	struct bios_rfkill2_state state;
820 821
	int err, i;

822
	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
823 824 825 826 827
				   0, sizeof(state));
	if (err)
		return err;

	if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
828
		pr_warn("unable to parse 0x1b query output\n");
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
		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;
849 850 851 852
		case HPWMI_GPS:
			type = RFKILL_TYPE_GPS;
			name = "hp-gps";
			break;
853
		default:
854 855
			pr_warn("unknown device type 0x%x\n",
				state.device[i].radio_type);
856 857 858 859
			continue;
		}

		if (!state.device[i].vendor_id) {
860 861
			pr_warn("zero device %d while %d reported\n",
				i, state.count);
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
			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))
882
			pr_info("device %s blocked by BIOS\n", name);
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901

		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;
}

902
static int __init hp_wmi_bios_setup(struct platform_device *device)
903 904 905
{
	int err;

906 907 908 909
	/* clear detected rfkill devices */
	wifi_rfkill = NULL;
	bluetooth_rfkill = NULL;
	wwan_rfkill = NULL;
910
	rfkill2_count = 0;
911

912
	if (hp_wmi_rfkill_setup(device))
913
		hp_wmi_rfkill2_setup(device);
914 915 916 917 918 919 920 921 922 923 924 925 926 927

	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);
928 929 930
	if (err)
		goto add_sysfs_error;
	err = device_create_file(&device->dev, &dev_attr_postcode);
931 932 933 934
	if (err)
		goto add_sysfs_error;
	return 0;

935 936 937 938 939 940 941
add_sysfs_error:
	cleanup_sysfs(device);
	return err;
}

static int __exit hp_wmi_bios_remove(struct platform_device *device)
{
942
	int i;
943 944
	cleanup_sysfs(device);

945 946 947 948 949
	for (i = 0; i < rfkill2_count; i++) {
		rfkill_unregister(rfkill2[i].rfkill);
		rfkill_destroy(rfkill2[i].rfkill);
	}

J
Johannes Berg 已提交
950
	if (wifi_rfkill) {
951
		rfkill_unregister(wifi_rfkill);
J
Johannes Berg 已提交
952 953 954
		rfkill_destroy(wifi_rfkill);
	}
	if (bluetooth_rfkill) {
955
		rfkill_unregister(bluetooth_rfkill);
956
		rfkill_destroy(bluetooth_rfkill);
J
Johannes Berg 已提交
957 958
	}
	if (wwan_rfkill) {
959
		rfkill_unregister(wwan_rfkill);
J
Johannes Berg 已提交
960 961
		rfkill_destroy(wwan_rfkill);
	}
962 963 964 965

	return 0;
}

F
Frans Pop 已提交
966
static int hp_wmi_resume_handler(struct device *device)
967 968
{
	/*
969 970
	 * Hardware state may have changed while suspended, so trigger
	 * input events for the current state. As this is a switch,
971 972 973
	 * the input layer will only actually pass it on if the state
	 * changed.
	 */
974
	if (hp_wmi_input_dev) {
975 976 977 978 979 980
		if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
			input_report_switch(hp_wmi_input_dev, SW_DOCK,
					    hp_wmi_dock_state());
		if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
					    hp_wmi_tablet_state());
981 982
		input_sync(hp_wmi_input_dev);
	}
983

984 985 986
	if (rfkill2_count)
		hp_wmi_rfkill2_refresh();

A
Alan Jenkins 已提交
987 988 989 990 991 992 993 994 995 996 997 998 999
	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));

1000 1001 1002
	return 0;
}

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
static const struct dev_pm_ops hp_wmi_pm_ops = {
	.resume  = hp_wmi_resume_handler,
	.restore  = hp_wmi_resume_handler,
};

static struct platform_driver hp_wmi_driver = {
	.driver = {
		.name = "hp-wmi",
		.pm = &hp_wmi_pm_ops,
	},
	.remove = __exit_p(hp_wmi_bios_remove),
};

1016 1017
static int __init hp_wmi_init(void)
{
1018 1019
	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
1020
	int err;
1021

1022 1023 1024
	if (!bios_capable && !event_capable)
		return -ENODEV;

1025
	if (event_capable) {
1026
		err = hp_wmi_input_setup();
1027
		if (err)
1028
			return err;
1029 1030
	}

1031
	if (bios_capable) {
1032 1033 1034 1035 1036
		hp_wmi_platform_dev =
			platform_device_register_simple("hp-wmi", -1, NULL, 0);
		if (IS_ERR(hp_wmi_platform_dev)) {
			err = PTR_ERR(hp_wmi_platform_dev);
			goto err_destroy_input;
1037
		}
1038 1039

		err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
1040
		if (err)
1041
			goto err_unregister_device;
1042 1043 1044
	}

	return 0;
1045

1046 1047 1048
err_unregister_device:
	platform_device_unregister(hp_wmi_platform_dev);
err_destroy_input:
1049 1050
	if (event_capable)
		hp_wmi_input_destroy();
1051 1052

	return err;
1053
}
1054
module_init(hp_wmi_init);
1055 1056 1057

static void __exit hp_wmi_exit(void)
{
1058 1059 1060
	if (wmi_has_guid(HPWMI_EVENT_GUID))
		hp_wmi_input_destroy();

1061
	if (hp_wmi_platform_dev) {
1062
		platform_device_unregister(hp_wmi_platform_dev);
1063 1064 1065 1066
		platform_driver_unregister(&hp_wmi_driver);
	}
}
module_exit(hp_wmi_exit);