diff options
Diffstat (limited to 'drivers/misc/acer-wmi.c')
-rw-r--r-- | drivers/misc/acer-wmi.c | 130 |
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 | ||
162 | static struct rfkill *wireless_rfkill; | ||
163 | static 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 */ |
161 | struct wmi_interface { | 166 | struct 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 | */ | ||
943 | static struct workqueue_struct *rfkill_workqueue; | ||
944 | |||
945 | static void acer_rfkill_update(struct work_struct *ignored); | ||
946 | static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update); | ||
947 | static 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 | |||
969 | static 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 | |||
979 | static struct rfkill * acer_rfkill_register(struct device *dev, | ||
980 | enum 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 | |||
1013 | static 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 | |||
1046 | static 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 | ||
1031 | error_brightness: | 1157 | error_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 | ||