aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/hp-wmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/hp-wmi.c')
-rw-r--r--drivers/platform/x86/hp-wmi.c281
1 files changed, 219 insertions, 62 deletions
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 51c07a05a7b..f1551637498 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -29,7 +29,6 @@
29#include <linux/slab.h> 29#include <linux/slab.h>
30#include <linux/types.h> 30#include <linux/types.h>
31#include <linux/input.h> 31#include <linux/input.h>
32#include <acpi/acpi_drivers.h>
33#include <linux/platform_device.h> 32#include <linux/platform_device.h>
34#include <linux/acpi.h> 33#include <linux/acpi.h>
35#include <linux/rfkill.h> 34#include <linux/rfkill.h>
@@ -52,12 +51,25 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
52#define HPWMI_WIRELESS_QUERY 0x5 51#define HPWMI_WIRELESS_QUERY 0x5
53#define HPWMI_HOTKEY_QUERY 0xc 52#define HPWMI_HOTKEY_QUERY 0xc
54 53
54#define PREFIX "HP WMI: "
55#define UNIMP "Unimplemented "
56
55enum hp_wmi_radio { 57enum hp_wmi_radio {
56 HPWMI_WIFI = 0, 58 HPWMI_WIFI = 0,
57 HPWMI_BLUETOOTH = 1, 59 HPWMI_BLUETOOTH = 1,
58 HPWMI_WWAN = 2, 60 HPWMI_WWAN = 2,
59}; 61};
60 62
63enum hp_wmi_event_ids {
64 HPWMI_DOCK_EVENT = 1,
65 HPWMI_PARK_HDD = 2,
66 HPWMI_SMART_ADAPTER = 3,
67 HPWMI_BEZEL_BUTTON = 4,
68 HPWMI_WIRELESS = 5,
69 HPWMI_CPU_BATTERY_THROTTLE = 6,
70 HPWMI_LOCK_SWITCH = 7,
71};
72
61static int __devinit hp_wmi_bios_setup(struct platform_device *device); 73static int __devinit hp_wmi_bios_setup(struct platform_device *device);
62static int __exit hp_wmi_bios_remove(struct platform_device *device); 74static int __exit hp_wmi_bios_remove(struct platform_device *device);
63static int hp_wmi_resume_handler(struct device *device); 75static int hp_wmi_resume_handler(struct device *device);
@@ -67,13 +79,12 @@ struct bios_args {
67 u32 command; 79 u32 command;
68 u32 commandtype; 80 u32 commandtype;
69 u32 datasize; 81 u32 datasize;
70 u32 data; 82 char *data;
71}; 83};
72 84
73struct bios_return { 85struct bios_return {
74 u32 sigpass; 86 u32 sigpass;
75 u32 return_code; 87 u32 return_code;
76 u32 value;
77}; 88};
78 89
79struct key_entry { 90struct key_entry {
@@ -88,6 +99,7 @@ static struct key_entry hp_wmi_keymap[] = {
88 {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, 99 {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
89 {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, 100 {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
90 {KE_KEY, 0x20e6, KEY_PROG1}, 101 {KE_KEY, 0x20e6, KEY_PROG1},
102 {KE_KEY, 0x20e8, KEY_MEDIA},
91 {KE_KEY, 0x2142, KEY_MEDIA}, 103 {KE_KEY, 0x2142, KEY_MEDIA},
92 {KE_KEY, 0x213b, KEY_INFO}, 104 {KE_KEY, 0x213b, KEY_INFO},
93 {KE_KEY, 0x2169, KEY_DIRECTION}, 105 {KE_KEY, 0x2169, KEY_DIRECTION},
@@ -117,7 +129,27 @@ static struct platform_driver hp_wmi_driver = {
117 .remove = hp_wmi_bios_remove, 129 .remove = hp_wmi_bios_remove,
118}; 130};
119 131
120static int hp_wmi_perform_query(int query, int write, int value) 132/*
133 * hp_wmi_perform_query
134 *
135 * query: The commandtype -> What should be queried
136 * write: The command -> 0 read, 1 write, 3 ODM specific
137 * buffer: Buffer used as input and/or output
138 * buffersize: Size of buffer
139 *
140 * returns zero on success
141 * an HP WMI query specific error code (which is positive)
142 * -EINVAL if the query was not successful at all
143 * -EINVAL if the output buffer size exceeds buffersize
144 *
145 * Note: The buffersize must at least be the maximum of the input and output
146 * size. E.g. Battery info query (0x7) is defined to have 1 byte input
147 * and 128 byte output. The caller would do:
148 * buffer = kzalloc(128, GFP_KERNEL);
149 * ret = hp_wmi_perform_query(0x7, 0, buffer, 128)
150 */
151static int hp_wmi_perform_query(int query, int write, char *buffer,
152 int buffersize)
121{ 153{
122 struct bios_return bios_return; 154 struct bios_return bios_return;
123 acpi_status status; 155 acpi_status status;
@@ -126,8 +158,8 @@ static int hp_wmi_perform_query(int query, int write, int value)
126 .signature = 0x55434553, 158 .signature = 0x55434553,
127 .command = write ? 0x2 : 0x1, 159 .command = write ? 0x2 : 0x1,
128 .commandtype = query, 160 .commandtype = query,
129 .datasize = write ? 0x4 : 0, 161 .datasize = buffersize,
130 .data = value, 162 .data = buffer,
131 }; 163 };
132 struct acpi_buffer input = { sizeof(struct bios_args), &args }; 164 struct acpi_buffer input = { sizeof(struct bios_args), &args };
133 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 165 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -144,54 +176,90 @@ static int hp_wmi_perform_query(int query, int write, int value)
144 } 176 }
145 177
146 bios_return = *((struct bios_return *)obj->buffer.pointer); 178 bios_return = *((struct bios_return *)obj->buffer.pointer);
179
180 if (bios_return.return_code) {
181 printk(KERN_WARNING PREFIX "Query %d returned %d\n", query,
182 bios_return.return_code);
183 kfree(obj);
184 return bios_return.return_code;
185 }
186 if (obj->buffer.length - sizeof(bios_return) > buffersize) {
187 kfree(obj);
188 return -EINVAL;
189 }
190
191 memset(buffer, 0, buffersize);
192 memcpy(buffer,
193 ((char *)obj->buffer.pointer) + sizeof(struct bios_return),
194 obj->buffer.length - sizeof(bios_return));
147 kfree(obj); 195 kfree(obj);
148 if (bios_return.return_code > 0) 196 return 0;
149 return bios_return.return_code * -1;
150 else
151 return bios_return.value;
152} 197}
153 198
154static int hp_wmi_display_state(void) 199static int hp_wmi_display_state(void)
155{ 200{
156 return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0); 201 int state;
202 int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, (char *)&state,
203 sizeof(state));
204 if (ret)
205 return -EINVAL;
206 return state;
157} 207}
158 208
159static int hp_wmi_hddtemp_state(void) 209static int hp_wmi_hddtemp_state(void)
160{ 210{
161 return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0); 211 int state;
212 int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, (char *)&state,
213 sizeof(state));
214 if (ret)
215 return -EINVAL;
216 return state;
162} 217}
163 218
164static int hp_wmi_als_state(void) 219static int hp_wmi_als_state(void)
165{ 220{
166 return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0); 221 int state;
222 int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, (char *)&state,
223 sizeof(state));
224 if (ret)
225 return -EINVAL;
226 return state;
167} 227}
168 228
169static int hp_wmi_dock_state(void) 229static int hp_wmi_dock_state(void)
170{ 230{
171 int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); 231 int state;
232 int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, (char *)&state,
233 sizeof(state));
172 234
173 if (ret < 0) 235 if (ret)
174 return ret; 236 return -EINVAL;
175 237
176 return ret & 0x1; 238 return state & 0x1;
177} 239}
178 240
179static int hp_wmi_tablet_state(void) 241static int hp_wmi_tablet_state(void)
180{ 242{
181 int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); 243 int state;
182 244 int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, (char *)&state,
183 if (ret < 0) 245 sizeof(state));
246 if (ret)
184 return ret; 247 return ret;
185 248
186 return (ret & 0x4) ? 1 : 0; 249 return (state & 0x4) ? 1 : 0;
187} 250}
188 251
189static int hp_wmi_set_block(void *data, bool blocked) 252static int hp_wmi_set_block(void *data, bool blocked)
190{ 253{
191 enum hp_wmi_radio r = (enum hp_wmi_radio) data; 254 enum hp_wmi_radio r = (enum hp_wmi_radio) data;
192 int query = BIT(r + 8) | ((!blocked) << r); 255 int query = BIT(r + 8) | ((!blocked) << r);
256 int ret;
193 257
194 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); 258 ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
259 (char *)&query, sizeof(query));
260 if (ret)
261 return -EINVAL;
262 return 0;
195} 263}
196 264
197static const struct rfkill_ops hp_wmi_rfkill_ops = { 265static const struct rfkill_ops hp_wmi_rfkill_ops = {
@@ -200,8 +268,13 @@ static const struct rfkill_ops hp_wmi_rfkill_ops = {
200 268
201static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) 269static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
202{ 270{
203 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); 271 int wireless;
204 int mask = 0x200 << (r * 8); 272 int mask;
273 hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
274 (char *)&wireless, sizeof(wireless));
275 /* TBD: Pass error */
276
277 mask = 0x200 << (r * 8);
205 278
206 if (wireless & mask) 279 if (wireless & mask)
207 return false; 280 return false;
@@ -211,8 +284,13 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
211 284
212static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) 285static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
213{ 286{
214 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); 287 int wireless;
215 int mask = 0x800 << (r * 8); 288 int mask;
289 hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
290 (char *)&wireless, sizeof(wireless));
291 /* TBD: Pass error */
292
293 mask = 0x800 << (r * 8);
216 294
217 if (wireless & mask) 295 if (wireless & mask)
218 return false; 296 return false;
@@ -269,7 +347,11 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr,
269 const char *buf, size_t count) 347 const char *buf, size_t count)
270{ 348{
271 u32 tmp = simple_strtoul(buf, NULL, 10); 349 u32 tmp = simple_strtoul(buf, NULL, 10);
272 hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp); 350 int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, (char *)&tmp,
351 sizeof(tmp));
352 if (ret)
353 return -EINVAL;
354
273 return count; 355 return count;
274} 356}
275 357
@@ -338,47 +420,82 @@ static void hp_wmi_notify(u32 value, void *context)
338 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 420 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
339 static struct key_entry *key; 421 static struct key_entry *key;
340 union acpi_object *obj; 422 union acpi_object *obj;
341 int eventcode; 423 u32 event_id, event_data;
424 int key_code, ret;
425 u32 *location;
342 acpi_status status; 426 acpi_status status;
343 427
344 status = wmi_get_event_data(value, &response); 428 status = wmi_get_event_data(value, &response);
345 if (status != AE_OK) { 429 if (status != AE_OK) {
346 printk(KERN_INFO "hp-wmi: bad event status 0x%x\n", status); 430 printk(KERN_INFO PREFIX "bad event status 0x%x\n", status);
347 return; 431 return;
348 } 432 }
349 433
350 obj = (union acpi_object *)response.pointer; 434 obj = (union acpi_object *)response.pointer;
351 435
352 if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) { 436 if (!obj)
353 printk(KERN_INFO "HP WMI: Unknown response received\n"); 437 return;
438 if (obj->type != ACPI_TYPE_BUFFER) {
439 printk(KERN_INFO "hp-wmi: Unknown response received %d\n",
440 obj->type);
354 kfree(obj); 441 kfree(obj);
355 return; 442 return;
356 } 443 }
357 444
358 eventcode = *((u8 *) obj->buffer.pointer); 445 /*
446 * Depending on ACPI version the concatenation of id and event data
447 * inside _WED function will result in a 8 or 16 byte buffer.
448 */
449 location = (u32 *)obj->buffer.pointer;
450 if (obj->buffer.length == 8) {
451 event_id = *location;
452 event_data = *(location + 1);
453 } else if (obj->buffer.length == 16) {
454 event_id = *location;
455 event_data = *(location + 2);
456 } else {
457 printk(KERN_INFO "hp-wmi: Unknown buffer length %d\n",
458 obj->buffer.length);
459 kfree(obj);
460 return;
461 }
359 kfree(obj); 462 kfree(obj);
360 if (eventcode == 0x4) 463
361 eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, 464 switch (event_id) {
362 0); 465 case HPWMI_DOCK_EVENT:
363 key = hp_wmi_get_entry_by_scancode(eventcode);
364 if (key) {
365 switch (key->type) {
366 case KE_KEY:
367 input_report_key(hp_wmi_input_dev,
368 key->keycode, 1);
369 input_sync(hp_wmi_input_dev);
370 input_report_key(hp_wmi_input_dev,
371 key->keycode, 0);
372 input_sync(hp_wmi_input_dev);
373 break;
374 }
375 } else if (eventcode == 0x1) {
376 input_report_switch(hp_wmi_input_dev, SW_DOCK, 466 input_report_switch(hp_wmi_input_dev, SW_DOCK,
377 hp_wmi_dock_state()); 467 hp_wmi_dock_state());
378 input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, 468 input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
379 hp_wmi_tablet_state()); 469 hp_wmi_tablet_state());
380 input_sync(hp_wmi_input_dev); 470 input_sync(hp_wmi_input_dev);
381 } else if (eventcode == 0x5) { 471 break;
472 case HPWMI_PARK_HDD:
473 break;
474 case HPWMI_SMART_ADAPTER:
475 break;
476 case HPWMI_BEZEL_BUTTON:
477 ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
478 (char *)&key_code,
479 sizeof(key_code));
480 if (ret)
481 break;
482 key = hp_wmi_get_entry_by_scancode(key_code);
483 if (key) {
484 switch (key->type) {
485 case KE_KEY:
486 input_report_key(hp_wmi_input_dev,
487 key->keycode, 1);
488 input_sync(hp_wmi_input_dev);
489 input_report_key(hp_wmi_input_dev,
490 key->keycode, 0);
491 input_sync(hp_wmi_input_dev);
492 break;
493 }
494 } else
495 printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
496 key_code);
497 break;
498 case HPWMI_WIRELESS:
382 if (wifi_rfkill) 499 if (wifi_rfkill)
383 rfkill_set_states(wifi_rfkill, 500 rfkill_set_states(wifi_rfkill,
384 hp_wmi_get_sw_state(HPWMI_WIFI), 501 hp_wmi_get_sw_state(HPWMI_WIFI),
@@ -391,9 +508,18 @@ static void hp_wmi_notify(u32 value, void *context)
391 rfkill_set_states(wwan_rfkill, 508 rfkill_set_states(wwan_rfkill,
392 hp_wmi_get_sw_state(HPWMI_WWAN), 509 hp_wmi_get_sw_state(HPWMI_WWAN),
393 hp_wmi_get_hw_state(HPWMI_WWAN)); 510 hp_wmi_get_hw_state(HPWMI_WWAN));
394 } else 511 break;
395 printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", 512 case HPWMI_CPU_BATTERY_THROTTLE:
396 eventcode); 513 printk(KERN_INFO PREFIX UNIMP "CPU throttle because of 3 Cell"
514 " battery event detected\n");
515 break;
516 case HPWMI_LOCK_SWITCH:
517 break;
518 default:
519 printk(KERN_INFO PREFIX "Unknown event_id - %d - 0x%x\n",
520 event_id, event_data);
521 break;
522 }
397} 523}
398 524
399static int __init hp_wmi_input_setup(void) 525static int __init hp_wmi_input_setup(void)
@@ -402,6 +528,8 @@ static int __init hp_wmi_input_setup(void)
402 int err; 528 int err;
403 529
404 hp_wmi_input_dev = input_allocate_device(); 530 hp_wmi_input_dev = input_allocate_device();
531 if (!hp_wmi_input_dev)
532 return -ENOMEM;
405 533
406 hp_wmi_input_dev->name = "HP WMI hotkeys"; 534 hp_wmi_input_dev->name = "HP WMI hotkeys";
407 hp_wmi_input_dev->phys = "wmi/input0"; 535 hp_wmi_input_dev->phys = "wmi/input0";
@@ -450,7 +578,12 @@ static void cleanup_sysfs(struct platform_device *device)
450static int __devinit hp_wmi_bios_setup(struct platform_device *device) 578static int __devinit hp_wmi_bios_setup(struct platform_device *device)
451{ 579{
452 int err; 580 int err;
453 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); 581 int wireless;
582
583 err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, (char *)&wireless,
584 sizeof(wireless));
585 if (err)
586 return err;
454 587
455 err = device_create_file(&device->dev, &dev_attr_display); 588 err = device_create_file(&device->dev, &dev_attr_display);
456 if (err) 589 if (err)
@@ -581,27 +714,51 @@ static int hp_wmi_resume_handler(struct device *device)
581static int __init hp_wmi_init(void) 714static int __init hp_wmi_init(void)
582{ 715{
583 int err; 716 int err;
717 int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
718 int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
584 719
585 if (wmi_has_guid(HPWMI_EVENT_GUID)) { 720 if (event_capable) {
586 err = wmi_install_notify_handler(HPWMI_EVENT_GUID, 721 err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
587 hp_wmi_notify, NULL); 722 hp_wmi_notify, NULL);
588 if (ACPI_SUCCESS(err)) 723 if (ACPI_FAILURE(err))
589 hp_wmi_input_setup(); 724 return -EINVAL;
725 err = hp_wmi_input_setup();
726 if (err) {
727 wmi_remove_notify_handler(HPWMI_EVENT_GUID);
728 return err;
729 }
590 } 730 }
591 731
592 if (wmi_has_guid(HPWMI_BIOS_GUID)) { 732 if (bios_capable) {
593 err = platform_driver_register(&hp_wmi_driver); 733 err = platform_driver_register(&hp_wmi_driver);
594 if (err) 734 if (err)
595 return 0; 735 goto err_driver_reg;
596 hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); 736 hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
597 if (!hp_wmi_platform_dev) { 737 if (!hp_wmi_platform_dev) {
598 platform_driver_unregister(&hp_wmi_driver); 738 err = -ENOMEM;
599 return 0; 739 goto err_device_alloc;
600 } 740 }
601 platform_device_add(hp_wmi_platform_dev); 741 err = platform_device_add(hp_wmi_platform_dev);
742 if (err)
743 goto err_device_add;
602 } 744 }
603 745
746 if (!bios_capable && !event_capable)
747 return -ENODEV;
748
604 return 0; 749 return 0;
750
751err_device_add:
752 platform_device_put(hp_wmi_platform_dev);
753err_device_alloc:
754 platform_driver_unregister(&hp_wmi_driver);
755err_driver_reg:
756 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
757 input_unregister_device(hp_wmi_input_dev);
758 wmi_remove_notify_handler(HPWMI_EVENT_GUID);
759 }
760
761 return err;
605} 762}
606 763
607static void __exit hp_wmi_exit(void) 764static void __exit hp_wmi_exit(void)
@@ -611,7 +768,7 @@ static void __exit hp_wmi_exit(void)
611 input_unregister_device(hp_wmi_input_dev); 768 input_unregister_device(hp_wmi_input_dev);
612 } 769 }
613 if (hp_wmi_platform_dev) { 770 if (hp_wmi_platform_dev) {
614 platform_device_del(hp_wmi_platform_dev); 771 platform_device_unregister(hp_wmi_platform_dev);
615 platform_driver_unregister(&hp_wmi_driver); 772 platform_driver_unregister(&hp_wmi_driver);
616 } 773 }
617} 774}