diff options
Diffstat (limited to 'drivers/misc/acer-wmi.c')
-rw-r--r-- | drivers/misc/acer-wmi.c | 225 |
1 files changed, 140 insertions, 85 deletions
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index d8b0d326e452..0532a2de2ce4 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> |
@@ -123,21 +125,15 @@ enum interface_flags { | |||
123 | 125 | ||
124 | static int max_brightness = 0xF; | 126 | static int max_brightness = 0xF; |
125 | 127 | ||
126 | static int wireless = -1; | ||
127 | static int bluetooth = -1; | ||
128 | static int mailled = -1; | 128 | static int mailled = -1; |
129 | static int brightness = -1; | 129 | static int brightness = -1; |
130 | static int threeg = -1; | 130 | static int threeg = -1; |
131 | static int force_series; | 131 | static int force_series; |
132 | 132 | ||
133 | module_param(mailled, int, 0444); | 133 | module_param(mailled, int, 0444); |
134 | module_param(wireless, int, 0444); | ||
135 | module_param(bluetooth, int, 0444); | ||
136 | module_param(brightness, int, 0444); | 134 | module_param(brightness, int, 0444); |
137 | module_param(threeg, int, 0444); | 135 | module_param(threeg, int, 0444); |
138 | module_param(force_series, int, 0444); | 136 | module_param(force_series, int, 0444); |
139 | MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware"); | ||
140 | MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware"); | ||
141 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); | 137 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); |
142 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); | 138 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); |
143 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); | 139 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); |
@@ -145,8 +141,6 @@ MODULE_PARM_DESC(force_series, "Force a different laptop series"); | |||
145 | 141 | ||
146 | struct acer_data { | 142 | struct acer_data { |
147 | int mailled; | 143 | int mailled; |
148 | int wireless; | ||
149 | int bluetooth; | ||
150 | int threeg; | 144 | int threeg; |
151 | int brightness; | 145 | int brightness; |
152 | }; | 146 | }; |
@@ -157,6 +151,9 @@ struct acer_debug { | |||
157 | u32 wmid_devices; | 151 | u32 wmid_devices; |
158 | }; | 152 | }; |
159 | 153 | ||
154 | static struct rfkill *wireless_rfkill; | ||
155 | static struct rfkill *bluetooth_rfkill; | ||
156 | |||
160 | /* Each low-level interface must define at least some of the following */ | 157 | /* Each low-level interface must define at least some of the following */ |
161 | struct wmi_interface { | 158 | struct wmi_interface { |
162 | /* The WMI device type */ | 159 | /* The WMI device type */ |
@@ -476,7 +473,7 @@ struct wmi_interface *iface) | |||
476 | } | 473 | } |
477 | break; | 474 | break; |
478 | default: | 475 | default: |
479 | return AE_BAD_ADDRESS; | 476 | return AE_ERROR; |
480 | } | 477 | } |
481 | return AE_OK; | 478 | return AE_OK; |
482 | } | 479 | } |
@@ -514,7 +511,7 @@ static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | |||
514 | break; | 511 | break; |
515 | } | 512 | } |
516 | default: | 513 | default: |
517 | return AE_BAD_ADDRESS; | 514 | return AE_ERROR; |
518 | } | 515 | } |
519 | 516 | ||
520 | /* Actually do the set */ | 517 | /* Actually do the set */ |
@@ -689,7 +686,7 @@ struct wmi_interface *iface) | |||
689 | return 0; | 686 | return 0; |
690 | } | 687 | } |
691 | default: | 688 | default: |
692 | return AE_BAD_ADDRESS; | 689 | return AE_ERROR; |
693 | } | 690 | } |
694 | status = WMI_execute_u32(method_id, 0, &result); | 691 | status = WMI_execute_u32(method_id, 0, &result); |
695 | 692 | ||
@@ -735,7 +732,7 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | |||
735 | } | 732 | } |
736 | break; | 733 | break; |
737 | default: | 734 | default: |
738 | return AE_BAD_ADDRESS; | 735 | return AE_ERROR; |
739 | } | 736 | } |
740 | return WMI_execute_u32(method_id, (u32)value, NULL); | 737 | return WMI_execute_u32(method_id, (u32)value, NULL); |
741 | } | 738 | } |
@@ -785,7 +782,7 @@ static struct wmi_interface wmid_interface = { | |||
785 | 782 | ||
786 | static acpi_status get_u32(u32 *value, u32 cap) | 783 | static acpi_status get_u32(u32 *value, u32 cap) |
787 | { | 784 | { |
788 | acpi_status status = AE_BAD_ADDRESS; | 785 | acpi_status status = AE_ERROR; |
789 | 786 | ||
790 | switch (interface->type) { | 787 | switch (interface->type) { |
791 | case ACER_AMW0: | 788 | case ACER_AMW0: |
@@ -846,8 +843,6 @@ static void __init acer_commandline_init(void) | |||
846 | * capability isn't available on the given interface | 843 | * capability isn't available on the given interface |
847 | */ | 844 | */ |
848 | set_u32(mailled, ACER_CAP_MAILLED); | 845 | set_u32(mailled, ACER_CAP_MAILLED); |
849 | set_u32(wireless, ACER_CAP_WIRELESS); | ||
850 | set_u32(bluetooth, ACER_CAP_BLUETOOTH); | ||
851 | set_u32(threeg, ACER_CAP_THREEG); | 846 | set_u32(threeg, ACER_CAP_THREEG); |
852 | set_u32(brightness, ACER_CAP_BRIGHTNESS); | 847 | set_u32(brightness, ACER_CAP_BRIGHTNESS); |
853 | } | 848 | } |
@@ -933,40 +928,135 @@ static void acer_backlight_exit(void) | |||
933 | } | 928 | } |
934 | 929 | ||
935 | /* | 930 | /* |
936 | * Read/ write bool sysfs macro | 931 | * Rfkill devices |
937 | */ | 932 | */ |
938 | #define show_set_bool(value, cap) \ | 933 | static void acer_rfkill_update(struct work_struct *ignored); |
939 | static ssize_t \ | 934 | static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update); |
940 | show_bool_##value(struct device *dev, struct device_attribute *attr, \ | 935 | static void acer_rfkill_update(struct work_struct *ignored) |
941 | char *buf) \ | 936 | { |
942 | { \ | 937 | u32 state; |
943 | u32 result; \ | 938 | acpi_status status; |
944 | acpi_status status = get_u32(&result, cap); \ | 939 | |
945 | if (ACPI_SUCCESS(status)) \ | 940 | status = get_u32(&state, ACER_CAP_WIRELESS); |
946 | return sprintf(buf, "%u\n", result); \ | 941 | if (ACPI_SUCCESS(status)) |
947 | return sprintf(buf, "Read error\n"); \ | 942 | rfkill_force_state(wireless_rfkill, state ? |
948 | } \ | 943 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED); |
949 | \ | 944 | |
950 | static ssize_t \ | 945 | if (has_cap(ACER_CAP_BLUETOOTH)) { |
951 | set_bool_##value(struct device *dev, struct device_attribute *attr, \ | 946 | status = get_u32(&state, ACER_CAP_BLUETOOTH); |
952 | const char *buf, size_t count) \ | 947 | if (ACPI_SUCCESS(status)) |
953 | { \ | 948 | rfkill_force_state(bluetooth_rfkill, state ? |
954 | u32 tmp = simple_strtoul(buf, NULL, 10); \ | 949 | RFKILL_STATE_UNBLOCKED : |
955 | acpi_status status = set_u32(tmp, cap); \ | 950 | RFKILL_STATE_SOFT_BLOCKED); |
956 | if (ACPI_FAILURE(status)) \ | 951 | } |
957 | return -EINVAL; \ | 952 | |
958 | return count; \ | 953 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
959 | } \ | 954 | } |
960 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \ | 955 | |
961 | show_bool_##value, set_bool_##value); | 956 | static int acer_rfkill_set(void *data, enum rfkill_state state) |
962 | 957 | { | |
963 | show_set_bool(wireless, ACER_CAP_WIRELESS); | 958 | acpi_status status; |
964 | show_set_bool(bluetooth, ACER_CAP_BLUETOOTH); | 959 | u32 *cap = data; |
965 | show_set_bool(threeg, ACER_CAP_THREEG); | 960 | status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap); |
961 | if (ACPI_FAILURE(status)) | ||
962 | return -ENODEV; | ||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | static struct rfkill * acer_rfkill_register(struct device *dev, | ||
967 | enum rfkill_type type, char *name, u32 cap) | ||
968 | { | ||
969 | int err; | ||
970 | u32 state; | ||
971 | u32 *data; | ||
972 | struct rfkill *rfkill_dev; | ||
973 | |||
974 | rfkill_dev = rfkill_allocate(dev, type); | ||
975 | if (!rfkill_dev) | ||
976 | return ERR_PTR(-ENOMEM); | ||
977 | rfkill_dev->name = name; | ||
978 | get_u32(&state, cap); | ||
979 | rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED : | ||
980 | RFKILL_STATE_SOFT_BLOCKED; | ||
981 | data = kzalloc(sizeof(u32), GFP_KERNEL); | ||
982 | if (!data) { | ||
983 | rfkill_free(rfkill_dev); | ||
984 | return ERR_PTR(-ENOMEM); | ||
985 | } | ||
986 | *data = cap; | ||
987 | rfkill_dev->data = data; | ||
988 | rfkill_dev->toggle_radio = acer_rfkill_set; | ||
989 | rfkill_dev->user_claim_unsupported = 1; | ||
990 | |||
991 | err = rfkill_register(rfkill_dev); | ||
992 | if (err) { | ||
993 | kfree(rfkill_dev->data); | ||
994 | rfkill_free(rfkill_dev); | ||
995 | return ERR_PTR(err); | ||
996 | } | ||
997 | return rfkill_dev; | ||
998 | } | ||
999 | |||
1000 | static int acer_rfkill_init(struct device *dev) | ||
1001 | { | ||
1002 | wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, | ||
1003 | "acer-wireless", ACER_CAP_WIRELESS); | ||
1004 | if (IS_ERR(wireless_rfkill)) | ||
1005 | return PTR_ERR(wireless_rfkill); | ||
1006 | |||
1007 | if (has_cap(ACER_CAP_BLUETOOTH)) { | ||
1008 | bluetooth_rfkill = acer_rfkill_register(dev, | ||
1009 | RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", | ||
1010 | ACER_CAP_BLUETOOTH); | ||
1011 | if (IS_ERR(bluetooth_rfkill)) { | ||
1012 | kfree(wireless_rfkill->data); | ||
1013 | rfkill_unregister(wireless_rfkill); | ||
1014 | return PTR_ERR(bluetooth_rfkill); | ||
1015 | } | ||
1016 | } | ||
1017 | |||
1018 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | static void acer_rfkill_exit(void) | ||
1024 | { | ||
1025 | cancel_delayed_work_sync(&acer_rfkill_work); | ||
1026 | kfree(wireless_rfkill->data); | ||
1027 | rfkill_unregister(wireless_rfkill); | ||
1028 | if (has_cap(ACER_CAP_BLUETOOTH)) { | ||
1029 | kfree(wireless_rfkill->data); | ||
1030 | rfkill_unregister(bluetooth_rfkill); | ||
1031 | } | ||
1032 | return; | ||
1033 | } | ||
966 | 1034 | ||
967 | /* | 1035 | /* |
968 | * Read interface sysfs macro | 1036 | * sysfs interface |
969 | */ | 1037 | */ |
1038 | static ssize_t show_bool_threeg(struct device *dev, | ||
1039 | struct device_attribute *attr, char *buf) | ||
1040 | { | ||
1041 | u32 result; \ | ||
1042 | acpi_status status = get_u32(&result, ACER_CAP_THREEG); | ||
1043 | if (ACPI_SUCCESS(status)) | ||
1044 | return sprintf(buf, "%u\n", result); | ||
1045 | return sprintf(buf, "Read error\n"); | ||
1046 | } | ||
1047 | |||
1048 | static ssize_t set_bool_threeg(struct device *dev, | ||
1049 | struct device_attribute *attr, const char *buf, size_t count) | ||
1050 | { | ||
1051 | u32 tmp = simple_strtoul(buf, NULL, 10); | ||
1052 | acpi_status status = set_u32(tmp, ACER_CAP_THREEG); | ||
1053 | if (ACPI_FAILURE(status)) | ||
1054 | return -EINVAL; | ||
1055 | return count; | ||
1056 | } | ||
1057 | static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg, | ||
1058 | set_bool_threeg); | ||
1059 | |||
970 | static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | 1060 | static ssize_t show_interface(struct device *dev, struct device_attribute *attr, |
971 | char *buf) | 1061 | char *buf) |
972 | { | 1062 | { |
@@ -1026,7 +1116,9 @@ static int __devinit acer_platform_probe(struct platform_device *device) | |||
1026 | goto error_brightness; | 1116 | goto error_brightness; |
1027 | } | 1117 | } |
1028 | 1118 | ||
1029 | return 0; | 1119 | err = acer_rfkill_init(&device->dev); |
1120 | |||
1121 | return err; | ||
1030 | 1122 | ||
1031 | error_brightness: | 1123 | error_brightness: |
1032 | acer_led_exit(); | 1124 | acer_led_exit(); |
@@ -1040,6 +1132,8 @@ static int acer_platform_remove(struct platform_device *device) | |||
1040 | acer_led_exit(); | 1132 | acer_led_exit(); |
1041 | if (has_cap(ACER_CAP_BRIGHTNESS)) | 1133 | if (has_cap(ACER_CAP_BRIGHTNESS)) |
1042 | acer_backlight_exit(); | 1134 | acer_backlight_exit(); |
1135 | |||
1136 | acer_rfkill_exit(); | ||
1043 | return 0; | 1137 | return 0; |
1044 | } | 1138 | } |
1045 | 1139 | ||
@@ -1052,16 +1146,6 @@ pm_message_t state) | |||
1052 | if (!data) | 1146 | if (!data) |
1053 | return -ENOMEM; | 1147 | return -ENOMEM; |
1054 | 1148 | ||
1055 | if (has_cap(ACER_CAP_WIRELESS)) { | ||
1056 | get_u32(&value, ACER_CAP_WIRELESS); | ||
1057 | data->wireless = value; | ||
1058 | } | ||
1059 | |||
1060 | if (has_cap(ACER_CAP_BLUETOOTH)) { | ||
1061 | get_u32(&value, ACER_CAP_BLUETOOTH); | ||
1062 | data->bluetooth = value; | ||
1063 | } | ||
1064 | |||
1065 | if (has_cap(ACER_CAP_MAILLED)) { | 1149 | if (has_cap(ACER_CAP_MAILLED)) { |
1066 | get_u32(&value, ACER_CAP_MAILLED); | 1150 | get_u32(&value, ACER_CAP_MAILLED); |
1067 | data->mailled = value; | 1151 | data->mailled = value; |
@@ -1082,15 +1166,6 @@ static int acer_platform_resume(struct platform_device *device) | |||
1082 | if (!data) | 1166 | if (!data) |
1083 | return -ENOMEM; | 1167 | return -ENOMEM; |
1084 | 1168 | ||
1085 | if (has_cap(ACER_CAP_WIRELESS)) | ||
1086 | set_u32(data->wireless, ACER_CAP_WIRELESS); | ||
1087 | |||
1088 | if (has_cap(ACER_CAP_BLUETOOTH)) | ||
1089 | set_u32(data->bluetooth, ACER_CAP_BLUETOOTH); | ||
1090 | |||
1091 | if (has_cap(ACER_CAP_THREEG)) | ||
1092 | set_u32(data->threeg, ACER_CAP_THREEG); | ||
1093 | |||
1094 | if (has_cap(ACER_CAP_MAILLED)) | 1169 | if (has_cap(ACER_CAP_MAILLED)) |
1095 | set_u32(data->mailled, ACER_CAP_MAILLED); | 1170 | set_u32(data->mailled, ACER_CAP_MAILLED); |
1096 | 1171 | ||
@@ -1115,12 +1190,6 @@ static struct platform_device *acer_platform_device; | |||
1115 | 1190 | ||
1116 | static int remove_sysfs(struct platform_device *device) | 1191 | static int remove_sysfs(struct platform_device *device) |
1117 | { | 1192 | { |
1118 | if (has_cap(ACER_CAP_WIRELESS)) | ||
1119 | device_remove_file(&device->dev, &dev_attr_wireless); | ||
1120 | |||
1121 | if (has_cap(ACER_CAP_BLUETOOTH)) | ||
1122 | device_remove_file(&device->dev, &dev_attr_bluetooth); | ||
1123 | |||
1124 | if (has_cap(ACER_CAP_THREEG)) | 1193 | if (has_cap(ACER_CAP_THREEG)) |
1125 | device_remove_file(&device->dev, &dev_attr_threeg); | 1194 | device_remove_file(&device->dev, &dev_attr_threeg); |
1126 | 1195 | ||
@@ -1133,20 +1202,6 @@ static int create_sysfs(void) | |||
1133 | { | 1202 | { |
1134 | int retval = -ENOMEM; | 1203 | int retval = -ENOMEM; |
1135 | 1204 | ||
1136 | if (has_cap(ACER_CAP_WIRELESS)) { | ||
1137 | retval = device_create_file(&acer_platform_device->dev, | ||
1138 | &dev_attr_wireless); | ||
1139 | if (retval) | ||
1140 | goto error_sysfs; | ||
1141 | } | ||
1142 | |||
1143 | if (has_cap(ACER_CAP_BLUETOOTH)) { | ||
1144 | retval = device_create_file(&acer_platform_device->dev, | ||
1145 | &dev_attr_bluetooth); | ||
1146 | if (retval) | ||
1147 | goto error_sysfs; | ||
1148 | } | ||
1149 | |||
1150 | if (has_cap(ACER_CAP_THREEG)) { | 1205 | if (has_cap(ACER_CAP_THREEG)) { |
1151 | retval = device_create_file(&acer_platform_device->dev, | 1206 | retval = device_create_file(&acer_platform_device->dev, |
1152 | &dev_attr_threeg); | 1207 | &dev_attr_threeg); |