aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@iki.fi>2011-02-20 13:07:26 -0500
committerMatthew Garrett <mjg@redhat.com>2011-03-28 06:07:19 -0400
commitc0b9c6494498014f28dbec37fe327bf016a91356 (patch)
tree6bcb7e86b32d082dbda358b58a169986e0b9cd13 /drivers/platform
parent7cd635da42fda9272fd200121e565d116c0c65c9 (diff)
hp-wmi: add rfkill support for wireless query 0x1b
Some recent HP laptops use a new wireless query command type 0x1b. Add support for it. Tested on HP Mini 5102. Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/hp-wmi.c188
1 files changed, 187 insertions, 1 deletions
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 524ffabc2866..1bc4a7539ba9 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -2,6 +2,7 @@
2 * HP WMI hotkeys 2 * HP WMI hotkeys
3 * 3 *
4 * Copyright (C) 2008 Red Hat <mjg@redhat.com> 4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5 * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
5 * 6 *
6 * Portions based on wistron_btns.c: 7 * Portions based on wistron_btns.c:
7 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> 8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
@@ -51,6 +52,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
51#define HPWMI_HARDWARE_QUERY 0x4 52#define HPWMI_HARDWARE_QUERY 0x4
52#define HPWMI_WIRELESS_QUERY 0x5 53#define HPWMI_WIRELESS_QUERY 0x5
53#define HPWMI_HOTKEY_QUERY 0xc 54#define HPWMI_HOTKEY_QUERY 0xc
55#define HPWMI_WIRELESS2_QUERY 0x1b
54 56
55#define PREFIX "HP WMI: " 57#define PREFIX "HP WMI: "
56#define UNIMP "Unimplemented " 58#define UNIMP "Unimplemented "
@@ -95,6 +97,39 @@ enum hp_return_value {
95 HPWMI_RET_INVALID_PARAMETERS = 0x05, 97 HPWMI_RET_INVALID_PARAMETERS = 0x05,
96}; 98};
97 99
100enum hp_wireless2_bits {
101 HPWMI_POWER_STATE = 0x01,
102 HPWMI_POWER_SOFT = 0x02,
103 HPWMI_POWER_BIOS = 0x04,
104 HPWMI_POWER_HARD = 0x08,
105};
106
107#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
108 != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
109#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
110
111struct bios_rfkill2_device_state {
112 u8 radio_type;
113 u8 bus_type;
114 u16 vendor_id;
115 u16 product_id;
116 u16 subsys_vendor_id;
117 u16 subsys_product_id;
118 u8 rfkill_id;
119 u8 power;
120 u8 unknown[4];
121};
122
123/* 7 devices fit into the 128 byte buffer */
124#define HPWMI_MAX_RFKILL2_DEVICES 7
125
126struct bios_rfkill2_state {
127 u8 unknown[7];
128 u8 count;
129 u8 pad[8];
130 struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
131};
132
98static const struct key_entry hp_wmi_keymap[] = { 133static const struct key_entry hp_wmi_keymap[] = {
99 { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, 134 { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } },
100 { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, 135 { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } },
@@ -114,6 +149,15 @@ static struct rfkill *wifi_rfkill;
114static struct rfkill *bluetooth_rfkill; 149static struct rfkill *bluetooth_rfkill;
115static struct rfkill *wwan_rfkill; 150static struct rfkill *wwan_rfkill;
116 151
152struct rfkill2_device {
153 u8 id;
154 int num;
155 struct rfkill *rfkill;
156};
157
158static int rfkill2_count;
159static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
160
117static const struct dev_pm_ops hp_wmi_pm_ops = { 161static const struct dev_pm_ops hp_wmi_pm_ops = {
118 .resume = hp_wmi_resume_handler, 162 .resume = hp_wmi_resume_handler,
119 .restore = hp_wmi_resume_handler, 163 .restore = hp_wmi_resume_handler,
@@ -308,6 +352,51 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
308 return true; 352 return true;
309} 353}
310 354
355static int hp_wmi_rfkill2_set_block(void *data, bool blocked)
356{
357 int rfkill_id = (int)(long)data;
358 char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked };
359
360 if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
361 buffer, sizeof(buffer), 0))
362 return -EINVAL;
363 return 0;
364}
365
366static const struct rfkill_ops hp_wmi_rfkill2_ops = {
367 .set_block = hp_wmi_rfkill2_set_block,
368};
369
370static int hp_wmi_rfkill2_refresh(void)
371{
372 int err, i;
373 struct bios_rfkill2_state state;
374
375 err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
376 0, sizeof(state));
377 if (err)
378 return err;
379
380 for (i = 0; i < rfkill2_count; i++) {
381 int num = rfkill2[i].num;
382 struct bios_rfkill2_device_state *devstate;
383 devstate = &state.device[num];
384
385 if (num >= state.count ||
386 devstate->rfkill_id != rfkill2[i].id) {
387 printk(KERN_WARNING PREFIX "power configuration of "
388 "the wireless devices unexpectedly changed\n");
389 continue;
390 }
391
392 rfkill_set_states(rfkill2[i].rfkill,
393 IS_SWBLOCKED(devstate->power),
394 IS_HWBLOCKED(devstate->power));
395 }
396
397 return 0;
398}
399
311static ssize_t show_display(struct device *dev, struct device_attribute *attr, 400static ssize_t show_display(struct device *dev, struct device_attribute *attr,
312 char *buf) 401 char *buf)
313{ 402{
@@ -442,6 +531,11 @@ static void hp_wmi_notify(u32 value, void *context)
442 key_code); 531 key_code);
443 break; 532 break;
444 case HPWMI_WIRELESS: 533 case HPWMI_WIRELESS:
534 if (rfkill2_count) {
535 hp_wmi_rfkill2_refresh();
536 break;
537 }
538
445 if (wifi_rfkill) 539 if (wifi_rfkill)
446 rfkill_set_states(wifi_rfkill, 540 rfkill_set_states(wifi_rfkill,
447 hp_wmi_get_sw_state(HPWMI_WIFI), 541 hp_wmi_get_sw_state(HPWMI_WIFI),
@@ -601,6 +695,87 @@ register_wifi_error:
601 return err; 695 return err;
602} 696}
603 697
698static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
699{
700 int err, i;
701 struct bios_rfkill2_state state;
702 err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
703 0, sizeof(state));
704 if (err)
705 return err;
706
707 if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
708 printk(KERN_WARNING PREFIX "unable to parse 0x1b query output\n");
709 return -EINVAL;
710 }
711
712 for (i = 0; i < state.count; i++) {
713 struct rfkill *rfkill;
714 enum rfkill_type type;
715 char *name;
716 switch (state.device[i].radio_type) {
717 case HPWMI_WIFI:
718 type = RFKILL_TYPE_WLAN;
719 name = "hp-wifi";
720 break;
721 case HPWMI_BLUETOOTH:
722 type = RFKILL_TYPE_BLUETOOTH;
723 name = "hp-bluetooth";
724 break;
725 case HPWMI_WWAN:
726 type = RFKILL_TYPE_WWAN;
727 name = "hp-wwan";
728 break;
729 default:
730 printk(KERN_WARNING PREFIX "unknown device type 0x%x\n",
731 state.device[i].radio_type);
732 continue;
733 }
734
735 if (!state.device[i].vendor_id) {
736 printk(KERN_WARNING PREFIX "zero device %d while %d "
737 "reported\n", i, state.count);
738 continue;
739 }
740
741 rfkill = rfkill_alloc(name, &device->dev, type,
742 &hp_wmi_rfkill2_ops, (void *)(long)i);
743 if (!rfkill) {
744 err = -ENOMEM;
745 goto fail;
746 }
747
748 rfkill2[rfkill2_count].id = state.device[i].rfkill_id;
749 rfkill2[rfkill2_count].num = i;
750 rfkill2[rfkill2_count].rfkill = rfkill;
751
752 rfkill_init_sw_state(rfkill,
753 IS_SWBLOCKED(state.device[i].power));
754 rfkill_set_hw_state(rfkill,
755 IS_HWBLOCKED(state.device[i].power));
756
757 if (!(state.device[i].power & HPWMI_POWER_BIOS))
758 printk(KERN_INFO PREFIX "device %s blocked by BIOS\n",
759 name);
760
761 err = rfkill_register(rfkill);
762 if (err) {
763 rfkill_destroy(rfkill);
764 goto fail;
765 }
766
767 rfkill2_count++;
768 }
769
770 return 0;
771fail:
772 for (; rfkill2_count > 0; rfkill2_count--) {
773 rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill);
774 rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill);
775 }
776 return err;
777}
778
604static int __devinit hp_wmi_bios_setup(struct platform_device *device) 779static int __devinit hp_wmi_bios_setup(struct platform_device *device)
605{ 780{
606 int err; 781 int err;
@@ -609,8 +784,10 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
609 wifi_rfkill = NULL; 784 wifi_rfkill = NULL;
610 bluetooth_rfkill = NULL; 785 bluetooth_rfkill = NULL;
611 wwan_rfkill = NULL; 786 wwan_rfkill = NULL;
787 rfkill2_count = 0;
612 788
613 hp_wmi_rfkill_setup(device); 789 if (hp_wmi_rfkill_setup(device))
790 hp_wmi_rfkill2_setup(device);
614 791
615 err = device_create_file(&device->dev, &dev_attr_display); 792 err = device_create_file(&device->dev, &dev_attr_display);
616 if (err) 793 if (err)
@@ -636,8 +813,14 @@ add_sysfs_error:
636 813
637static int __exit hp_wmi_bios_remove(struct platform_device *device) 814static int __exit hp_wmi_bios_remove(struct platform_device *device)
638{ 815{
816 int i;
639 cleanup_sysfs(device); 817 cleanup_sysfs(device);
640 818
819 for (i = 0; i < rfkill2_count; i++) {
820 rfkill_unregister(rfkill2[i].rfkill);
821 rfkill_destroy(rfkill2[i].rfkill);
822 }
823
641 if (wifi_rfkill) { 824 if (wifi_rfkill) {
642 rfkill_unregister(wifi_rfkill); 825 rfkill_unregister(wifi_rfkill);
643 rfkill_destroy(wifi_rfkill); 826 rfkill_destroy(wifi_rfkill);
@@ -670,6 +853,9 @@ static int hp_wmi_resume_handler(struct device *device)
670 input_sync(hp_wmi_input_dev); 853 input_sync(hp_wmi_input_dev);
671 } 854 }
672 855
856 if (rfkill2_count)
857 hp_wmi_rfkill2_refresh();
858
673 if (wifi_rfkill) 859 if (wifi_rfkill)
674 rfkill_set_states(wifi_rfkill, 860 rfkill_set_states(wifi_rfkill,
675 hp_wmi_get_sw_state(HPWMI_WIFI), 861 hp_wmi_get_sw_state(HPWMI_WIFI),