aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/acer-wmi.c
diff options
context:
space:
mode:
authorCarlos Corbacho <carlos@strangeworlds.co.uk>2008-10-08 16:40:21 -0400
committerLen Brown <len.brown@intel.com>2008-10-08 16:55:45 -0400
commit0606e1abfcb66c3a29722162f5d2c7ad8b638237 (patch)
tree9e270c4002f4b532c1492892e50d374390b3b994 /drivers/misc/acer-wmi.c
parent4330ed8ed4da360ac1ca14b0fddff4c05b10de16 (diff)
acer-wmi: Add rfkill support for wireless and bluetooth
This patch implements rfkill support for the wireless and bluetooth devices commonly found on Acer laptops. For now, we will always poll these devices once a second to guarantee we can catch state changes. On newer Acer laptops, it may be possible to rely on WMI events to do this instead, and experimental support for this will be added in a later patch. 3G has been deliberately left off for now, as we still have no way to detect it, (nor, AFAIK, has any Linux user tried the code) and on laptops that don't support 3G, trying to poll for the status will leave the logs full of ACPI tracebacks. The old sysfs interface for wireless and bluetooth will be removed in a later patch. (Thanks to Henrique de Moraes Holschuh and Dmitry Torokhov for reviewing this patch). Signed-off-by: Carlos Corbacho <carlos@strangeworlds.co.uk> Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc/acer-wmi.c')
-rw-r--r--drivers/misc/acer-wmi.c130
1 files changed, 129 insertions, 1 deletions
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
index d8b0d326e452..e3e11e88747e 100644
--- a/drivers/misc/acer-wmi.c
+++ b/drivers/misc/acer-wmi.c
@@ -33,6 +33,8 @@
33#include <linux/platform_device.h> 33#include <linux/platform_device.h>
34#include <linux/acpi.h> 34#include <linux/acpi.h>
35#include <linux/i8042.h> 35#include <linux/i8042.h>
36#include <linux/rfkill.h>
37#include <linux/workqueue.h>
36#include <linux/debugfs.h> 38#include <linux/debugfs.h>
37 39
38#include <acpi/acpi_drivers.h> 40#include <acpi/acpi_drivers.h>
@@ -157,6 +159,9 @@ struct acer_debug {
157 u32 wmid_devices; 159 u32 wmid_devices;
158}; 160};
159 161
162static struct rfkill *wireless_rfkill;
163static struct rfkill *bluetooth_rfkill;
164
160/* Each low-level interface must define at least some of the following */ 165/* Each low-level interface must define at least some of the following */
161struct wmi_interface { 166struct wmi_interface {
162 /* The WMI device type */ 167 /* The WMI device type */
@@ -933,6 +938,125 @@ static void acer_backlight_exit(void)
933} 938}
934 939
935/* 940/*
941 * Rfkill devices
942 */
943static struct workqueue_struct *rfkill_workqueue;
944
945static void acer_rfkill_update(struct work_struct *ignored);
946static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
947static void acer_rfkill_update(struct work_struct *ignored)
948{
949 u32 state;
950 acpi_status status;
951
952 status = get_u32(&state, ACER_CAP_WIRELESS);
953 if (ACPI_SUCCESS(status))
954 rfkill_force_state(wireless_rfkill, state ?
955 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
956
957 if (has_cap(ACER_CAP_BLUETOOTH)) {
958 status = get_u32(&state, ACER_CAP_BLUETOOTH);
959 if (ACPI_SUCCESS(status))
960 rfkill_force_state(bluetooth_rfkill, state ?
961 RFKILL_STATE_UNBLOCKED :
962 RFKILL_STATE_SOFT_BLOCKED);
963 }
964
965 queue_delayed_work(rfkill_workqueue, &acer_rfkill_work,
966 round_jiffies_relative(HZ));
967}
968
969static int acer_rfkill_set(void *data, enum rfkill_state state)
970{
971 acpi_status status;
972 u32 *cap = data;
973 status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
974 if (ACPI_FAILURE(status))
975 return -ENODEV;
976 return 0;
977}
978
979static struct rfkill * acer_rfkill_register(struct device *dev,
980enum rfkill_type type, char *name, u32 cap)
981{
982 int err;
983 u32 state;
984 u32 *data;
985 struct rfkill *rfkill_dev;
986
987 rfkill_dev = rfkill_allocate(dev, type);
988 if (!rfkill_dev)
989 return ERR_PTR(-ENOMEM);
990 rfkill_dev->name = name;
991 get_u32(&state, cap);
992 rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
993 RFKILL_STATE_SOFT_BLOCKED;
994 data = kzalloc(sizeof(u32), GFP_KERNEL);
995 if (!data) {
996 rfkill_free(rfkill_dev);
997 return ERR_PTR(-ENOMEM);
998 }
999 *data = cap;
1000 rfkill_dev->data = data;
1001 rfkill_dev->toggle_radio = acer_rfkill_set;
1002 rfkill_dev->user_claim_unsupported = 1;
1003
1004 err = rfkill_register(rfkill_dev);
1005 if (err) {
1006 kfree(rfkill_dev->data);
1007 rfkill_free(rfkill_dev);
1008 return ERR_PTR(err);
1009 }
1010 return rfkill_dev;
1011}
1012
1013static int acer_rfkill_init(struct device *dev)
1014{
1015 wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
1016 "acer-wireless", ACER_CAP_WIRELESS);
1017 if (IS_ERR(wireless_rfkill))
1018 return PTR_ERR(wireless_rfkill);
1019
1020 if (has_cap(ACER_CAP_BLUETOOTH)) {
1021 bluetooth_rfkill = acer_rfkill_register(dev,
1022 RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
1023 ACER_CAP_BLUETOOTH);
1024 if (IS_ERR(bluetooth_rfkill)) {
1025 kfree(wireless_rfkill->data);
1026 rfkill_unregister(wireless_rfkill);
1027 return PTR_ERR(bluetooth_rfkill);
1028 }
1029 }
1030
1031 rfkill_workqueue = create_singlethread_workqueue("rfkill_workqueue");
1032 if (!rfkill_workqueue) {
1033 if (has_cap(ACER_CAP_BLUETOOTH)) {
1034 kfree(bluetooth_rfkill->data);
1035 rfkill_unregister(bluetooth_rfkill);
1036 }
1037 kfree(wireless_rfkill->data);
1038 rfkill_unregister(wireless_rfkill);
1039 return -ENOMEM;
1040 }
1041 queue_delayed_work(rfkill_workqueue, &acer_rfkill_work, HZ);
1042
1043 return 0;
1044}
1045
1046static void acer_rfkill_exit(void)
1047{
1048 cancel_delayed_work_sync(&acer_rfkill_work);
1049 destroy_workqueue(rfkill_workqueue);
1050 kfree(wireless_rfkill->data);
1051 rfkill_unregister(wireless_rfkill);
1052 if (has_cap(ACER_CAP_BLUETOOTH)) {
1053 kfree(wireless_rfkill->data);
1054 rfkill_unregister(bluetooth_rfkill);
1055 }
1056 return;
1057}
1058
1059/*
936 * Read/ write bool sysfs macro 1060 * Read/ write bool sysfs macro
937 */ 1061 */
938#define show_set_bool(value, cap) \ 1062#define show_set_bool(value, cap) \
@@ -1026,7 +1150,9 @@ static int __devinit acer_platform_probe(struct platform_device *device)
1026 goto error_brightness; 1150 goto error_brightness;
1027 } 1151 }
1028 1152
1029 return 0; 1153 err = acer_rfkill_init(&device->dev);
1154
1155 return err;
1030 1156
1031error_brightness: 1157error_brightness:
1032 acer_led_exit(); 1158 acer_led_exit();
@@ -1040,6 +1166,8 @@ static int acer_platform_remove(struct platform_device *device)
1040 acer_led_exit(); 1166 acer_led_exit();
1041 if (has_cap(ACER_CAP_BRIGHTNESS)) 1167 if (has_cap(ACER_CAP_BRIGHTNESS))
1042 acer_backlight_exit(); 1168 acer_backlight_exit();
1169
1170 acer_rfkill_exit();
1043 return 0; 1171 return 0;
1044} 1172}
1045 1173