• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * HP WMI hotkeys
3  *
4  * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5  *
6  * Portions based on wistron_btns.c:
7  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8  * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9  * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25 
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/types.h>
30 #include <linux/input.h>
31 #include <acpi/acpi_drivers.h>
32 #include <linux/platform_device.h>
33 #include <linux/acpi.h>
34 #include <linux/rfkill.h>
35 #include <linux/string.h>
36 
37 MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
38 MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
39 MODULE_LICENSE("GPL");
40 
41 MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
42 MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
43 
44 #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
45 #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
46 
47 #define HPWMI_DISPLAY_QUERY 0x1
48 #define HPWMI_HDDTEMP_QUERY 0x2
49 #define HPWMI_ALS_QUERY 0x3
50 #define HPWMI_DOCK_QUERY 0x4
51 #define HPWMI_WIRELESS_QUERY 0x5
52 #define HPWMI_HOTKEY_QUERY 0xc
53 
54 static int __init hp_wmi_bios_setup(struct platform_device *device);
55 static int __exit hp_wmi_bios_remove(struct platform_device *device);
56 
57 struct bios_args {
58 	u32 signature;
59 	u32 command;
60 	u32 commandtype;
61 	u32 datasize;
62 	u32 data;
63 };
64 
65 struct bios_return {
66 	u32 sigpass;
67 	u32 return_code;
68 	u32 value;
69 };
70 
71 struct key_entry {
72 	char type;		/* See KE_* below */
73 	u16 code;
74 	u16 keycode;
75 };
76 
77 enum { KE_KEY, KE_SW, KE_END };
78 
79 static struct key_entry hp_wmi_keymap[] = {
80 	{KE_SW, 0x01, SW_DOCK},
81 	{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
82 	{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
83 	{KE_KEY, 0x20e6, KEY_PROG1},
84 	{KE_KEY, 0x2142, KEY_MEDIA},
85 	{KE_KEY, 0x213b, KEY_INFO},
86 	{KE_KEY, 0x231b, KEY_HELP},
87 	{KE_END, 0}
88 };
89 
90 static struct input_dev *hp_wmi_input_dev;
91 static struct platform_device *hp_wmi_platform_dev;
92 
93 static struct rfkill *wifi_rfkill;
94 static struct rfkill *bluetooth_rfkill;
95 static struct rfkill *wwan_rfkill;
96 
97 static struct platform_driver hp_wmi_driver = {
98 	.driver = {
99 		   .name = "hp-wmi",
100 		   .owner = THIS_MODULE,
101 	},
102 	.probe = hp_wmi_bios_setup,
103 	.remove = hp_wmi_bios_remove,
104 };
105 
hp_wmi_perform_query(int query,int write,int value)106 static int hp_wmi_perform_query(int query, int write, int value)
107 {
108 	struct bios_return bios_return;
109 	acpi_status status;
110 	union acpi_object *obj;
111 	struct bios_args args = {
112 		.signature = 0x55434553,
113 		.command = write ? 0x2 : 0x1,
114 		.commandtype = query,
115 		.datasize = write ? 0x4 : 0,
116 		.data = value,
117 	};
118 	struct acpi_buffer input = { sizeof(struct bios_args), &args };
119 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
120 
121 	status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
122 
123 	obj = output.pointer;
124 
125 	if (!obj || obj->type != ACPI_TYPE_BUFFER)
126 		return -EINVAL;
127 
128 	bios_return = *((struct bios_return *)obj->buffer.pointer);
129 	if (bios_return.return_code > 0)
130 		return bios_return.return_code * -1;
131 	else
132 		return bios_return.value;
133 }
134 
hp_wmi_display_state(void)135 static int hp_wmi_display_state(void)
136 {
137 	return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
138 }
139 
hp_wmi_hddtemp_state(void)140 static int hp_wmi_hddtemp_state(void)
141 {
142 	return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
143 }
144 
hp_wmi_als_state(void)145 static int hp_wmi_als_state(void)
146 {
147 	return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
148 }
149 
hp_wmi_dock_state(void)150 static int hp_wmi_dock_state(void)
151 {
152 	return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
153 }
154 
hp_wmi_wifi_set(void * data,enum rfkill_state state)155 static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
156 {
157 	if (state)
158 		return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
159 	else
160 		return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
161 }
162 
hp_wmi_bluetooth_set(void * data,enum rfkill_state state)163 static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
164 {
165 	if (state)
166 		return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
167 	else
168 		return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
169 }
170 
hp_wmi_wwan_set(void * data,enum rfkill_state state)171 static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
172 {
173 	if (state)
174 		return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
175 	else
176 		return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
177 }
178 
hp_wmi_wifi_state(void)179 static int hp_wmi_wifi_state(void)
180 {
181 	int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
182 
183 	if (wireless & 0x100)
184 		return RFKILL_STATE_UNBLOCKED;
185 	else
186 		return RFKILL_STATE_SOFT_BLOCKED;
187 }
188 
hp_wmi_bluetooth_state(void)189 static int hp_wmi_bluetooth_state(void)
190 {
191 	int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
192 
193 	if (wireless & 0x10000)
194 		return RFKILL_STATE_UNBLOCKED;
195 	else
196 		return RFKILL_STATE_SOFT_BLOCKED;
197 }
198 
hp_wmi_wwan_state(void)199 static int hp_wmi_wwan_state(void)
200 {
201 	int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
202 
203 	if (wireless & 0x1000000)
204 		return RFKILL_STATE_UNBLOCKED;
205 	else
206 		return RFKILL_STATE_SOFT_BLOCKED;
207 }
208 
show_display(struct device * dev,struct device_attribute * attr,char * buf)209 static ssize_t show_display(struct device *dev, struct device_attribute *attr,
210 			    char *buf)
211 {
212 	int value = hp_wmi_display_state();
213 	if (value < 0)
214 		return -EINVAL;
215 	return sprintf(buf, "%d\n", value);
216 }
217 
show_hddtemp(struct device * dev,struct device_attribute * attr,char * buf)218 static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
219 			    char *buf)
220 {
221 	int value = hp_wmi_hddtemp_state();
222 	if (value < 0)
223 		return -EINVAL;
224 	return sprintf(buf, "%d\n", value);
225 }
226 
show_als(struct device * dev,struct device_attribute * attr,char * buf)227 static ssize_t show_als(struct device *dev, struct device_attribute *attr,
228 			char *buf)
229 {
230 	int value = hp_wmi_als_state();
231 	if (value < 0)
232 		return -EINVAL;
233 	return sprintf(buf, "%d\n", value);
234 }
235 
show_dock(struct device * dev,struct device_attribute * attr,char * buf)236 static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
237 			 char *buf)
238 {
239 	int value = hp_wmi_dock_state();
240 	if (value < 0)
241 		return -EINVAL;
242 	return sprintf(buf, "%d\n", value);
243 }
244 
set_als(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)245 static ssize_t set_als(struct device *dev, struct device_attribute *attr,
246 		       const char *buf, size_t count)
247 {
248 	u32 tmp = simple_strtoul(buf, NULL, 10);
249 	hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
250 	return count;
251 }
252 
253 static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
254 static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
255 static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
256 static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
257 
hp_wmi_get_entry_by_scancode(int code)258 static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
259 {
260 	struct key_entry *key;
261 
262 	for (key = hp_wmi_keymap; key->type != KE_END; key++)
263 		if (code == key->code)
264 			return key;
265 
266 	return NULL;
267 }
268 
hp_wmi_get_entry_by_keycode(int keycode)269 static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
270 {
271 	struct key_entry *key;
272 
273 	for (key = hp_wmi_keymap; key->type != KE_END; key++)
274 		if (key->type == KE_KEY && keycode == key->keycode)
275 			return key;
276 
277 	return NULL;
278 }
279 
hp_wmi_getkeycode(struct input_dev * dev,int scancode,int * keycode)280 static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
281 {
282 	struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
283 
284 	if (key && key->type == KE_KEY) {
285 		*keycode = key->keycode;
286 		return 0;
287 	}
288 
289 	return -EINVAL;
290 }
291 
hp_wmi_setkeycode(struct input_dev * dev,int scancode,int keycode)292 static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
293 {
294 	struct key_entry *key;
295 	int old_keycode;
296 
297 	if (keycode < 0 || keycode > KEY_MAX)
298 		return -EINVAL;
299 
300 	key = hp_wmi_get_entry_by_scancode(scancode);
301 	if (key && key->type == KE_KEY) {
302 		old_keycode = key->keycode;
303 		key->keycode = keycode;
304 		set_bit(keycode, dev->keybit);
305 		if (!hp_wmi_get_entry_by_keycode(old_keycode))
306 			clear_bit(old_keycode, dev->keybit);
307 		return 0;
308 	}
309 
310 	return -EINVAL;
311 }
312 
hp_wmi_notify(u32 value,void * context)313 static void hp_wmi_notify(u32 value, void *context)
314 {
315 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
316 	static struct key_entry *key;
317 	union acpi_object *obj;
318 
319 	wmi_get_event_data(value, &response);
320 
321 	obj = (union acpi_object *)response.pointer;
322 
323 	if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
324 		int eventcode = *((u8 *) obj->buffer.pointer);
325 		if (eventcode == 0x4)
326 			eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
327 							 0);
328 		key = hp_wmi_get_entry_by_scancode(eventcode);
329 		if (key) {
330 			switch (key->type) {
331 			case KE_KEY:
332 				input_report_key(hp_wmi_input_dev,
333 						 key->keycode, 1);
334 				input_sync(hp_wmi_input_dev);
335 				input_report_key(hp_wmi_input_dev,
336 						 key->keycode, 0);
337 				input_sync(hp_wmi_input_dev);
338 				break;
339 			case KE_SW:
340 				input_report_switch(hp_wmi_input_dev,
341 						    key->keycode,
342 						    hp_wmi_dock_state());
343 				input_sync(hp_wmi_input_dev);
344 				break;
345 			}
346 		} else if (eventcode == 0x5) {
347 			if (wifi_rfkill)
348 				rfkill_force_state(wifi_rfkill,
349 						   hp_wmi_wifi_state());
350 			if (bluetooth_rfkill)
351 				rfkill_force_state(bluetooth_rfkill,
352 						   hp_wmi_bluetooth_state());
353 			if (wwan_rfkill)
354 				rfkill_force_state(wwan_rfkill,
355 						   hp_wmi_wwan_state());
356 		} else
357 			printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
358 			       eventcode);
359 	} else
360 		printk(KERN_INFO "HP WMI: Unknown response received\n");
361 }
362 
hp_wmi_input_setup(void)363 static int __init hp_wmi_input_setup(void)
364 {
365 	struct key_entry *key;
366 	int err;
367 
368 	hp_wmi_input_dev = input_allocate_device();
369 
370 	hp_wmi_input_dev->name = "HP WMI hotkeys";
371 	hp_wmi_input_dev->phys = "wmi/input0";
372 	hp_wmi_input_dev->id.bustype = BUS_HOST;
373 	hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
374 	hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
375 
376 	for (key = hp_wmi_keymap; key->type != KE_END; key++) {
377 		switch (key->type) {
378 		case KE_KEY:
379 			set_bit(EV_KEY, hp_wmi_input_dev->evbit);
380 			set_bit(key->keycode, hp_wmi_input_dev->keybit);
381 			break;
382 		case KE_SW:
383 			set_bit(EV_SW, hp_wmi_input_dev->evbit);
384 			set_bit(key->keycode, hp_wmi_input_dev->swbit);
385 
386 			/* Set initial dock state */
387 			input_report_switch(hp_wmi_input_dev, key->keycode,
388 					    hp_wmi_dock_state());
389 			input_sync(hp_wmi_input_dev);
390 			break;
391 		}
392 	}
393 
394 	err = input_register_device(hp_wmi_input_dev);
395 
396 	if (err) {
397 		input_free_device(hp_wmi_input_dev);
398 		return err;
399 	}
400 
401 	return 0;
402 }
403 
cleanup_sysfs(struct platform_device * device)404 static void cleanup_sysfs(struct platform_device *device)
405 {
406 	device_remove_file(&device->dev, &dev_attr_display);
407 	device_remove_file(&device->dev, &dev_attr_hddtemp);
408 	device_remove_file(&device->dev, &dev_attr_als);
409 	device_remove_file(&device->dev, &dev_attr_dock);
410 }
411 
hp_wmi_bios_setup(struct platform_device * device)412 static int __init hp_wmi_bios_setup(struct platform_device *device)
413 {
414 	int err;
415 	int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
416 
417 	err = device_create_file(&device->dev, &dev_attr_display);
418 	if (err)
419 		goto add_sysfs_error;
420 	err = device_create_file(&device->dev, &dev_attr_hddtemp);
421 	if (err)
422 		goto add_sysfs_error;
423 	err = device_create_file(&device->dev, &dev_attr_als);
424 	if (err)
425 		goto add_sysfs_error;
426 	err = device_create_file(&device->dev, &dev_attr_dock);
427 	if (err)
428 		goto add_sysfs_error;
429 
430 	if (wireless & 0x1) {
431 		wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
432 		wifi_rfkill->name = "hp-wifi";
433 		wifi_rfkill->state = hp_wmi_wifi_state();
434 		wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
435 		wifi_rfkill->user_claim_unsupported = 1;
436 		err = rfkill_register(wifi_rfkill);
437 		if (err)
438 			goto add_sysfs_error;
439 	}
440 
441 	if (wireless & 0x2) {
442 		bluetooth_rfkill = rfkill_allocate(&device->dev,
443 						   RFKILL_TYPE_BLUETOOTH);
444 		bluetooth_rfkill->name = "hp-bluetooth";
445 		bluetooth_rfkill->state = hp_wmi_bluetooth_state();
446 		bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
447 		bluetooth_rfkill->user_claim_unsupported = 1;
448 		err = rfkill_register(bluetooth_rfkill);
449 		if (err)
450 			goto register_bluetooth_error;
451 	}
452 
453 	if (wireless & 0x4) {
454 		wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
455 		wwan_rfkill->name = "hp-wwan";
456 		wwan_rfkill->state = hp_wmi_wwan_state();
457 		wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
458 		wwan_rfkill->user_claim_unsupported = 1;
459 		err = rfkill_register(wwan_rfkill);
460 		if (err)
461 			goto register_wwan_err;
462 	}
463 
464 	return 0;
465 register_wwan_err:
466 	if (bluetooth_rfkill)
467 		rfkill_unregister(bluetooth_rfkill);
468 register_bluetooth_error:
469 	if (wifi_rfkill)
470 		rfkill_unregister(wifi_rfkill);
471 add_sysfs_error:
472 	cleanup_sysfs(device);
473 	return err;
474 }
475 
hp_wmi_bios_remove(struct platform_device * device)476 static int __exit hp_wmi_bios_remove(struct platform_device *device)
477 {
478 	cleanup_sysfs(device);
479 
480 	if (wifi_rfkill)
481 		rfkill_unregister(wifi_rfkill);
482 	if (bluetooth_rfkill)
483 		rfkill_unregister(bluetooth_rfkill);
484 	if (wwan_rfkill)
485 		rfkill_unregister(wwan_rfkill);
486 
487 	return 0;
488 }
489 
hp_wmi_init(void)490 static int __init hp_wmi_init(void)
491 {
492 	int err;
493 
494 	if (wmi_has_guid(HPWMI_EVENT_GUID)) {
495 		err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
496 						 hp_wmi_notify, NULL);
497 		if (!err)
498 			hp_wmi_input_setup();
499 	}
500 
501 	if (wmi_has_guid(HPWMI_BIOS_GUID)) {
502 		err = platform_driver_register(&hp_wmi_driver);
503 		if (err)
504 			return 0;
505 		hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
506 		if (!hp_wmi_platform_dev) {
507 			platform_driver_unregister(&hp_wmi_driver);
508 			return 0;
509 		}
510 		platform_device_add(hp_wmi_platform_dev);
511 	}
512 
513 	return 0;
514 }
515 
hp_wmi_exit(void)516 static void __exit hp_wmi_exit(void)
517 {
518 	if (wmi_has_guid(HPWMI_EVENT_GUID)) {
519 		wmi_remove_notify_handler(HPWMI_EVENT_GUID);
520 		input_unregister_device(hp_wmi_input_dev);
521 	}
522 	if (hp_wmi_platform_dev) {
523 		platform_device_del(hp_wmi_platform_dev);
524 		platform_driver_unregister(&hp_wmi_driver);
525 	}
526 }
527 
528 module_init(hp_wmi_init);
529 module_exit(hp_wmi_exit);
530