diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 31 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 52 | ||||
-rw-r--r-- | drivers/platform/x86/acerhdf.c | 602 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 101 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 56 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 141 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 185 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 194 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 876 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 160 |
11 files changed, 1466 insertions, 933 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 284ebaca6e45..5613483db141 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -21,7 +21,7 @@ config ACER_WMI | |||
21 | depends on NEW_LEDS | 21 | depends on NEW_LEDS |
22 | depends on BACKLIGHT_CLASS_DEVICE | 22 | depends on BACKLIGHT_CLASS_DEVICE |
23 | depends on SERIO_I8042 | 23 | depends on SERIO_I8042 |
24 | depends on RFKILL | 24 | depends on RFKILL || RFKILL = n |
25 | select ACPI_WMI | 25 | select ACPI_WMI |
26 | ---help--- | 26 | ---help--- |
27 | This is a driver for newer Acer (and Wistron) laptops. It adds | 27 | This is a driver for newer Acer (and Wistron) laptops. It adds |
@@ -34,6 +34,23 @@ config ACER_WMI | |||
34 | If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M | 34 | If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M |
35 | here. | 35 | here. |
36 | 36 | ||
37 | config ACERHDF | ||
38 | tristate "Acer Aspire One temperature and fan driver" | ||
39 | depends on THERMAL && THERMAL_HWMON && ACPI | ||
40 | ---help--- | ||
41 | This is a driver for Acer Aspire One netbooks. It allows to access | ||
42 | the temperature sensor and to control the fan. | ||
43 | |||
44 | After loading this driver the BIOS is still in control of the fan. | ||
45 | To let the kernel handle the fan, do: | ||
46 | echo -n enabled > /sys/class/thermal/thermal_zone0/mode | ||
47 | |||
48 | For more information about this driver see | ||
49 | <http://piie.net/files/acerhdf_README.txt> | ||
50 | |||
51 | If you have an Acer Aspire One netbook, say Y or M | ||
52 | here. | ||
53 | |||
37 | config ASUS_LAPTOP | 54 | config ASUS_LAPTOP |
38 | tristate "Asus Laptop Extras (EXPERIMENTAL)" | 55 | tristate "Asus Laptop Extras (EXPERIMENTAL)" |
39 | depends on ACPI | 56 | depends on ACPI |
@@ -60,7 +77,7 @@ config DELL_LAPTOP | |||
60 | depends on DCDBAS | 77 | depends on DCDBAS |
61 | depends on EXPERIMENTAL | 78 | depends on EXPERIMENTAL |
62 | depends on BACKLIGHT_CLASS_DEVICE | 79 | depends on BACKLIGHT_CLASS_DEVICE |
63 | depends on RFKILL | 80 | depends on RFKILL || RFKILL = n |
64 | depends on POWER_SUPPLY | 81 | depends on POWER_SUPPLY |
65 | default n | 82 | default n |
66 | ---help--- | 83 | ---help--- |
@@ -117,7 +134,7 @@ config HP_WMI | |||
117 | tristate "HP WMI extras" | 134 | tristate "HP WMI extras" |
118 | depends on ACPI_WMI | 135 | depends on ACPI_WMI |
119 | depends on INPUT | 136 | depends on INPUT |
120 | depends on RFKILL | 137 | depends on RFKILL || RFKILL = n |
121 | help | 138 | help |
122 | Say Y here if you want to support WMI-based hotkeys on HP laptops and | 139 | Say Y here if you want to support WMI-based hotkeys on HP laptops and |
123 | to read data from WMI such as docking or ambient light sensor state. | 140 | to read data from WMI such as docking or ambient light sensor state. |
@@ -196,14 +213,13 @@ config THINKPAD_ACPI | |||
196 | tristate "ThinkPad ACPI Laptop Extras" | 213 | tristate "ThinkPad ACPI Laptop Extras" |
197 | depends on ACPI | 214 | depends on ACPI |
198 | depends on INPUT | 215 | depends on INPUT |
216 | depends on RFKILL || RFKILL = n | ||
199 | select BACKLIGHT_LCD_SUPPORT | 217 | select BACKLIGHT_LCD_SUPPORT |
200 | select BACKLIGHT_CLASS_DEVICE | 218 | select BACKLIGHT_CLASS_DEVICE |
201 | select HWMON | 219 | select HWMON |
202 | select NVRAM | 220 | select NVRAM |
203 | select NEW_LEDS | 221 | select NEW_LEDS |
204 | select LEDS_CLASS | 222 | select LEDS_CLASS |
205 | select NET | ||
206 | select RFKILL | ||
207 | ---help--- | 223 | ---help--- |
208 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds | 224 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds |
209 | support for Fn-Fx key combinations, Bluetooth control, video | 225 | support for Fn-Fx key combinations, Bluetooth control, video |
@@ -338,9 +354,9 @@ config EEEPC_LAPTOP | |||
338 | depends on ACPI | 354 | depends on ACPI |
339 | depends on INPUT | 355 | depends on INPUT |
340 | depends on EXPERIMENTAL | 356 | depends on EXPERIMENTAL |
357 | depends on RFKILL || RFKILL = n | ||
341 | select BACKLIGHT_CLASS_DEVICE | 358 | select BACKLIGHT_CLASS_DEVICE |
342 | select HWMON | 359 | select HWMON |
343 | select RFKILL | ||
344 | ---help--- | 360 | ---help--- |
345 | This driver supports the Fn-Fx keys on Eee PC laptops. | 361 | This driver supports the Fn-Fx keys on Eee PC laptops. |
346 | It also adds the ability to switch camera/wlan on/off. | 362 | It also adds the ability to switch camera/wlan on/off. |
@@ -405,9 +421,8 @@ config ACPI_TOSHIBA | |||
405 | tristate "Toshiba Laptop Extras" | 421 | tristate "Toshiba Laptop Extras" |
406 | depends on ACPI | 422 | depends on ACPI |
407 | depends on INPUT | 423 | depends on INPUT |
424 | depends on RFKILL || RFKILL = n | ||
408 | select INPUT_POLLDEV | 425 | select INPUT_POLLDEV |
409 | select NET | ||
410 | select RFKILL | ||
411 | select BACKLIGHT_CLASS_DEVICE | 426 | select BACKLIGHT_CLASS_DEVICE |
412 | ---help--- | 427 | ---help--- |
413 | This driver adds support for access to certain system settings | 428 | This driver adds support for access to certain system settings |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e40c7bd1b87e..641b8bfa5538 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o | |||
9 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o | 9 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o |
10 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o | 10 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o |
11 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o | 11 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o |
12 | obj-$(CONFIG_ACERHDF) += acerhdf.o | ||
12 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | 13 | obj-$(CONFIG_HP_WMI) += hp-wmi.o |
13 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 14 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
14 | obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o | 15 | obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o |
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 0f6e43bf4fc2..be2fd6f91639 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -958,59 +958,47 @@ static void acer_rfkill_update(struct work_struct *ignored) | |||
958 | 958 | ||
959 | status = get_u32(&state, ACER_CAP_WIRELESS); | 959 | status = get_u32(&state, ACER_CAP_WIRELESS); |
960 | if (ACPI_SUCCESS(status)) | 960 | if (ACPI_SUCCESS(status)) |
961 | rfkill_force_state(wireless_rfkill, state ? | 961 | rfkill_set_sw_state(wireless_rfkill, !state); |
962 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED); | ||
963 | 962 | ||
964 | if (has_cap(ACER_CAP_BLUETOOTH)) { | 963 | if (has_cap(ACER_CAP_BLUETOOTH)) { |
965 | status = get_u32(&state, ACER_CAP_BLUETOOTH); | 964 | status = get_u32(&state, ACER_CAP_BLUETOOTH); |
966 | if (ACPI_SUCCESS(status)) | 965 | if (ACPI_SUCCESS(status)) |
967 | rfkill_force_state(bluetooth_rfkill, state ? | 966 | rfkill_set_sw_state(bluetooth_rfkill, !state); |
968 | RFKILL_STATE_UNBLOCKED : | ||
969 | RFKILL_STATE_SOFT_BLOCKED); | ||
970 | } | 967 | } |
971 | 968 | ||
972 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 969 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
973 | } | 970 | } |
974 | 971 | ||
975 | static int acer_rfkill_set(void *data, enum rfkill_state state) | 972 | static int acer_rfkill_set(void *data, bool blocked) |
976 | { | 973 | { |
977 | acpi_status status; | 974 | acpi_status status; |
978 | u32 *cap = data; | 975 | u32 cap = (unsigned long)data; |
979 | status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap); | 976 | status = set_u32(!!blocked, cap); |
980 | if (ACPI_FAILURE(status)) | 977 | if (ACPI_FAILURE(status)) |
981 | return -ENODEV; | 978 | return -ENODEV; |
982 | return 0; | 979 | return 0; |
983 | } | 980 | } |
984 | 981 | ||
985 | static struct rfkill * acer_rfkill_register(struct device *dev, | 982 | static const struct rfkill_ops acer_rfkill_ops = { |
986 | enum rfkill_type type, char *name, u32 cap) | 983 | .set_block = acer_rfkill_set, |
984 | }; | ||
985 | |||
986 | static struct rfkill *acer_rfkill_register(struct device *dev, | ||
987 | enum rfkill_type type, | ||
988 | char *name, u32 cap) | ||
987 | { | 989 | { |
988 | int err; | 990 | int err; |
989 | u32 state; | ||
990 | u32 *data; | ||
991 | struct rfkill *rfkill_dev; | 991 | struct rfkill *rfkill_dev; |
992 | 992 | ||
993 | rfkill_dev = rfkill_allocate(dev, type); | 993 | rfkill_dev = rfkill_alloc(name, dev, type, |
994 | &acer_rfkill_ops, | ||
995 | (void *)(unsigned long)cap); | ||
994 | if (!rfkill_dev) | 996 | if (!rfkill_dev) |
995 | return ERR_PTR(-ENOMEM); | 997 | return ERR_PTR(-ENOMEM); |
996 | rfkill_dev->name = name; | ||
997 | get_u32(&state, cap); | ||
998 | rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED : | ||
999 | RFKILL_STATE_SOFT_BLOCKED; | ||
1000 | data = kzalloc(sizeof(u32), GFP_KERNEL); | ||
1001 | if (!data) { | ||
1002 | rfkill_free(rfkill_dev); | ||
1003 | return ERR_PTR(-ENOMEM); | ||
1004 | } | ||
1005 | *data = cap; | ||
1006 | rfkill_dev->data = data; | ||
1007 | rfkill_dev->toggle_radio = acer_rfkill_set; | ||
1008 | rfkill_dev->user_claim_unsupported = 1; | ||
1009 | 998 | ||
1010 | err = rfkill_register(rfkill_dev); | 999 | err = rfkill_register(rfkill_dev); |
1011 | if (err) { | 1000 | if (err) { |
1012 | kfree(rfkill_dev->data); | 1001 | rfkill_destroy(rfkill_dev); |
1013 | rfkill_free(rfkill_dev); | ||
1014 | return ERR_PTR(err); | 1002 | return ERR_PTR(err); |
1015 | } | 1003 | } |
1016 | return rfkill_dev; | 1004 | return rfkill_dev; |
@@ -1028,8 +1016,8 @@ static int acer_rfkill_init(struct device *dev) | |||
1028 | RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", | 1016 | RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", |
1029 | ACER_CAP_BLUETOOTH); | 1017 | ACER_CAP_BLUETOOTH); |
1030 | if (IS_ERR(bluetooth_rfkill)) { | 1018 | if (IS_ERR(bluetooth_rfkill)) { |
1031 | kfree(wireless_rfkill->data); | ||
1032 | rfkill_unregister(wireless_rfkill); | 1019 | rfkill_unregister(wireless_rfkill); |
1020 | rfkill_destroy(wireless_rfkill); | ||
1033 | return PTR_ERR(bluetooth_rfkill); | 1021 | return PTR_ERR(bluetooth_rfkill); |
1034 | } | 1022 | } |
1035 | } | 1023 | } |
@@ -1042,11 +1030,13 @@ static int acer_rfkill_init(struct device *dev) | |||
1042 | static void acer_rfkill_exit(void) | 1030 | static void acer_rfkill_exit(void) |
1043 | { | 1031 | { |
1044 | cancel_delayed_work_sync(&acer_rfkill_work); | 1032 | cancel_delayed_work_sync(&acer_rfkill_work); |
1045 | kfree(wireless_rfkill->data); | 1033 | |
1046 | rfkill_unregister(wireless_rfkill); | 1034 | rfkill_unregister(wireless_rfkill); |
1035 | rfkill_destroy(wireless_rfkill); | ||
1036 | |||
1047 | if (has_cap(ACER_CAP_BLUETOOTH)) { | 1037 | if (has_cap(ACER_CAP_BLUETOOTH)) { |
1048 | kfree(bluetooth_rfkill->data); | ||
1049 | rfkill_unregister(bluetooth_rfkill); | 1038 | rfkill_unregister(bluetooth_rfkill); |
1039 | rfkill_destroy(bluetooth_rfkill); | ||
1050 | } | 1040 | } |
1051 | return; | 1041 | return; |
1052 | } | 1042 | } |
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c new file mode 100644 index 000000000000..bdfee177eefb --- /dev/null +++ b/drivers/platform/x86/acerhdf.c | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * acerhdf - A driver which monitors the temperature | ||
3 | * of the aspire one netbook, turns on/off the fan | ||
4 | * as soon as the upper/lower threshold is reached. | ||
5 | * | ||
6 | * (C) 2009 - Peter Feuerer peter (a) piie.net | ||
7 | * http://piie.net | ||
8 | * 2009 Borislav Petkov <petkovbb@gmail.com> | ||
9 | * | ||
10 | * Inspired by and many thanks to: | ||
11 | * o acerfand - Rachel Greenham | ||
12 | * o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com | ||
13 | * - Petr Tomasek tomasek (#) etf,cuni,cz | ||
14 | * - Carlos Corbacho cathectic (at) gmail.com | ||
15 | * o lkml - Matthew Garrett | ||
16 | * - Borislav Petkov | ||
17 | * - Andreas Mohr | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License as published by | ||
21 | * the Free Software Foundation; either version 2 of the License, or | ||
22 | * (at your option) any later version. | ||
23 | * | ||
24 | * This program is distributed in the hope that it will be useful, | ||
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
27 | * GNU General Public License for more details. | ||
28 | * | ||
29 | * You should have received a copy of the GNU General Public License | ||
30 | * along with this program; if not, write to the Free Software | ||
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
32 | */ | ||
33 | |||
34 | #define pr_fmt(fmt) "acerhdf: " fmt | ||
35 | |||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/module.h> | ||
38 | #include <linux/fs.h> | ||
39 | #include <linux/dmi.h> | ||
40 | #include <acpi/acpi_drivers.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/thermal.h> | ||
43 | #include <linux/platform_device.h> | ||
44 | |||
45 | /* | ||
46 | * The driver is started with "kernel mode off" by default. That means, the BIOS | ||
47 | * is still in control of the fan. In this mode the driver allows to read the | ||
48 | * temperature of the cpu and a userspace tool may take over control of the fan. | ||
49 | * If the driver is switched to "kernel mode" (e.g. via module parameter) the | ||
50 | * driver is in full control of the fan. If you want the module to be started in | ||
51 | * kernel mode by default, define the following: | ||
52 | */ | ||
53 | #undef START_IN_KERNEL_MODE | ||
54 | |||
55 | #define DRV_VER "0.5.13" | ||
56 | |||
57 | /* | ||
58 | * According to the Atom N270 datasheet, | ||
59 | * (http://download.intel.com/design/processor/datashts/320032.pdf) the | ||
60 | * CPU's optimal operating limits denoted in junction temperature as | ||
61 | * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So, | ||
62 | * assume 89°C is critical temperature. | ||
63 | */ | ||
64 | #define ACERHDF_TEMP_CRIT 89 | ||
65 | #define ACERHDF_FAN_OFF 0 | ||
66 | #define ACERHDF_FAN_AUTO 1 | ||
67 | |||
68 | /* | ||
69 | * No matter what value the user puts into the fanon variable, turn on the fan | ||
70 | * at 80 degree Celsius to prevent hardware damage | ||
71 | */ | ||
72 | #define ACERHDF_MAX_FANON 80 | ||
73 | |||
74 | /* | ||
75 | * Maximum interval between two temperature checks is 15 seconds, as the die | ||
76 | * can get hot really fast under heavy load (plus we shouldn't forget about | ||
77 | * possible impact of _external_ aggressive sources such as heaters, sun etc.) | ||
78 | */ | ||
79 | #define ACERHDF_MAX_INTERVAL 15 | ||
80 | |||
81 | #ifdef START_IN_KERNEL_MODE | ||
82 | static int kernelmode = 1; | ||
83 | #else | ||
84 | static int kernelmode; | ||
85 | #endif | ||
86 | |||
87 | static unsigned int interval = 10; | ||
88 | static unsigned int fanon = 63; | ||
89 | static unsigned int fanoff = 58; | ||
90 | static unsigned int verbose; | ||
91 | static unsigned int fanstate = ACERHDF_FAN_AUTO; | ||
92 | static char force_bios[16]; | ||
93 | static unsigned int prev_interval; | ||
94 | struct thermal_zone_device *thz_dev; | ||
95 | struct thermal_cooling_device *cl_dev; | ||
96 | struct platform_device *acerhdf_dev; | ||
97 | |||
98 | module_param(kernelmode, uint, 0); | ||
99 | MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); | ||
100 | module_param(interval, uint, 0600); | ||
101 | MODULE_PARM_DESC(interval, "Polling interval of temperature check"); | ||
102 | module_param(fanon, uint, 0600); | ||
103 | MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature"); | ||
104 | module_param(fanoff, uint, 0600); | ||
105 | MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); | ||
106 | module_param(verbose, uint, 0600); | ||
107 | MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); | ||
108 | module_param_string(force_bios, force_bios, 16, 0); | ||
109 | MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); | ||
110 | |||
111 | /* BIOS settings */ | ||
112 | struct bios_settings_t { | ||
113 | const char *vendor; | ||
114 | const char *version; | ||
115 | unsigned char fanreg; | ||
116 | unsigned char tempreg; | ||
117 | unsigned char fancmd[2]; /* fan off and auto commands */ | ||
118 | }; | ||
119 | |||
120 | /* Register addresses and values for different BIOS versions */ | ||
121 | static const struct bios_settings_t bios_tbl[] = { | ||
122 | {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, | ||
123 | {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, | ||
124 | {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, | ||
125 | {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, | ||
126 | {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, | ||
127 | {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, | ||
128 | {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, | ||
129 | {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, | ||
130 | {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, | ||
131 | {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, | ||
132 | {"", "", 0, 0, {0, 0} } | ||
133 | }; | ||
134 | |||
135 | static const struct bios_settings_t *bios_cfg __read_mostly; | ||
136 | |||
137 | |||
138 | static int acerhdf_get_temp(int *temp) | ||
139 | { | ||
140 | u8 read_temp; | ||
141 | |||
142 | if (ec_read(bios_cfg->tempreg, &read_temp)) | ||
143 | return -EINVAL; | ||
144 | |||
145 | *temp = read_temp; | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int acerhdf_get_fanstate(int *state) | ||
151 | { | ||
152 | u8 fan; | ||
153 | bool tmp; | ||
154 | |||
155 | if (ec_read(bios_cfg->fanreg, &fan)) | ||
156 | return -EINVAL; | ||
157 | |||
158 | tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]); | ||
159 | *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static void acerhdf_change_fanstate(int state) | ||
165 | { | ||
166 | unsigned char cmd; | ||
167 | |||
168 | if (verbose) | ||
169 | pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ? | ||
170 | "OFF" : "ON"); | ||
171 | |||
172 | if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { | ||
173 | pr_err("invalid fan state %d requested, setting to auto!\n", | ||
174 | state); | ||
175 | state = ACERHDF_FAN_AUTO; | ||
176 | } | ||
177 | |||
178 | cmd = bios_cfg->fancmd[state]; | ||
179 | fanstate = state; | ||
180 | |||
181 | ec_write(bios_cfg->fanreg, cmd); | ||
182 | } | ||
183 | |||
184 | static void acerhdf_check_param(struct thermal_zone_device *thermal) | ||
185 | { | ||
186 | if (fanon > ACERHDF_MAX_FANON) { | ||
187 | pr_err("fanon temperature too high, set to %d\n", | ||
188 | ACERHDF_MAX_FANON); | ||
189 | fanon = ACERHDF_MAX_FANON; | ||
190 | } | ||
191 | |||
192 | if (kernelmode && prev_interval != interval) { | ||
193 | if (interval > ACERHDF_MAX_INTERVAL) { | ||
194 | pr_err("interval too high, set to %d\n", | ||
195 | ACERHDF_MAX_INTERVAL); | ||
196 | interval = ACERHDF_MAX_INTERVAL; | ||
197 | } | ||
198 | if (verbose) | ||
199 | pr_notice("interval changed to: %d\n", | ||
200 | interval); | ||
201 | thermal->polling_delay = interval*1000; | ||
202 | prev_interval = interval; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * This is the thermal zone callback which does the delayed polling of the fan | ||
208 | * state. We do check /sysfs-originating settings here in acerhdf_check_param() | ||
209 | * as late as the polling interval is since we can't do that in the respective | ||
210 | * accessors of the module parameters. | ||
211 | */ | ||
212 | static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, | ||
213 | unsigned long *t) | ||
214 | { | ||
215 | int temp, err = 0; | ||
216 | |||
217 | acerhdf_check_param(thermal); | ||
218 | |||
219 | err = acerhdf_get_temp(&temp); | ||
220 | if (err) | ||
221 | return err; | ||
222 | |||
223 | if (verbose) | ||
224 | pr_notice("temp %d\n", temp); | ||
225 | |||
226 | *t = temp; | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int acerhdf_bind(struct thermal_zone_device *thermal, | ||
231 | struct thermal_cooling_device *cdev) | ||
232 | { | ||
233 | /* if the cooling device is the one from acerhdf bind it */ | ||
234 | if (cdev != cl_dev) | ||
235 | return 0; | ||
236 | |||
237 | if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { | ||
238 | pr_err("error binding cooling dev\n"); | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int acerhdf_unbind(struct thermal_zone_device *thermal, | ||
245 | struct thermal_cooling_device *cdev) | ||
246 | { | ||
247 | if (cdev != cl_dev) | ||
248 | return 0; | ||
249 | |||
250 | if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { | ||
251 | pr_err("error unbinding cooling dev\n"); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static inline void acerhdf_revert_to_bios_mode(void) | ||
258 | { | ||
259 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
260 | kernelmode = 0; | ||
261 | if (thz_dev) | ||
262 | thz_dev->polling_delay = 0; | ||
263 | pr_notice("kernel mode fan control OFF\n"); | ||
264 | } | ||
265 | static inline void acerhdf_enable_kernelmode(void) | ||
266 | { | ||
267 | kernelmode = 1; | ||
268 | |||
269 | thz_dev->polling_delay = interval*1000; | ||
270 | thermal_zone_device_update(thz_dev); | ||
271 | pr_notice("kernel mode fan control ON\n"); | ||
272 | } | ||
273 | |||
274 | static int acerhdf_get_mode(struct thermal_zone_device *thermal, | ||
275 | enum thermal_device_mode *mode) | ||
276 | { | ||
277 | if (verbose) | ||
278 | pr_notice("kernel mode fan control %d\n", kernelmode); | ||
279 | |||
280 | *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED | ||
281 | : THERMAL_DEVICE_DISABLED; | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * set operation mode; | ||
288 | * enabled: the thermal layer of the kernel takes care about | ||
289 | * the temperature and the fan. | ||
290 | * disabled: the BIOS takes control of the fan. | ||
291 | */ | ||
292 | static int acerhdf_set_mode(struct thermal_zone_device *thermal, | ||
293 | enum thermal_device_mode mode) | ||
294 | { | ||
295 | if (mode == THERMAL_DEVICE_DISABLED && kernelmode) | ||
296 | acerhdf_revert_to_bios_mode(); | ||
297 | else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode) | ||
298 | acerhdf_enable_kernelmode(); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip, | ||
304 | enum thermal_trip_type *type) | ||
305 | { | ||
306 | if (trip == 0) | ||
307 | *type = THERMAL_TRIP_ACTIVE; | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip, | ||
313 | unsigned long *temp) | ||
314 | { | ||
315 | if (trip == 0) | ||
316 | *temp = fanon; | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, | ||
322 | unsigned long *temperature) | ||
323 | { | ||
324 | *temperature = ACERHDF_TEMP_CRIT; | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /* bind callback functions to thermalzone */ | ||
329 | struct thermal_zone_device_ops acerhdf_dev_ops = { | ||
330 | .bind = acerhdf_bind, | ||
331 | .unbind = acerhdf_unbind, | ||
332 | .get_temp = acerhdf_get_ec_temp, | ||
333 | .get_mode = acerhdf_get_mode, | ||
334 | .set_mode = acerhdf_set_mode, | ||
335 | .get_trip_type = acerhdf_get_trip_type, | ||
336 | .get_trip_temp = acerhdf_get_trip_temp, | ||
337 | .get_crit_temp = acerhdf_get_crit_temp, | ||
338 | }; | ||
339 | |||
340 | |||
341 | /* | ||
342 | * cooling device callback functions | ||
343 | * get maximal fan cooling state | ||
344 | */ | ||
345 | static int acerhdf_get_max_state(struct thermal_cooling_device *cdev, | ||
346 | unsigned long *state) | ||
347 | { | ||
348 | *state = 1; | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev, | ||
354 | unsigned long *state) | ||
355 | { | ||
356 | int err = 0, tmp; | ||
357 | |||
358 | err = acerhdf_get_fanstate(&tmp); | ||
359 | if (err) | ||
360 | return err; | ||
361 | |||
362 | *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0; | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /* change current fan state - is overwritten when running in kernel mode */ | ||
367 | static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev, | ||
368 | unsigned long state) | ||
369 | { | ||
370 | int cur_temp, cur_state, err = 0; | ||
371 | |||
372 | if (!kernelmode) | ||
373 | return 0; | ||
374 | |||
375 | err = acerhdf_get_temp(&cur_temp); | ||
376 | if (err) { | ||
377 | pr_err("error reading temperature, hand off control to BIOS\n"); | ||
378 | goto err_out; | ||
379 | } | ||
380 | |||
381 | err = acerhdf_get_fanstate(&cur_state); | ||
382 | if (err) { | ||
383 | pr_err("error reading fan state, hand off control to BIOS\n"); | ||
384 | goto err_out; | ||
385 | } | ||
386 | |||
387 | if (state == 0) { | ||
388 | /* turn fan off only if below fanoff temperature */ | ||
389 | if ((cur_state == ACERHDF_FAN_AUTO) && | ||
390 | (cur_temp < fanoff)) | ||
391 | acerhdf_change_fanstate(ACERHDF_FAN_OFF); | ||
392 | } else { | ||
393 | if (cur_state == ACERHDF_FAN_OFF) | ||
394 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
395 | } | ||
396 | return 0; | ||
397 | |||
398 | err_out: | ||
399 | acerhdf_revert_to_bios_mode(); | ||
400 | return -EINVAL; | ||
401 | } | ||
402 | |||
403 | /* bind fan callbacks to fan device */ | ||
404 | struct thermal_cooling_device_ops acerhdf_cooling_ops = { | ||
405 | .get_max_state = acerhdf_get_max_state, | ||
406 | .get_cur_state = acerhdf_get_cur_state, | ||
407 | .set_cur_state = acerhdf_set_cur_state, | ||
408 | }; | ||
409 | |||
410 | /* suspend / resume functionality */ | ||
411 | static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) | ||
412 | { | ||
413 | if (kernelmode) | ||
414 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
415 | |||
416 | if (verbose) | ||
417 | pr_notice("going suspend\n"); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int acerhdf_resume(struct platform_device *device) | ||
423 | { | ||
424 | if (verbose) | ||
425 | pr_notice("resuming\n"); | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int __devinit acerhdf_probe(struct platform_device *device) | ||
431 | { | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int acerhdf_remove(struct platform_device *device) | ||
436 | { | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | struct platform_driver acerhdf_drv = { | ||
441 | .driver = { | ||
442 | .name = "acerhdf", | ||
443 | .owner = THIS_MODULE, | ||
444 | }, | ||
445 | .probe = acerhdf_probe, | ||
446 | .remove = acerhdf_remove, | ||
447 | .suspend = acerhdf_suspend, | ||
448 | .resume = acerhdf_resume, | ||
449 | }; | ||
450 | |||
451 | |||
452 | /* check hardware */ | ||
453 | static int acerhdf_check_hardware(void) | ||
454 | { | ||
455 | char const *vendor, *version, *product; | ||
456 | int i; | ||
457 | |||
458 | /* get BIOS data */ | ||
459 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); | ||
460 | version = dmi_get_system_info(DMI_BIOS_VERSION); | ||
461 | product = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
462 | |||
463 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); | ||
464 | |||
465 | if (!force_bios[0]) { | ||
466 | if (strncmp(product, "AO", 2)) { | ||
467 | pr_err("no Aspire One hardware found\n"); | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | } else { | ||
471 | pr_info("forcing BIOS version: %s\n", version); | ||
472 | version = force_bios; | ||
473 | kernelmode = 0; | ||
474 | } | ||
475 | |||
476 | if (verbose) | ||
477 | pr_info("BIOS info: %s %s, product: %s\n", | ||
478 | vendor, version, product); | ||
479 | |||
480 | /* search BIOS version and vendor in BIOS settings table */ | ||
481 | for (i = 0; bios_tbl[i].version[0]; i++) { | ||
482 | if (!strcmp(bios_tbl[i].vendor, vendor) && | ||
483 | !strcmp(bios_tbl[i].version, version)) { | ||
484 | bios_cfg = &bios_tbl[i]; | ||
485 | break; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | if (!bios_cfg) { | ||
490 | pr_err("unknown (unsupported) BIOS version %s/%s, " | ||
491 | "please report, aborting!\n", vendor, version); | ||
492 | return -EINVAL; | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * if started with kernel mode off, prevent the kernel from switching | ||
497 | * off the fan | ||
498 | */ | ||
499 | if (!kernelmode) { | ||
500 | pr_notice("Fan control off, to enable do:\n"); | ||
501 | pr_notice("echo -n \"enabled\" > " | ||
502 | "/sys/class/thermal/thermal_zone0/mode\n"); | ||
503 | } | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int acerhdf_register_platform(void) | ||
509 | { | ||
510 | int err = 0; | ||
511 | |||
512 | err = platform_driver_register(&acerhdf_drv); | ||
513 | if (err) | ||
514 | return err; | ||
515 | |||
516 | acerhdf_dev = platform_device_alloc("acerhdf", -1); | ||
517 | platform_device_add(acerhdf_dev); | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | static void acerhdf_unregister_platform(void) | ||
523 | { | ||
524 | if (!acerhdf_dev) | ||
525 | return; | ||
526 | |||
527 | platform_device_del(acerhdf_dev); | ||
528 | platform_driver_unregister(&acerhdf_drv); | ||
529 | } | ||
530 | |||
531 | static int acerhdf_register_thermal(void) | ||
532 | { | ||
533 | cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, | ||
534 | &acerhdf_cooling_ops); | ||
535 | |||
536 | if (IS_ERR(cl_dev)) | ||
537 | return -EINVAL; | ||
538 | |||
539 | thz_dev = thermal_zone_device_register("acerhdf", 1, NULL, | ||
540 | &acerhdf_dev_ops, 0, 0, 0, | ||
541 | (kernelmode) ? interval*1000 : 0); | ||
542 | if (IS_ERR(thz_dev)) | ||
543 | return -EINVAL; | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static void acerhdf_unregister_thermal(void) | ||
549 | { | ||
550 | if (cl_dev) { | ||
551 | thermal_cooling_device_unregister(cl_dev); | ||
552 | cl_dev = NULL; | ||
553 | } | ||
554 | |||
555 | if (thz_dev) { | ||
556 | thermal_zone_device_unregister(thz_dev); | ||
557 | thz_dev = NULL; | ||
558 | } | ||
559 | } | ||
560 | |||
561 | static int __init acerhdf_init(void) | ||
562 | { | ||
563 | int err = 0; | ||
564 | |||
565 | err = acerhdf_check_hardware(); | ||
566 | if (err) | ||
567 | goto out_err; | ||
568 | |||
569 | err = acerhdf_register_platform(); | ||
570 | if (err) | ||
571 | goto err_unreg; | ||
572 | |||
573 | err = acerhdf_register_thermal(); | ||
574 | if (err) | ||
575 | goto err_unreg; | ||
576 | |||
577 | return 0; | ||
578 | |||
579 | err_unreg: | ||
580 | acerhdf_unregister_thermal(); | ||
581 | acerhdf_unregister_platform(); | ||
582 | |||
583 | out_err: | ||
584 | return -ENODEV; | ||
585 | } | ||
586 | |||
587 | static void __exit acerhdf_exit(void) | ||
588 | { | ||
589 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | ||
590 | acerhdf_unregister_thermal(); | ||
591 | acerhdf_unregister_platform(); | ||
592 | } | ||
593 | |||
594 | MODULE_LICENSE("GPL"); | ||
595 | MODULE_AUTHOR("Peter Feuerer"); | ||
596 | MODULE_DESCRIPTION("Aspire One temperature and fan driver"); | ||
597 | MODULE_ALIAS("dmi:*:*Acer*:*:"); | ||
598 | MODULE_ALIAS("dmi:*:*Gateway*:*:"); | ||
599 | MODULE_ALIAS("dmi:*:*Packard Bell*:*:"); | ||
600 | |||
601 | module_init(acerhdf_init); | ||
602 | module_exit(acerhdf_exit); | ||
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index af9f43021172..74909c4aaeea 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -174,10 +174,11 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
174 | result[3]: NVRAM format version number | 174 | result[3]: NVRAM format version number |
175 | */ | 175 | */ |
176 | 176 | ||
177 | static int dell_rfkill_set(int radio, enum rfkill_state state) | 177 | static int dell_rfkill_set(void *data, bool blocked) |
178 | { | 178 | { |
179 | struct calling_interface_buffer buffer; | 179 | struct calling_interface_buffer buffer; |
180 | int disable = (state == RFKILL_STATE_UNBLOCKED) ? 0 : 1; | 180 | int disable = blocked ? 1 : 0; |
181 | unsigned long radio = (unsigned long)data; | ||
181 | 182 | ||
182 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 183 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); |
183 | buffer.input[0] = (1 | (radio<<8) | (disable << 16)); | 184 | buffer.input[0] = (1 | (radio<<8) | (disable << 16)); |
@@ -186,56 +187,24 @@ static int dell_rfkill_set(int radio, enum rfkill_state state) | |||
186 | return 0; | 187 | return 0; |
187 | } | 188 | } |
188 | 189 | ||
189 | static int dell_wifi_set(void *data, enum rfkill_state state) | 190 | static void dell_rfkill_query(struct rfkill *rfkill, void *data) |
190 | { | ||
191 | return dell_rfkill_set(1, state); | ||
192 | } | ||
193 | |||
194 | static int dell_bluetooth_set(void *data, enum rfkill_state state) | ||
195 | { | ||
196 | return dell_rfkill_set(2, state); | ||
197 | } | ||
198 | |||
199 | static int dell_wwan_set(void *data, enum rfkill_state state) | ||
200 | { | ||
201 | return dell_rfkill_set(3, state); | ||
202 | } | ||
203 | |||
204 | static int dell_rfkill_get(int bit, enum rfkill_state *state) | ||
205 | { | 191 | { |
206 | struct calling_interface_buffer buffer; | 192 | struct calling_interface_buffer buffer; |
207 | int status; | 193 | int status; |
208 | int new_state = RFKILL_STATE_HARD_BLOCKED; | 194 | int bit = (unsigned long)data + 16; |
209 | 195 | ||
210 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 196 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); |
211 | dell_send_request(&buffer, 17, 11); | 197 | dell_send_request(&buffer, 17, 11); |
212 | status = buffer.output[1]; | 198 | status = buffer.output[1]; |
213 | 199 | ||
214 | if (status & (1<<16)) | 200 | if (status & BIT(bit)) |
215 | new_state = RFKILL_STATE_SOFT_BLOCKED; | 201 | rfkill_set_hw_state(rfkill, !!(status & BIT(16))); |
216 | |||
217 | if (status & (1<<bit)) | ||
218 | *state = new_state; | ||
219 | else | ||
220 | *state = RFKILL_STATE_UNBLOCKED; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int dell_wifi_get(void *data, enum rfkill_state *state) | ||
226 | { | ||
227 | return dell_rfkill_get(17, state); | ||
228 | } | ||
229 | |||
230 | static int dell_bluetooth_get(void *data, enum rfkill_state *state) | ||
231 | { | ||
232 | return dell_rfkill_get(18, state); | ||
233 | } | 202 | } |
234 | 203 | ||
235 | static int dell_wwan_get(void *data, enum rfkill_state *state) | 204 | static const struct rfkill_ops dell_rfkill_ops = { |
236 | { | 205 | .set_block = dell_rfkill_set, |
237 | return dell_rfkill_get(19, state); | 206 | .query = dell_rfkill_query, |
238 | } | 207 | }; |
239 | 208 | ||
240 | static int dell_setup_rfkill(void) | 209 | static int dell_setup_rfkill(void) |
241 | { | 210 | { |
@@ -248,36 +217,37 @@ static int dell_setup_rfkill(void) | |||
248 | status = buffer.output[1]; | 217 | status = buffer.output[1]; |
249 | 218 | ||
250 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { | 219 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { |
251 | wifi_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WLAN); | 220 | wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN, |
252 | if (!wifi_rfkill) | 221 | &dell_rfkill_ops, (void *) 1); |
222 | if (!wifi_rfkill) { | ||
223 | ret = -ENOMEM; | ||
253 | goto err_wifi; | 224 | goto err_wifi; |
254 | wifi_rfkill->name = "dell-wifi"; | 225 | } |
255 | wifi_rfkill->toggle_radio = dell_wifi_set; | ||
256 | wifi_rfkill->get_state = dell_wifi_get; | ||
257 | ret = rfkill_register(wifi_rfkill); | 226 | ret = rfkill_register(wifi_rfkill); |
258 | if (ret) | 227 | if (ret) |
259 | goto err_wifi; | 228 | goto err_wifi; |
260 | } | 229 | } |
261 | 230 | ||
262 | if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { | 231 | if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { |
263 | bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH); | 232 | bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL, |
264 | if (!bluetooth_rfkill) | 233 | RFKILL_TYPE_BLUETOOTH, |
234 | &dell_rfkill_ops, (void *) 2); | ||
235 | if (!bluetooth_rfkill) { | ||
236 | ret = -ENOMEM; | ||
265 | goto err_bluetooth; | 237 | goto err_bluetooth; |
266 | bluetooth_rfkill->name = "dell-bluetooth"; | 238 | } |
267 | bluetooth_rfkill->toggle_radio = dell_bluetooth_set; | ||
268 | bluetooth_rfkill->get_state = dell_bluetooth_get; | ||
269 | ret = rfkill_register(bluetooth_rfkill); | 239 | ret = rfkill_register(bluetooth_rfkill); |
270 | if (ret) | 240 | if (ret) |
271 | goto err_bluetooth; | 241 | goto err_bluetooth; |
272 | } | 242 | } |
273 | 243 | ||
274 | if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { | 244 | if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { |
275 | wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN); | 245 | wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN, |
276 | if (!wwan_rfkill) | 246 | &dell_rfkill_ops, (void *) 3); |
247 | if (!wwan_rfkill) { | ||
248 | ret = -ENOMEM; | ||
277 | goto err_wwan; | 249 | goto err_wwan; |
278 | wwan_rfkill->name = "dell-wwan"; | 250 | } |
279 | wwan_rfkill->toggle_radio = dell_wwan_set; | ||
280 | wwan_rfkill->get_state = dell_wwan_get; | ||
281 | ret = rfkill_register(wwan_rfkill); | 251 | ret = rfkill_register(wwan_rfkill); |
282 | if (ret) | 252 | if (ret) |
283 | goto err_wwan; | 253 | goto err_wwan; |
@@ -285,22 +255,15 @@ static int dell_setup_rfkill(void) | |||
285 | 255 | ||
286 | return 0; | 256 | return 0; |
287 | err_wwan: | 257 | err_wwan: |
288 | if (wwan_rfkill) | 258 | rfkill_destroy(wwan_rfkill); |
289 | rfkill_free(wwan_rfkill); | 259 | if (bluetooth_rfkill) |
290 | if (bluetooth_rfkill) { | ||
291 | rfkill_unregister(bluetooth_rfkill); | 260 | rfkill_unregister(bluetooth_rfkill); |
292 | bluetooth_rfkill = NULL; | ||
293 | } | ||
294 | err_bluetooth: | 261 | err_bluetooth: |
295 | if (bluetooth_rfkill) | 262 | rfkill_destroy(bluetooth_rfkill); |
296 | rfkill_free(bluetooth_rfkill); | 263 | if (wifi_rfkill) |
297 | if (wifi_rfkill) { | ||
298 | rfkill_unregister(wifi_rfkill); | 264 | rfkill_unregister(wifi_rfkill); |
299 | wifi_rfkill = NULL; | ||
300 | } | ||
301 | err_wifi: | 265 | err_wifi: |
302 | if (wifi_rfkill) | 266 | rfkill_destroy(wifi_rfkill); |
303 | rfkill_free(wifi_rfkill); | ||
304 | 267 | ||
305 | return ret; | 268 | return ret; |
306 | } | 269 | } |
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 2fab94162147..0f900cc9fa7a 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -46,10 +46,53 @@ struct key_entry { | |||
46 | u16 keycode; | 46 | u16 keycode; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | enum { KE_KEY, KE_SW, KE_END }; | 49 | enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; |
50 | |||
51 | /* | ||
52 | * Certain keys are flagged as KE_IGNORE. All of these are either | ||
53 | * notifications (rather than requests for change) or are also sent | ||
54 | * via the keyboard controller so should not be sent again. | ||
55 | */ | ||
50 | 56 | ||
51 | static struct key_entry dell_wmi_keymap[] = { | 57 | static struct key_entry dell_wmi_keymap[] = { |
52 | {KE_KEY, 0xe045, KEY_PROG1}, | 58 | {KE_KEY, 0xe045, KEY_PROG1}, |
59 | {KE_KEY, 0xe009, KEY_EJECTCD}, | ||
60 | |||
61 | /* These also contain the brightness level at offset 6 */ | ||
62 | {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, | ||
63 | {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, | ||
64 | |||
65 | /* Battery health status button */ | ||
66 | {KE_KEY, 0xe007, KEY_BATTERY}, | ||
67 | |||
68 | /* This is actually for all radios. Although physically a | ||
69 | * switch, the notification does not provide an indication of | ||
70 | * state and so it should be reported as a key */ | ||
71 | {KE_KEY, 0xe008, KEY_WLAN}, | ||
72 | |||
73 | /* The next device is at offset 6, the active devices are at | ||
74 | offset 8 and the attached devices at offset 10 */ | ||
75 | {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE}, | ||
76 | |||
77 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, | ||
78 | |||
79 | /* BIOS error detected */ | ||
80 | {KE_IGNORE, 0xe00d, KEY_RESERVED}, | ||
81 | |||
82 | /* Wifi Catcher */ | ||
83 | {KE_KEY, 0xe011, KEY_PROG2}, | ||
84 | |||
85 | /* Ambient light sensor toggle */ | ||
86 | {KE_IGNORE, 0xe013, KEY_RESERVED}, | ||
87 | |||
88 | {KE_IGNORE, 0xe020, KEY_MUTE}, | ||
89 | {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, | ||
90 | {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, | ||
91 | {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, | ||
92 | {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, | ||
93 | {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, | ||
94 | {KE_IGNORE, 0xe045, KEY_NUMLOCK}, | ||
95 | {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, | ||
53 | {KE_END, 0} | 96 | {KE_END, 0} |
54 | }; | 97 | }; |
55 | 98 | ||
@@ -122,15 +165,20 @@ static void dell_wmi_notify(u32 value, void *context) | |||
122 | 165 | ||
123 | if (obj && obj->type == ACPI_TYPE_BUFFER) { | 166 | if (obj && obj->type == ACPI_TYPE_BUFFER) { |
124 | int *buffer = (int *)obj->buffer.pointer; | 167 | int *buffer = (int *)obj->buffer.pointer; |
125 | key = dell_wmi_get_entry_by_scancode(buffer[1]); | 168 | /* |
169 | * The upper bytes of the event may contain | ||
170 | * additional information, so mask them off for the | ||
171 | * scancode lookup | ||
172 | */ | ||
173 | key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF); | ||
126 | if (key) { | 174 | if (key) { |
127 | input_report_key(dell_wmi_input_dev, key->keycode, 1); | 175 | input_report_key(dell_wmi_input_dev, key->keycode, 1); |
128 | input_sync(dell_wmi_input_dev); | 176 | input_sync(dell_wmi_input_dev); |
129 | input_report_key(dell_wmi_input_dev, key->keycode, 0); | 177 | input_report_key(dell_wmi_input_dev, key->keycode, 0); |
130 | input_sync(dell_wmi_input_dev); | 178 | input_sync(dell_wmi_input_dev); |
131 | } else | 179 | } else if (buffer[1] & 0xFFFF) |
132 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | 180 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", |
133 | buffer[1]); | 181 | buffer[1] & 0xFFFF); |
134 | } | 182 | } |
135 | } | 183 | } |
136 | 184 | ||
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 353a898c3693..8153b3e59189 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -180,6 +180,7 @@ static struct key_entry eeepc_keymap[] = { | |||
180 | */ | 180 | */ |
181 | static int eeepc_hotk_add(struct acpi_device *device); | 181 | static int eeepc_hotk_add(struct acpi_device *device); |
182 | static int eeepc_hotk_remove(struct acpi_device *device, int type); | 182 | static int eeepc_hotk_remove(struct acpi_device *device, int type); |
183 | static int eeepc_hotk_resume(struct acpi_device *device); | ||
183 | 184 | ||
184 | static const struct acpi_device_id eeepc_device_ids[] = { | 185 | static const struct acpi_device_id eeepc_device_ids[] = { |
185 | {EEEPC_HOTK_HID, 0}, | 186 | {EEEPC_HOTK_HID, 0}, |
@@ -194,6 +195,7 @@ static struct acpi_driver eeepc_hotk_driver = { | |||
194 | .ops = { | 195 | .ops = { |
195 | .add = eeepc_hotk_add, | 196 | .add = eeepc_hotk_add, |
196 | .remove = eeepc_hotk_remove, | 197 | .remove = eeepc_hotk_remove, |
198 | .resume = eeepc_hotk_resume, | ||
197 | }, | 199 | }, |
198 | }; | 200 | }; |
199 | 201 | ||
@@ -299,39 +301,22 @@ static int update_bl_status(struct backlight_device *bd) | |||
299 | * Rfkill helpers | 301 | * Rfkill helpers |
300 | */ | 302 | */ |
301 | 303 | ||
302 | static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state) | 304 | static bool eeepc_wlan_rfkill_blocked(void) |
303 | { | ||
304 | if (state == RFKILL_STATE_SOFT_BLOCKED) | ||
305 | return set_acpi(CM_ASL_WLAN, 0); | ||
306 | else | ||
307 | return set_acpi(CM_ASL_WLAN, 1); | ||
308 | } | ||
309 | |||
310 | static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state) | ||
311 | { | 305 | { |
312 | if (get_acpi(CM_ASL_WLAN) == 1) | 306 | if (get_acpi(CM_ASL_WLAN) == 1) |
313 | *state = RFKILL_STATE_UNBLOCKED; | 307 | return false; |
314 | else | 308 | return true; |
315 | *state = RFKILL_STATE_SOFT_BLOCKED; | ||
316 | return 0; | ||
317 | } | 309 | } |
318 | 310 | ||
319 | static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state) | 311 | static int eeepc_rfkill_set(void *data, bool blocked) |
320 | { | 312 | { |
321 | if (state == RFKILL_STATE_SOFT_BLOCKED) | 313 | unsigned long asl = (unsigned long)data; |
322 | return set_acpi(CM_ASL_BLUETOOTH, 0); | 314 | return set_acpi(asl, !blocked); |
323 | else | ||
324 | return set_acpi(CM_ASL_BLUETOOTH, 1); | ||
325 | } | 315 | } |
326 | 316 | ||
327 | static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state) | 317 | static const struct rfkill_ops eeepc_rfkill_ops = { |
328 | { | 318 | .set_block = eeepc_rfkill_set, |
329 | if (get_acpi(CM_ASL_BLUETOOTH) == 1) | 319 | }; |
330 | *state = RFKILL_STATE_UNBLOCKED; | ||
331 | else | ||
332 | *state = RFKILL_STATE_SOFT_BLOCKED; | ||
333 | return 0; | ||
334 | } | ||
335 | 320 | ||
336 | /* | 321 | /* |
337 | * Sys helpers | 322 | * Sys helpers |
@@ -529,23 +514,19 @@ static int notify_brn(void) | |||
529 | return -1; | 514 | return -1; |
530 | } | 515 | } |
531 | 516 | ||
532 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | 517 | static void eeepc_rfkill_hotplug(void) |
533 | { | 518 | { |
534 | enum rfkill_state state; | ||
535 | struct pci_dev *dev; | 519 | struct pci_dev *dev; |
536 | struct pci_bus *bus = pci_find_bus(0, 1); | 520 | struct pci_bus *bus = pci_find_bus(0, 1); |
537 | 521 | bool blocked; | |
538 | if (event != ACPI_NOTIFY_BUS_CHECK) | ||
539 | return; | ||
540 | 522 | ||
541 | if (!bus) { | 523 | if (!bus) { |
542 | printk(EEEPC_WARNING "Unable to find PCI bus 1?\n"); | 524 | printk(EEEPC_WARNING "Unable to find PCI bus 1?\n"); |
543 | return; | 525 | return; |
544 | } | 526 | } |
545 | 527 | ||
546 | eeepc_wlan_rfkill_state(ehotk->eeepc_wlan_rfkill, &state); | 528 | blocked = eeepc_wlan_rfkill_blocked(); |
547 | 529 | if (!blocked) { | |
548 | if (state == RFKILL_STATE_UNBLOCKED) { | ||
549 | dev = pci_get_slot(bus, 0); | 530 | dev = pci_get_slot(bus, 0); |
550 | if (dev) { | 531 | if (dev) { |
551 | /* Device already present */ | 532 | /* Device already present */ |
@@ -566,7 +547,15 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |||
566 | } | 547 | } |
567 | } | 548 | } |
568 | 549 | ||
569 | rfkill_force_state(ehotk->eeepc_wlan_rfkill, state); | 550 | rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked); |
551 | } | ||
552 | |||
553 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | ||
554 | { | ||
555 | if (event != ACPI_NOTIFY_BUS_CHECK) | ||
556 | return; | ||
557 | |||
558 | eeepc_rfkill_hotplug(); | ||
570 | } | 559 | } |
571 | 560 | ||
572 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | 561 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) |
@@ -684,26 +673,17 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
684 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | 673 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); |
685 | 674 | ||
686 | if (get_acpi(CM_ASL_WLAN) != -1) { | 675 | if (get_acpi(CM_ASL_WLAN) != -1) { |
687 | ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev, | 676 | ehotk->eeepc_wlan_rfkill = rfkill_alloc("eeepc-wlan", |
688 | RFKILL_TYPE_WLAN); | 677 | &device->dev, |
678 | RFKILL_TYPE_WLAN, | ||
679 | &eeepc_rfkill_ops, | ||
680 | (void *)CM_ASL_WLAN); | ||
689 | 681 | ||
690 | if (!ehotk->eeepc_wlan_rfkill) | 682 | if (!ehotk->eeepc_wlan_rfkill) |
691 | goto wlan_fail; | 683 | goto wlan_fail; |
692 | 684 | ||
693 | ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan"; | 685 | rfkill_init_sw_state(ehotk->eeepc_wlan_rfkill, |
694 | ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set; | 686 | get_acpi(CM_ASL_WLAN) != 1); |
695 | ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state; | ||
696 | if (get_acpi(CM_ASL_WLAN) == 1) { | ||
697 | ehotk->eeepc_wlan_rfkill->state = | ||
698 | RFKILL_STATE_UNBLOCKED; | ||
699 | rfkill_set_default(RFKILL_TYPE_WLAN, | ||
700 | RFKILL_STATE_UNBLOCKED); | ||
701 | } else { | ||
702 | ehotk->eeepc_wlan_rfkill->state = | ||
703 | RFKILL_STATE_SOFT_BLOCKED; | ||
704 | rfkill_set_default(RFKILL_TYPE_WLAN, | ||
705 | RFKILL_STATE_SOFT_BLOCKED); | ||
706 | } | ||
707 | result = rfkill_register(ehotk->eeepc_wlan_rfkill); | 687 | result = rfkill_register(ehotk->eeepc_wlan_rfkill); |
708 | if (result) | 688 | if (result) |
709 | goto wlan_fail; | 689 | goto wlan_fail; |
@@ -711,28 +691,17 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
711 | 691 | ||
712 | if (get_acpi(CM_ASL_BLUETOOTH) != -1) { | 692 | if (get_acpi(CM_ASL_BLUETOOTH) != -1) { |
713 | ehotk->eeepc_bluetooth_rfkill = | 693 | ehotk->eeepc_bluetooth_rfkill = |
714 | rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH); | 694 | rfkill_alloc("eeepc-bluetooth", |
695 | &device->dev, | ||
696 | RFKILL_TYPE_BLUETOOTH, | ||
697 | &eeepc_rfkill_ops, | ||
698 | (void *)CM_ASL_BLUETOOTH); | ||
715 | 699 | ||
716 | if (!ehotk->eeepc_bluetooth_rfkill) | 700 | if (!ehotk->eeepc_bluetooth_rfkill) |
717 | goto bluetooth_fail; | 701 | goto bluetooth_fail; |
718 | 702 | ||
719 | ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth"; | 703 | rfkill_init_sw_state(ehotk->eeepc_bluetooth_rfkill, |
720 | ehotk->eeepc_bluetooth_rfkill->toggle_radio = | 704 | get_acpi(CM_ASL_BLUETOOTH) != 1); |
721 | eeepc_bluetooth_rfkill_set; | ||
722 | ehotk->eeepc_bluetooth_rfkill->get_state = | ||
723 | eeepc_bluetooth_rfkill_state; | ||
724 | if (get_acpi(CM_ASL_BLUETOOTH) == 1) { | ||
725 | ehotk->eeepc_bluetooth_rfkill->state = | ||
726 | RFKILL_STATE_UNBLOCKED; | ||
727 | rfkill_set_default(RFKILL_TYPE_BLUETOOTH, | ||
728 | RFKILL_STATE_UNBLOCKED); | ||
729 | } else { | ||
730 | ehotk->eeepc_bluetooth_rfkill->state = | ||
731 | RFKILL_STATE_SOFT_BLOCKED; | ||
732 | rfkill_set_default(RFKILL_TYPE_BLUETOOTH, | ||
733 | RFKILL_STATE_SOFT_BLOCKED); | ||
734 | } | ||
735 | |||
736 | result = rfkill_register(ehotk->eeepc_bluetooth_rfkill); | 705 | result = rfkill_register(ehotk->eeepc_bluetooth_rfkill); |
737 | if (result) | 706 | if (result) |
738 | goto bluetooth_fail; | 707 | goto bluetooth_fail; |
@@ -741,13 +710,10 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
741 | return 0; | 710 | return 0; |
742 | 711 | ||
743 | bluetooth_fail: | 712 | bluetooth_fail: |
744 | if (ehotk->eeepc_bluetooth_rfkill) | 713 | rfkill_destroy(ehotk->eeepc_bluetooth_rfkill); |
745 | rfkill_free(ehotk->eeepc_bluetooth_rfkill); | ||
746 | rfkill_unregister(ehotk->eeepc_wlan_rfkill); | 714 | rfkill_unregister(ehotk->eeepc_wlan_rfkill); |
747 | ehotk->eeepc_wlan_rfkill = NULL; | ||
748 | wlan_fail: | 715 | wlan_fail: |
749 | if (ehotk->eeepc_wlan_rfkill) | 716 | rfkill_destroy(ehotk->eeepc_wlan_rfkill); |
750 | rfkill_free(ehotk->eeepc_wlan_rfkill); | ||
751 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | 717 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); |
752 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | 718 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); |
753 | ehotk_fail: | 719 | ehotk_fail: |
@@ -775,6 +741,33 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) | |||
775 | return 0; | 741 | return 0; |
776 | } | 742 | } |
777 | 743 | ||
744 | static int eeepc_hotk_resume(struct acpi_device *device) | ||
745 | { | ||
746 | if (ehotk->eeepc_wlan_rfkill) { | ||
747 | bool wlan; | ||
748 | |||
749 | /* Workaround - it seems that _PTS disables the wireless | ||
750 | without notification or changing the value read by WLAN. | ||
751 | Normally this is fine because the correct value is restored | ||
752 | from the non-volatile storage on resume, but we need to do | ||
753 | it ourself if case suspend is aborted, or we lose wireless. | ||
754 | */ | ||
755 | wlan = get_acpi(CM_ASL_WLAN); | ||
756 | set_acpi(CM_ASL_WLAN, wlan); | ||
757 | |||
758 | rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, | ||
759 | wlan != 1); | ||
760 | |||
761 | eeepc_rfkill_hotplug(); | ||
762 | } | ||
763 | |||
764 | if (ehotk->eeepc_bluetooth_rfkill) | ||
765 | rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill, | ||
766 | get_acpi(CM_ASL_BLUETOOTH) != 1); | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
778 | /* | 771 | /* |
779 | * Hwmon | 772 | * Hwmon |
780 | */ | 773 | */ |
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 50d9019de2be..4ac2311c00af 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -47,7 +47,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | |||
47 | #define HPWMI_DISPLAY_QUERY 0x1 | 47 | #define HPWMI_DISPLAY_QUERY 0x1 |
48 | #define HPWMI_HDDTEMP_QUERY 0x2 | 48 | #define HPWMI_HDDTEMP_QUERY 0x2 |
49 | #define HPWMI_ALS_QUERY 0x3 | 49 | #define HPWMI_ALS_QUERY 0x3 |
50 | #define HPWMI_DOCK_QUERY 0x4 | 50 | #define HPWMI_HARDWARE_QUERY 0x4 |
51 | #define HPWMI_WIRELESS_QUERY 0x5 | 51 | #define HPWMI_WIRELESS_QUERY 0x5 |
52 | #define HPWMI_HOTKEY_QUERY 0xc | 52 | #define HPWMI_HOTKEY_QUERY 0xc |
53 | 53 | ||
@@ -75,10 +75,9 @@ struct key_entry { | |||
75 | u16 keycode; | 75 | u16 keycode; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | enum { KE_KEY, KE_SW, KE_END }; | 78 | enum { KE_KEY, KE_END }; |
79 | 79 | ||
80 | static struct key_entry hp_wmi_keymap[] = { | 80 | static struct key_entry hp_wmi_keymap[] = { |
81 | {KE_SW, 0x01, SW_DOCK}, | ||
82 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | 81 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, |
83 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | 82 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, |
84 | {KE_KEY, 0x20e6, KEY_PROG1}, | 83 | {KE_KEY, 0x20e6, KEY_PROG1}, |
@@ -151,61 +150,64 @@ static int hp_wmi_als_state(void) | |||
151 | 150 | ||
152 | static int hp_wmi_dock_state(void) | 151 | static int hp_wmi_dock_state(void) |
153 | { | 152 | { |
154 | return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); | 153 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); |
155 | } | ||
156 | 154 | ||
157 | static int hp_wmi_wifi_set(void *data, enum rfkill_state state) | 155 | if (ret < 0) |
158 | { | 156 | return ret; |
159 | if (state) | 157 | |
160 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101); | 158 | return ret & 0x1; |
161 | else | ||
162 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100); | ||
163 | } | 159 | } |
164 | 160 | ||
165 | static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state) | 161 | static int hp_wmi_tablet_state(void) |
166 | { | 162 | { |
167 | if (state) | 163 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); |
168 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202); | 164 | |
169 | else | 165 | if (ret < 0) |
170 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200); | 166 | return ret; |
167 | |||
168 | return (ret & 0x4) ? 1 : 0; | ||
171 | } | 169 | } |
172 | 170 | ||
173 | static int hp_wmi_wwan_set(void *data, enum rfkill_state state) | 171 | static int hp_wmi_set_block(void *data, bool blocked) |
174 | { | 172 | { |
175 | if (state) | 173 | unsigned long b = (unsigned long) data; |
176 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404); | 174 | int query = BIT(b + 8) | ((!!blocked) << b); |
177 | else | 175 | |
178 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400); | 176 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); |
179 | } | 177 | } |
180 | 178 | ||
181 | static int hp_wmi_wifi_state(void) | 179 | static const struct rfkill_ops hp_wmi_rfkill_ops = { |
180 | .set_block = hp_wmi_set_block, | ||
181 | }; | ||
182 | |||
183 | static bool hp_wmi_wifi_state(void) | ||
182 | { | 184 | { |
183 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 185 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); |
184 | 186 | ||
185 | if (wireless & 0x100) | 187 | if (wireless & 0x100) |
186 | return RFKILL_STATE_UNBLOCKED; | 188 | return false; |
187 | else | 189 | else |
188 | return RFKILL_STATE_SOFT_BLOCKED; | 190 | return true; |
189 | } | 191 | } |
190 | 192 | ||
191 | static int hp_wmi_bluetooth_state(void) | 193 | static bool hp_wmi_bluetooth_state(void) |
192 | { | 194 | { |
193 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 195 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); |
194 | 196 | ||
195 | if (wireless & 0x10000) | 197 | if (wireless & 0x10000) |
196 | return RFKILL_STATE_UNBLOCKED; | 198 | return false; |
197 | else | 199 | else |
198 | return RFKILL_STATE_SOFT_BLOCKED; | 200 | return true; |
199 | } | 201 | } |
200 | 202 | ||
201 | static int hp_wmi_wwan_state(void) | 203 | static bool hp_wmi_wwan_state(void) |
202 | { | 204 | { |
203 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 205 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); |
204 | 206 | ||
205 | if (wireless & 0x1000000) | 207 | if (wireless & 0x1000000) |
206 | return RFKILL_STATE_UNBLOCKED; | 208 | return false; |
207 | else | 209 | else |
208 | return RFKILL_STATE_SOFT_BLOCKED; | 210 | return true; |
209 | } | 211 | } |
210 | 212 | ||
211 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, | 213 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, |
@@ -244,6 +246,15 @@ static ssize_t show_dock(struct device *dev, struct device_attribute *attr, | |||
244 | return sprintf(buf, "%d\n", value); | 246 | return sprintf(buf, "%d\n", value); |
245 | } | 247 | } |
246 | 248 | ||
249 | static ssize_t show_tablet(struct device *dev, struct device_attribute *attr, | ||
250 | char *buf) | ||
251 | { | ||
252 | int value = hp_wmi_tablet_state(); | ||
253 | if (value < 0) | ||
254 | return -EINVAL; | ||
255 | return sprintf(buf, "%d\n", value); | ||
256 | } | ||
257 | |||
247 | static ssize_t set_als(struct device *dev, struct device_attribute *attr, | 258 | static ssize_t set_als(struct device *dev, struct device_attribute *attr, |
248 | const char *buf, size_t count) | 259 | const char *buf, size_t count) |
249 | { | 260 | { |
@@ -256,6 +267,7 @@ static DEVICE_ATTR(display, S_IRUGO, show_display, NULL); | |||
256 | static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); | 267 | static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); |
257 | static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | 268 | static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); |
258 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | 269 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); |
270 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); | ||
259 | 271 | ||
260 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) | 272 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) |
261 | { | 273 | { |
@@ -338,23 +350,23 @@ static void hp_wmi_notify(u32 value, void *context) | |||
338 | key->keycode, 0); | 350 | key->keycode, 0); |
339 | input_sync(hp_wmi_input_dev); | 351 | input_sync(hp_wmi_input_dev); |
340 | break; | 352 | break; |
341 | case KE_SW: | ||
342 | input_report_switch(hp_wmi_input_dev, | ||
343 | key->keycode, | ||
344 | hp_wmi_dock_state()); | ||
345 | input_sync(hp_wmi_input_dev); | ||
346 | break; | ||
347 | } | 353 | } |
354 | } else if (eventcode == 0x1) { | ||
355 | input_report_switch(hp_wmi_input_dev, SW_DOCK, | ||
356 | hp_wmi_dock_state()); | ||
357 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, | ||
358 | hp_wmi_tablet_state()); | ||
359 | input_sync(hp_wmi_input_dev); | ||
348 | } else if (eventcode == 0x5) { | 360 | } else if (eventcode == 0x5) { |
349 | if (wifi_rfkill) | 361 | if (wifi_rfkill) |
350 | rfkill_force_state(wifi_rfkill, | 362 | rfkill_set_sw_state(wifi_rfkill, |
351 | hp_wmi_wifi_state()); | 363 | hp_wmi_wifi_state()); |
352 | if (bluetooth_rfkill) | 364 | if (bluetooth_rfkill) |
353 | rfkill_force_state(bluetooth_rfkill, | 365 | rfkill_set_sw_state(bluetooth_rfkill, |
354 | hp_wmi_bluetooth_state()); | 366 | hp_wmi_bluetooth_state()); |
355 | if (wwan_rfkill) | 367 | if (wwan_rfkill) |
356 | rfkill_force_state(wwan_rfkill, | 368 | rfkill_set_sw_state(wwan_rfkill, |
357 | hp_wmi_wwan_state()); | 369 | hp_wmi_wwan_state()); |
358 | } else | 370 | } else |
359 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", | 371 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", |
360 | eventcode); | 372 | eventcode); |
@@ -381,18 +393,19 @@ static int __init hp_wmi_input_setup(void) | |||
381 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | 393 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); |
382 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | 394 | set_bit(key->keycode, hp_wmi_input_dev->keybit); |
383 | break; | 395 | break; |
384 | case KE_SW: | ||
385 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | ||
386 | set_bit(key->keycode, hp_wmi_input_dev->swbit); | ||
387 | |||
388 | /* Set initial dock state */ | ||
389 | input_report_switch(hp_wmi_input_dev, key->keycode, | ||
390 | hp_wmi_dock_state()); | ||
391 | input_sync(hp_wmi_input_dev); | ||
392 | break; | ||
393 | } | 396 | } |
394 | } | 397 | } |
395 | 398 | ||
399 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | ||
400 | set_bit(SW_DOCK, hp_wmi_input_dev->swbit); | ||
401 | set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); | ||
402 | |||
403 | /* Set initial hardware state */ | ||
404 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); | ||
405 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, | ||
406 | hp_wmi_tablet_state()); | ||
407 | input_sync(hp_wmi_input_dev); | ||
408 | |||
396 | err = input_register_device(hp_wmi_input_dev); | 409 | err = input_register_device(hp_wmi_input_dev); |
397 | 410 | ||
398 | if (err) { | 411 | if (err) { |
@@ -409,6 +422,7 @@ static void cleanup_sysfs(struct platform_device *device) | |||
409 | device_remove_file(&device->dev, &dev_attr_hddtemp); | 422 | device_remove_file(&device->dev, &dev_attr_hddtemp); |
410 | device_remove_file(&device->dev, &dev_attr_als); | 423 | device_remove_file(&device->dev, &dev_attr_als); |
411 | device_remove_file(&device->dev, &dev_attr_dock); | 424 | device_remove_file(&device->dev, &dev_attr_dock); |
425 | device_remove_file(&device->dev, &dev_attr_tablet); | ||
412 | } | 426 | } |
413 | 427 | ||
414 | static int __init hp_wmi_bios_setup(struct platform_device *device) | 428 | static int __init hp_wmi_bios_setup(struct platform_device *device) |
@@ -428,36 +442,35 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) | |||
428 | err = device_create_file(&device->dev, &dev_attr_dock); | 442 | err = device_create_file(&device->dev, &dev_attr_dock); |
429 | if (err) | 443 | if (err) |
430 | goto add_sysfs_error; | 444 | goto add_sysfs_error; |
445 | err = device_create_file(&device->dev, &dev_attr_tablet); | ||
446 | if (err) | ||
447 | goto add_sysfs_error; | ||
431 | 448 | ||
432 | if (wireless & 0x1) { | 449 | if (wireless & 0x1) { |
433 | wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); | 450 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, |
434 | wifi_rfkill->name = "hp-wifi"; | 451 | RFKILL_TYPE_WLAN, |
435 | wifi_rfkill->state = hp_wmi_wifi_state(); | 452 | &hp_wmi_rfkill_ops, |
436 | wifi_rfkill->toggle_radio = hp_wmi_wifi_set; | 453 | (void *) 0); |
437 | wifi_rfkill->user_claim_unsupported = 1; | ||
438 | err = rfkill_register(wifi_rfkill); | 454 | err = rfkill_register(wifi_rfkill); |
439 | if (err) | 455 | if (err) |
440 | goto add_sysfs_error; | 456 | goto register_wifi_error; |
441 | } | 457 | } |
442 | 458 | ||
443 | if (wireless & 0x2) { | 459 | if (wireless & 0x2) { |
444 | bluetooth_rfkill = rfkill_allocate(&device->dev, | 460 | bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, |
445 | RFKILL_TYPE_BLUETOOTH); | 461 | RFKILL_TYPE_BLUETOOTH, |
446 | bluetooth_rfkill->name = "hp-bluetooth"; | 462 | &hp_wmi_rfkill_ops, |
447 | bluetooth_rfkill->state = hp_wmi_bluetooth_state(); | 463 | (void *) 1); |
448 | bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; | ||
449 | bluetooth_rfkill->user_claim_unsupported = 1; | ||
450 | err = rfkill_register(bluetooth_rfkill); | 464 | err = rfkill_register(bluetooth_rfkill); |
451 | if (err) | 465 | if (err) |
452 | goto register_bluetooth_error; | 466 | goto register_bluetooth_error; |
453 | } | 467 | } |
454 | 468 | ||
455 | if (wireless & 0x4) { | 469 | if (wireless & 0x4) { |
456 | wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); | 470 | wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, |
457 | wwan_rfkill->name = "hp-wwan"; | 471 | RFKILL_TYPE_WWAN, |
458 | wwan_rfkill->state = hp_wmi_wwan_state(); | 472 | &hp_wmi_rfkill_ops, |
459 | wwan_rfkill->toggle_radio = hp_wmi_wwan_set; | 473 | (void *) 2); |
460 | wwan_rfkill->user_claim_unsupported = 1; | ||
461 | err = rfkill_register(wwan_rfkill); | 474 | err = rfkill_register(wwan_rfkill); |
462 | if (err) | 475 | if (err) |
463 | goto register_wwan_err; | 476 | goto register_wwan_err; |
@@ -465,11 +478,15 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) | |||
465 | 478 | ||
466 | return 0; | 479 | return 0; |
467 | register_wwan_err: | 480 | register_wwan_err: |
481 | rfkill_destroy(wwan_rfkill); | ||
468 | if (bluetooth_rfkill) | 482 | if (bluetooth_rfkill) |
469 | rfkill_unregister(bluetooth_rfkill); | 483 | rfkill_unregister(bluetooth_rfkill); |
470 | register_bluetooth_error: | 484 | register_bluetooth_error: |
485 | rfkill_destroy(bluetooth_rfkill); | ||
471 | if (wifi_rfkill) | 486 | if (wifi_rfkill) |
472 | rfkill_unregister(wifi_rfkill); | 487 | rfkill_unregister(wifi_rfkill); |
488 | register_wifi_error: | ||
489 | rfkill_destroy(wifi_rfkill); | ||
473 | add_sysfs_error: | 490 | add_sysfs_error: |
474 | cleanup_sysfs(device); | 491 | cleanup_sysfs(device); |
475 | return err; | 492 | return err; |
@@ -479,35 +496,35 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) | |||
479 | { | 496 | { |
480 | cleanup_sysfs(device); | 497 | cleanup_sysfs(device); |
481 | 498 | ||
482 | if (wifi_rfkill) | 499 | if (wifi_rfkill) { |
483 | rfkill_unregister(wifi_rfkill); | 500 | rfkill_unregister(wifi_rfkill); |
484 | if (bluetooth_rfkill) | 501 | rfkill_destroy(wifi_rfkill); |
502 | } | ||
503 | if (bluetooth_rfkill) { | ||
485 | rfkill_unregister(bluetooth_rfkill); | 504 | rfkill_unregister(bluetooth_rfkill); |
486 | if (wwan_rfkill) | 505 | rfkill_destroy(wifi_rfkill); |
506 | } | ||
507 | if (wwan_rfkill) { | ||
487 | rfkill_unregister(wwan_rfkill); | 508 | rfkill_unregister(wwan_rfkill); |
509 | rfkill_destroy(wwan_rfkill); | ||
510 | } | ||
488 | 511 | ||
489 | return 0; | 512 | return 0; |
490 | } | 513 | } |
491 | 514 | ||
492 | static int hp_wmi_resume_handler(struct platform_device *device) | 515 | static int hp_wmi_resume_handler(struct platform_device *device) |
493 | { | 516 | { |
494 | struct key_entry *key; | ||
495 | |||
496 | /* | 517 | /* |
497 | * Docking state may have changed while suspended, so trigger | 518 | * Hardware state may have changed while suspended, so trigger |
498 | * an input event for the current state. As this is a switch, | 519 | * input events for the current state. As this is a switch, |
499 | * the input layer will only actually pass it on if the state | 520 | * the input layer will only actually pass it on if the state |
500 | * changed. | 521 | * changed. |
501 | */ | 522 | */ |
502 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | 523 | |
503 | switch (key->type) { | 524 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); |
504 | case KE_SW: | 525 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, |
505 | input_report_switch(hp_wmi_input_dev, key->keycode, | 526 | hp_wmi_tablet_state()); |
506 | hp_wmi_dock_state()); | 527 | input_sync(hp_wmi_input_dev); |
507 | input_sync(hp_wmi_input_dev); | ||
508 | break; | ||
509 | } | ||
510 | } | ||
511 | 528 | ||
512 | return 0; | 529 | return 0; |
513 | } | 530 | } |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 552958545f94..dafaa4a92df5 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -128,11 +128,11 @@ enum sony_nc_rfkill { | |||
128 | SONY_BLUETOOTH, | 128 | SONY_BLUETOOTH, |
129 | SONY_WWAN, | 129 | SONY_WWAN, |
130 | SONY_WIMAX, | 130 | SONY_WIMAX, |
131 | SONY_RFKILL_MAX, | 131 | N_SONY_RFKILL, |
132 | }; | 132 | }; |
133 | 133 | ||
134 | static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX]; | 134 | static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; |
135 | static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900}; | 135 | static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; |
136 | static void sony_nc_rfkill_update(void); | 136 | static void sony_nc_rfkill_update(void); |
137 | 137 | ||
138 | /*********** Input Devices ***********/ | 138 | /*********** Input Devices ***********/ |
@@ -1051,151 +1051,97 @@ static void sony_nc_rfkill_cleanup(void) | |||
1051 | { | 1051 | { |
1052 | int i; | 1052 | int i; |
1053 | 1053 | ||
1054 | for (i = 0; i < SONY_RFKILL_MAX; i++) { | 1054 | for (i = 0; i < N_SONY_RFKILL; i++) { |
1055 | if (sony_rfkill_devices[i]) | 1055 | if (sony_rfkill_devices[i]) { |
1056 | rfkill_unregister(sony_rfkill_devices[i]); | 1056 | rfkill_unregister(sony_rfkill_devices[i]); |
1057 | rfkill_destroy(sony_rfkill_devices[i]); | ||
1058 | } | ||
1057 | } | 1059 | } |
1058 | } | 1060 | } |
1059 | 1061 | ||
1060 | static int sony_nc_rfkill_get(void *data, enum rfkill_state *state) | 1062 | static int sony_nc_rfkill_set(void *data, bool blocked) |
1061 | { | ||
1062 | int result; | ||
1063 | int argument = sony_rfkill_address[(long) data]; | ||
1064 | |||
1065 | sony_call_snc_handle(0x124, 0x200, &result); | ||
1066 | if (result & 0x1) { | ||
1067 | sony_call_snc_handle(0x124, argument, &result); | ||
1068 | if (result & 0xf) | ||
1069 | *state = RFKILL_STATE_UNBLOCKED; | ||
1070 | else | ||
1071 | *state = RFKILL_STATE_SOFT_BLOCKED; | ||
1072 | } else { | ||
1073 | *state = RFKILL_STATE_HARD_BLOCKED; | ||
1074 | } | ||
1075 | |||
1076 | return 0; | ||
1077 | } | ||
1078 | |||
1079 | static int sony_nc_rfkill_set(void *data, enum rfkill_state state) | ||
1080 | { | 1063 | { |
1081 | int result; | 1064 | int result; |
1082 | int argument = sony_rfkill_address[(long) data] + 0x100; | 1065 | int argument = sony_rfkill_address[(long) data] + 0x100; |
1083 | 1066 | ||
1084 | if (state == RFKILL_STATE_UNBLOCKED) | 1067 | if (!blocked) |
1085 | argument |= 0xff0000; | 1068 | argument |= 0xff0000; |
1086 | 1069 | ||
1087 | return sony_call_snc_handle(0x124, argument, &result); | 1070 | return sony_call_snc_handle(0x124, argument, &result); |
1088 | } | 1071 | } |
1089 | 1072 | ||
1090 | static int sony_nc_setup_wifi_rfkill(struct acpi_device *device) | 1073 | static const struct rfkill_ops sony_rfkill_ops = { |
1091 | { | 1074 | .set_block = sony_nc_rfkill_set, |
1092 | int err = 0; | 1075 | }; |
1093 | struct rfkill *sony_wifi_rfkill; | ||
1094 | |||
1095 | sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); | ||
1096 | if (!sony_wifi_rfkill) | ||
1097 | return -1; | ||
1098 | sony_wifi_rfkill->name = "sony-wifi"; | ||
1099 | sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set; | ||
1100 | sony_wifi_rfkill->get_state = sony_nc_rfkill_get; | ||
1101 | sony_wifi_rfkill->user_claim_unsupported = 1; | ||
1102 | sony_wifi_rfkill->data = (void *)SONY_WIFI; | ||
1103 | err = rfkill_register(sony_wifi_rfkill); | ||
1104 | if (err) | ||
1105 | rfkill_free(sony_wifi_rfkill); | ||
1106 | else { | ||
1107 | sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill; | ||
1108 | sony_nc_rfkill_set(sony_wifi_rfkill->data, | ||
1109 | RFKILL_STATE_UNBLOCKED); | ||
1110 | } | ||
1111 | return err; | ||
1112 | } | ||
1113 | 1076 | ||
1114 | static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device) | 1077 | static int sony_nc_setup_rfkill(struct acpi_device *device, |
1078 | enum sony_nc_rfkill nc_type) | ||
1115 | { | 1079 | { |
1116 | int err = 0; | 1080 | int err = 0; |
1117 | struct rfkill *sony_bluetooth_rfkill; | 1081 | struct rfkill *rfk; |
1118 | 1082 | enum rfkill_type type; | |
1119 | sony_bluetooth_rfkill = rfkill_allocate(&device->dev, | 1083 | const char *name; |
1120 | RFKILL_TYPE_BLUETOOTH); | 1084 | |
1121 | if (!sony_bluetooth_rfkill) | 1085 | switch (nc_type) { |
1122 | return -1; | 1086 | case SONY_WIFI: |
1123 | sony_bluetooth_rfkill->name = "sony-bluetooth"; | 1087 | type = RFKILL_TYPE_WLAN; |
1124 | sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set; | 1088 | name = "sony-wifi"; |
1125 | sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get; | 1089 | break; |
1126 | sony_bluetooth_rfkill->user_claim_unsupported = 1; | 1090 | case SONY_BLUETOOTH: |
1127 | sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH; | 1091 | type = RFKILL_TYPE_BLUETOOTH; |
1128 | err = rfkill_register(sony_bluetooth_rfkill); | 1092 | name = "sony-bluetooth"; |
1129 | if (err) | 1093 | break; |
1130 | rfkill_free(sony_bluetooth_rfkill); | 1094 | case SONY_WWAN: |
1131 | else { | 1095 | type = RFKILL_TYPE_WWAN; |
1132 | sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill; | 1096 | name = "sony-wwan"; |
1133 | sony_nc_rfkill_set(sony_bluetooth_rfkill->data, | 1097 | break; |
1134 | RFKILL_STATE_UNBLOCKED); | 1098 | case SONY_WIMAX: |
1099 | type = RFKILL_TYPE_WIMAX; | ||
1100 | name = "sony-wimax"; | ||
1101 | break; | ||
1102 | default: | ||
1103 | return -EINVAL; | ||
1135 | } | 1104 | } |
1136 | return err; | ||
1137 | } | ||
1138 | 1105 | ||
1139 | static int sony_nc_setup_wwan_rfkill(struct acpi_device *device) | 1106 | rfk = rfkill_alloc(name, &device->dev, type, |
1140 | { | 1107 | &sony_rfkill_ops, (void *)nc_type); |
1141 | int err = 0; | 1108 | if (!rfk) |
1142 | struct rfkill *sony_wwan_rfkill; | 1109 | return -ENOMEM; |
1143 | 1110 | ||
1144 | sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); | 1111 | err = rfkill_register(rfk); |
1145 | if (!sony_wwan_rfkill) | 1112 | if (err) { |
1146 | return -1; | 1113 | rfkill_destroy(rfk); |
1147 | sony_wwan_rfkill->name = "sony-wwan"; | 1114 | return err; |
1148 | sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set; | ||
1149 | sony_wwan_rfkill->get_state = sony_nc_rfkill_get; | ||
1150 | sony_wwan_rfkill->user_claim_unsupported = 1; | ||
1151 | sony_wwan_rfkill->data = (void *)SONY_WWAN; | ||
1152 | err = rfkill_register(sony_wwan_rfkill); | ||
1153 | if (err) | ||
1154 | rfkill_free(sony_wwan_rfkill); | ||
1155 | else { | ||
1156 | sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill; | ||
1157 | sony_nc_rfkill_set(sony_wwan_rfkill->data, | ||
1158 | RFKILL_STATE_UNBLOCKED); | ||
1159 | } | 1115 | } |
1116 | sony_rfkill_devices[nc_type] = rfk; | ||
1160 | return err; | 1117 | return err; |
1161 | } | 1118 | } |
1162 | 1119 | ||
1163 | static int sony_nc_setup_wimax_rfkill(struct acpi_device *device) | 1120 | static void sony_nc_rfkill_update() |
1164 | { | 1121 | { |
1165 | int err = 0; | 1122 | enum sony_nc_rfkill i; |
1166 | struct rfkill *sony_wimax_rfkill; | 1123 | int result; |
1124 | bool hwblock; | ||
1167 | 1125 | ||
1168 | sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); | 1126 | sony_call_snc_handle(0x124, 0x200, &result); |
1169 | if (!sony_wimax_rfkill) | 1127 | hwblock = !(result & 0x1); |
1170 | return -1; | ||
1171 | sony_wimax_rfkill->name = "sony-wimax"; | ||
1172 | sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set; | ||
1173 | sony_wimax_rfkill->get_state = sony_nc_rfkill_get; | ||
1174 | sony_wimax_rfkill->user_claim_unsupported = 1; | ||
1175 | sony_wimax_rfkill->data = (void *)SONY_WIMAX; | ||
1176 | err = rfkill_register(sony_wimax_rfkill); | ||
1177 | if (err) | ||
1178 | rfkill_free(sony_wimax_rfkill); | ||
1179 | else { | ||
1180 | sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill; | ||
1181 | sony_nc_rfkill_set(sony_wimax_rfkill->data, | ||
1182 | RFKILL_STATE_UNBLOCKED); | ||
1183 | } | ||
1184 | return err; | ||
1185 | } | ||
1186 | 1128 | ||
1187 | static void sony_nc_rfkill_update() | 1129 | for (i = 0; i < N_SONY_RFKILL; i++) { |
1188 | { | 1130 | int argument = sony_rfkill_address[i]; |
1189 | int i; | ||
1190 | enum rfkill_state state; | ||
1191 | 1131 | ||
1192 | for (i = 0; i < SONY_RFKILL_MAX; i++) { | 1132 | if (!sony_rfkill_devices[i]) |
1193 | if (sony_rfkill_devices[i]) { | 1133 | continue; |
1194 | sony_rfkill_devices[i]-> | 1134 | |
1195 | get_state(sony_rfkill_devices[i]->data, | 1135 | if (hwblock) { |
1196 | &state); | 1136 | if (rfkill_set_hw_state(sony_rfkill_devices[i], true)) { |
1197 | rfkill_force_state(sony_rfkill_devices[i], state); | 1137 | /* we already know we're blocked */ |
1138 | } | ||
1139 | continue; | ||
1198 | } | 1140 | } |
1141 | |||
1142 | sony_call_snc_handle(0x124, argument, &result); | ||
1143 | rfkill_set_states(sony_rfkill_devices[i], | ||
1144 | !(result & 0xf), false); | ||
1199 | } | 1145 | } |
1200 | } | 1146 | } |
1201 | 1147 | ||
@@ -1214,13 +1160,13 @@ static int sony_nc_rfkill_setup(struct acpi_device *device) | |||
1214 | } | 1160 | } |
1215 | 1161 | ||
1216 | if (result & 0x1) | 1162 | if (result & 0x1) |
1217 | sony_nc_setup_wifi_rfkill(device); | 1163 | sony_nc_setup_rfkill(device, SONY_WIFI); |
1218 | if (result & 0x2) | 1164 | if (result & 0x2) |
1219 | sony_nc_setup_bluetooth_rfkill(device); | 1165 | sony_nc_setup_rfkill(device, SONY_BLUETOOTH); |
1220 | if (result & 0x1c) | 1166 | if (result & 0x1c) |
1221 | sony_nc_setup_wwan_rfkill(device); | 1167 | sony_nc_setup_rfkill(device, SONY_WWAN); |
1222 | if (result & 0x20) | 1168 | if (result & 0x20) |
1223 | sony_nc_setup_wimax_rfkill(device); | 1169 | sony_nc_setup_rfkill(device, SONY_WIMAX); |
1224 | 1170 | ||
1225 | return 0; | 1171 | return 0; |
1226 | } | 1172 | } |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 27ca676a7092..a463fd72c495 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -166,13 +166,6 @@ enum { | |||
166 | 166 | ||
167 | #define TPACPI_MAX_ACPI_ARGS 3 | 167 | #define TPACPI_MAX_ACPI_ARGS 3 |
168 | 168 | ||
169 | /* rfkill switches */ | ||
170 | enum { | ||
171 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
172 | TPACPI_RFK_WWAN_SW_ID, | ||
173 | TPACPI_RFK_UWB_SW_ID, | ||
174 | }; | ||
175 | |||
176 | /* printk headers */ | 169 | /* printk headers */ |
177 | #define TPACPI_LOG TPACPI_FILE ": " | 170 | #define TPACPI_LOG TPACPI_FILE ": " |
178 | #define TPACPI_EMERG KERN_EMERG TPACPI_LOG | 171 | #define TPACPI_EMERG KERN_EMERG TPACPI_LOG |
@@ -1076,67 +1069,234 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
1076 | return 0; | 1069 | return 0; |
1077 | } | 1070 | } |
1078 | 1071 | ||
1079 | static int __init tpacpi_new_rfkill(const unsigned int id, | 1072 | static void printk_deprecated_attribute(const char * const what, |
1080 | struct rfkill **rfk, | 1073 | const char * const details) |
1074 | { | ||
1075 | tpacpi_log_usertask("deprecated sysfs attribute"); | ||
1076 | printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " | ||
1077 | "will be removed. %s\n", | ||
1078 | what, details); | ||
1079 | } | ||
1080 | |||
1081 | /************************************************************************* | ||
1082 | * rfkill and radio control support helpers | ||
1083 | */ | ||
1084 | |||
1085 | /* | ||
1086 | * ThinkPad-ACPI firmware handling model: | ||
1087 | * | ||
1088 | * WLSW (master wireless switch) is event-driven, and is common to all | ||
1089 | * firmware-controlled radios. It cannot be controlled, just monitored, | ||
1090 | * as expected. It overrides all radio state in firmware | ||
1091 | * | ||
1092 | * The kernel, a masked-off hotkey, and WLSW can change the radio state | ||
1093 | * (TODO: verify how WLSW interacts with the returned radio state). | ||
1094 | * | ||
1095 | * The only time there are shadow radio state changes, is when | ||
1096 | * masked-off hotkeys are used. | ||
1097 | */ | ||
1098 | |||
1099 | /* | ||
1100 | * Internal driver API for radio state: | ||
1101 | * | ||
1102 | * int: < 0 = error, otherwise enum tpacpi_rfkill_state | ||
1103 | * bool: true means radio blocked (off) | ||
1104 | */ | ||
1105 | enum tpacpi_rfkill_state { | ||
1106 | TPACPI_RFK_RADIO_OFF = 0, | ||
1107 | TPACPI_RFK_RADIO_ON | ||
1108 | }; | ||
1109 | |||
1110 | /* rfkill switches */ | ||
1111 | enum tpacpi_rfk_id { | ||
1112 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
1113 | TPACPI_RFK_WWAN_SW_ID, | ||
1114 | TPACPI_RFK_UWB_SW_ID, | ||
1115 | TPACPI_RFK_SW_MAX | ||
1116 | }; | ||
1117 | |||
1118 | static const char *tpacpi_rfkill_names[] = { | ||
1119 | [TPACPI_RFK_BLUETOOTH_SW_ID] = "bluetooth", | ||
1120 | [TPACPI_RFK_WWAN_SW_ID] = "wwan", | ||
1121 | [TPACPI_RFK_UWB_SW_ID] = "uwb", | ||
1122 | [TPACPI_RFK_SW_MAX] = NULL | ||
1123 | }; | ||
1124 | |||
1125 | /* ThinkPad-ACPI rfkill subdriver */ | ||
1126 | struct tpacpi_rfk { | ||
1127 | struct rfkill *rfkill; | ||
1128 | enum tpacpi_rfk_id id; | ||
1129 | const struct tpacpi_rfk_ops *ops; | ||
1130 | }; | ||
1131 | |||
1132 | struct tpacpi_rfk_ops { | ||
1133 | /* firmware interface */ | ||
1134 | int (*get_status)(void); | ||
1135 | int (*set_status)(const enum tpacpi_rfkill_state); | ||
1136 | }; | ||
1137 | |||
1138 | static struct tpacpi_rfk *tpacpi_rfkill_switches[TPACPI_RFK_SW_MAX]; | ||
1139 | |||
1140 | /* Query FW and update rfkill sw state for a given rfkill switch */ | ||
1141 | static int tpacpi_rfk_update_swstate(const struct tpacpi_rfk *tp_rfk) | ||
1142 | { | ||
1143 | int status; | ||
1144 | |||
1145 | if (!tp_rfk) | ||
1146 | return -ENODEV; | ||
1147 | |||
1148 | status = (tp_rfk->ops->get_status)(); | ||
1149 | if (status < 0) | ||
1150 | return status; | ||
1151 | |||
1152 | rfkill_set_sw_state(tp_rfk->rfkill, | ||
1153 | (status == TPACPI_RFK_RADIO_OFF)); | ||
1154 | |||
1155 | return status; | ||
1156 | } | ||
1157 | |||
1158 | /* Query FW and update rfkill sw state for all rfkill switches */ | ||
1159 | static void tpacpi_rfk_update_swstate_all(void) | ||
1160 | { | ||
1161 | unsigned int i; | ||
1162 | |||
1163 | for (i = 0; i < TPACPI_RFK_SW_MAX; i++) | ||
1164 | tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[i]); | ||
1165 | } | ||
1166 | |||
1167 | /* | ||
1168 | * Sync the HW-blocking state of all rfkill switches, | ||
1169 | * do notice it causes the rfkill core to schedule uevents | ||
1170 | */ | ||
1171 | static void tpacpi_rfk_update_hwblock_state(bool blocked) | ||
1172 | { | ||
1173 | unsigned int i; | ||
1174 | struct tpacpi_rfk *tp_rfk; | ||
1175 | |||
1176 | for (i = 0; i < TPACPI_RFK_SW_MAX; i++) { | ||
1177 | tp_rfk = tpacpi_rfkill_switches[i]; | ||
1178 | if (tp_rfk) { | ||
1179 | if (rfkill_set_hw_state(tp_rfk->rfkill, | ||
1180 | blocked)) { | ||
1181 | /* ignore -- we track sw block */ | ||
1182 | } | ||
1183 | } | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | /* Call to get the WLSW state from the firmware */ | ||
1188 | static int hotkey_get_wlsw(void); | ||
1189 | |||
1190 | /* Call to query WLSW state and update all rfkill switches */ | ||
1191 | static bool tpacpi_rfk_check_hwblock_state(void) | ||
1192 | { | ||
1193 | int res = hotkey_get_wlsw(); | ||
1194 | int hw_blocked; | ||
1195 | |||
1196 | /* When unknown or unsupported, we have to assume it is unblocked */ | ||
1197 | if (res < 0) | ||
1198 | return false; | ||
1199 | |||
1200 | hw_blocked = (res == TPACPI_RFK_RADIO_OFF); | ||
1201 | tpacpi_rfk_update_hwblock_state(hw_blocked); | ||
1202 | |||
1203 | return hw_blocked; | ||
1204 | } | ||
1205 | |||
1206 | static int tpacpi_rfk_hook_set_block(void *data, bool blocked) | ||
1207 | { | ||
1208 | struct tpacpi_rfk *tp_rfk = data; | ||
1209 | int res; | ||
1210 | |||
1211 | dbg_printk(TPACPI_DBG_RFKILL, | ||
1212 | "request to change radio state to %s\n", | ||
1213 | blocked ? "blocked" : "unblocked"); | ||
1214 | |||
1215 | /* try to set radio state */ | ||
1216 | res = (tp_rfk->ops->set_status)(blocked ? | ||
1217 | TPACPI_RFK_RADIO_OFF : TPACPI_RFK_RADIO_ON); | ||
1218 | |||
1219 | /* and update the rfkill core with whatever the FW really did */ | ||
1220 | tpacpi_rfk_update_swstate(tp_rfk); | ||
1221 | |||
1222 | return (res < 0) ? res : 0; | ||
1223 | } | ||
1224 | |||
1225 | static const struct rfkill_ops tpacpi_rfk_rfkill_ops = { | ||
1226 | .set_block = tpacpi_rfk_hook_set_block, | ||
1227 | }; | ||
1228 | |||
1229 | static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | ||
1230 | const struct tpacpi_rfk_ops *tp_rfkops, | ||
1081 | const enum rfkill_type rfktype, | 1231 | const enum rfkill_type rfktype, |
1082 | const char *name, | 1232 | const char *name, |
1083 | const bool set_default, | 1233 | const bool set_default) |
1084 | int (*toggle_radio)(void *, enum rfkill_state), | ||
1085 | int (*get_state)(void *, enum rfkill_state *)) | ||
1086 | { | 1234 | { |
1235 | struct tpacpi_rfk *atp_rfk; | ||
1087 | int res; | 1236 | int res; |
1088 | enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; | 1237 | bool sw_state = false; |
1089 | 1238 | int sw_status; | |
1090 | res = get_state(NULL, &initial_state); | 1239 | |
1091 | if (res < 0) { | 1240 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); |
1092 | printk(TPACPI_ERR | 1241 | |
1093 | "failed to read initial state for %s, error %d; " | 1242 | atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL); |
1094 | "will turn radio off\n", name, res); | 1243 | if (atp_rfk) |
1095 | } else if (set_default) { | 1244 | atp_rfk->rfkill = rfkill_alloc(name, |
1096 | /* try to set the initial state as the default for the rfkill | 1245 | &tpacpi_pdev->dev, |
1097 | * type, since we ask the firmware to preserve it across S5 in | 1246 | rfktype, |
1098 | * NVRAM */ | 1247 | &tpacpi_rfk_rfkill_ops, |
1099 | if (rfkill_set_default(rfktype, | 1248 | atp_rfk); |
1100 | (initial_state == RFKILL_STATE_UNBLOCKED) ? | 1249 | if (!atp_rfk || !atp_rfk->rfkill) { |
1101 | RFKILL_STATE_UNBLOCKED : | ||
1102 | RFKILL_STATE_SOFT_BLOCKED) == -EPERM) | ||
1103 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
1104 | "Default state for %s cannot be changed\n", | ||
1105 | name); | ||
1106 | } | ||
1107 | |||
1108 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | ||
1109 | if (!*rfk) { | ||
1110 | printk(TPACPI_ERR | 1250 | printk(TPACPI_ERR |
1111 | "failed to allocate memory for rfkill class\n"); | 1251 | "failed to allocate memory for rfkill class\n"); |
1252 | kfree(atp_rfk); | ||
1112 | return -ENOMEM; | 1253 | return -ENOMEM; |
1113 | } | 1254 | } |
1114 | 1255 | ||
1115 | (*rfk)->name = name; | 1256 | atp_rfk->id = id; |
1116 | (*rfk)->get_state = get_state; | 1257 | atp_rfk->ops = tp_rfkops; |
1117 | (*rfk)->toggle_radio = toggle_radio; | 1258 | |
1118 | (*rfk)->state = initial_state; | 1259 | sw_status = (tp_rfkops->get_status)(); |
1260 | if (sw_status < 0) { | ||
1261 | printk(TPACPI_ERR | ||
1262 | "failed to read initial state for %s, error %d\n", | ||
1263 | name, sw_status); | ||
1264 | } else { | ||
1265 | sw_state = (sw_status == TPACPI_RFK_RADIO_OFF); | ||
1266 | if (set_default) { | ||
1267 | /* try to keep the initial state, since we ask the | ||
1268 | * firmware to preserve it across S5 in NVRAM */ | ||
1269 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); | ||
1270 | } | ||
1271 | } | ||
1272 | rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); | ||
1119 | 1273 | ||
1120 | res = rfkill_register(*rfk); | 1274 | res = rfkill_register(atp_rfk->rfkill); |
1121 | if (res < 0) { | 1275 | if (res < 0) { |
1122 | printk(TPACPI_ERR | 1276 | printk(TPACPI_ERR |
1123 | "failed to register %s rfkill switch: %d\n", | 1277 | "failed to register %s rfkill switch: %d\n", |
1124 | name, res); | 1278 | name, res); |
1125 | rfkill_free(*rfk); | 1279 | rfkill_destroy(atp_rfk->rfkill); |
1126 | *rfk = NULL; | 1280 | kfree(atp_rfk); |
1127 | return res; | 1281 | return res; |
1128 | } | 1282 | } |
1129 | 1283 | ||
1284 | tpacpi_rfkill_switches[id] = atp_rfk; | ||
1130 | return 0; | 1285 | return 0; |
1131 | } | 1286 | } |
1132 | 1287 | ||
1133 | static void printk_deprecated_attribute(const char * const what, | 1288 | static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id) |
1134 | const char * const details) | ||
1135 | { | 1289 | { |
1136 | tpacpi_log_usertask("deprecated sysfs attribute"); | 1290 | struct tpacpi_rfk *tp_rfk; |
1137 | printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " | 1291 | |
1138 | "will be removed. %s\n", | 1292 | BUG_ON(id >= TPACPI_RFK_SW_MAX); |
1139 | what, details); | 1293 | |
1294 | tp_rfk = tpacpi_rfkill_switches[id]; | ||
1295 | if (tp_rfk) { | ||
1296 | rfkill_unregister(tp_rfk->rfkill); | ||
1297 | tpacpi_rfkill_switches[id] = NULL; | ||
1298 | kfree(tp_rfk); | ||
1299 | } | ||
1140 | } | 1300 | } |
1141 | 1301 | ||
1142 | static void printk_deprecated_rfkill_attribute(const char * const what) | 1302 | static void printk_deprecated_rfkill_attribute(const char * const what) |
@@ -1145,6 +1305,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what) | |||
1145 | "Please switch to generic rfkill before year 2010"); | 1305 | "Please switch to generic rfkill before year 2010"); |
1146 | } | 1306 | } |
1147 | 1307 | ||
1308 | /* sysfs <radio> enable ------------------------------------------------ */ | ||
1309 | static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id, | ||
1310 | struct device_attribute *attr, | ||
1311 | char *buf) | ||
1312 | { | ||
1313 | int status; | ||
1314 | |||
1315 | printk_deprecated_rfkill_attribute(attr->attr.name); | ||
1316 | |||
1317 | /* This is in the ABI... */ | ||
1318 | if (tpacpi_rfk_check_hwblock_state()) { | ||
1319 | status = TPACPI_RFK_RADIO_OFF; | ||
1320 | } else { | ||
1321 | status = tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); | ||
1322 | if (status < 0) | ||
1323 | return status; | ||
1324 | } | ||
1325 | |||
1326 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
1327 | (status == TPACPI_RFK_RADIO_ON) ? 1 : 0); | ||
1328 | } | ||
1329 | |||
1330 | static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, | ||
1331 | struct device_attribute *attr, | ||
1332 | const char *buf, size_t count) | ||
1333 | { | ||
1334 | unsigned long t; | ||
1335 | int res; | ||
1336 | |||
1337 | printk_deprecated_rfkill_attribute(attr->attr.name); | ||
1338 | |||
1339 | if (parse_strtoul(buf, 1, &t)) | ||
1340 | return -EINVAL; | ||
1341 | |||
1342 | tpacpi_disclose_usertask(attr->attr.name, "set to %ld\n", t); | ||
1343 | |||
1344 | /* This is in the ABI... */ | ||
1345 | if (tpacpi_rfk_check_hwblock_state() && !!t) | ||
1346 | return -EPERM; | ||
1347 | |||
1348 | res = tpacpi_rfkill_switches[id]->ops->set_status((!!t) ? | ||
1349 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF); | ||
1350 | tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); | ||
1351 | |||
1352 | return (res < 0) ? res : count; | ||
1353 | } | ||
1354 | |||
1355 | /* procfs -------------------------------------------------------------- */ | ||
1356 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | ||
1357 | { | ||
1358 | int len = 0; | ||
1359 | |||
1360 | if (id >= TPACPI_RFK_SW_MAX) | ||
1361 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
1362 | else { | ||
1363 | int status; | ||
1364 | |||
1365 | /* This is in the ABI... */ | ||
1366 | if (tpacpi_rfk_check_hwblock_state()) { | ||
1367 | status = TPACPI_RFK_RADIO_OFF; | ||
1368 | } else { | ||
1369 | status = tpacpi_rfk_update_swstate( | ||
1370 | tpacpi_rfkill_switches[id]); | ||
1371 | if (status < 0) | ||
1372 | return status; | ||
1373 | } | ||
1374 | |||
1375 | len += sprintf(p + len, "status:\t\t%s\n", | ||
1376 | (status == TPACPI_RFK_RADIO_ON) ? | ||
1377 | "enabled" : "disabled"); | ||
1378 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
1379 | } | ||
1380 | |||
1381 | return len; | ||
1382 | } | ||
1383 | |||
1384 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) | ||
1385 | { | ||
1386 | char *cmd; | ||
1387 | int status = -1; | ||
1388 | int res = 0; | ||
1389 | |||
1390 | if (id >= TPACPI_RFK_SW_MAX) | ||
1391 | return -ENODEV; | ||
1392 | |||
1393 | while ((cmd = next_cmd(&buf))) { | ||
1394 | if (strlencmp(cmd, "enable") == 0) | ||
1395 | status = TPACPI_RFK_RADIO_ON; | ||
1396 | else if (strlencmp(cmd, "disable") == 0) | ||
1397 | status = TPACPI_RFK_RADIO_OFF; | ||
1398 | else | ||
1399 | return -EINVAL; | ||
1400 | } | ||
1401 | |||
1402 | if (status != -1) { | ||
1403 | tpacpi_disclose_usertask("procfs", "attempt to %s %s\n", | ||
1404 | (status == TPACPI_RFK_RADIO_ON) ? | ||
1405 | "enable" : "disable", | ||
1406 | tpacpi_rfkill_names[id]); | ||
1407 | res = (tpacpi_rfkill_switches[id]->ops->set_status)(status); | ||
1408 | tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); | ||
1409 | } | ||
1410 | |||
1411 | return res; | ||
1412 | } | ||
1413 | |||
1148 | /************************************************************************* | 1414 | /************************************************************************* |
1149 | * thinkpad-acpi driver attributes | 1415 | * thinkpad-acpi driver attributes |
1150 | */ | 1416 | */ |
@@ -1198,8 +1464,6 @@ static DRIVER_ATTR(version, S_IRUGO, | |||
1198 | 1464 | ||
1199 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 1465 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
1200 | 1466 | ||
1201 | static void tpacpi_send_radiosw_update(void); | ||
1202 | |||
1203 | /* wlsw_emulstate ------------------------------------------------------ */ | 1467 | /* wlsw_emulstate ------------------------------------------------------ */ |
1204 | static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, | 1468 | static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, |
1205 | char *buf) | 1469 | char *buf) |
@@ -1215,11 +1479,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, | |||
1215 | if (parse_strtoul(buf, 1, &t)) | 1479 | if (parse_strtoul(buf, 1, &t)) |
1216 | return -EINVAL; | 1480 | return -EINVAL; |
1217 | 1481 | ||
1218 | if (tpacpi_wlsw_emulstate != t) { | 1482 | if (tpacpi_wlsw_emulstate != !!t) { |
1219 | tpacpi_wlsw_emulstate = !!t; | ||
1220 | tpacpi_send_radiosw_update(); | ||
1221 | } else | ||
1222 | tpacpi_wlsw_emulstate = !!t; | 1483 | tpacpi_wlsw_emulstate = !!t; |
1484 | tpacpi_rfk_update_hwblock_state(!t); /* negative logic */ | ||
1485 | } | ||
1223 | 1486 | ||
1224 | return count; | 1487 | return count; |
1225 | } | 1488 | } |
@@ -1534,17 +1797,23 @@ static struct attribute_set *hotkey_dev_attributes; | |||
1534 | /* HKEY.MHKG() return bits */ | 1797 | /* HKEY.MHKG() return bits */ |
1535 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | 1798 | #define TP_HOTKEY_TABLET_MASK (1 << 3) |
1536 | 1799 | ||
1537 | static int hotkey_get_wlsw(int *status) | 1800 | static int hotkey_get_wlsw(void) |
1538 | { | 1801 | { |
1802 | int status; | ||
1803 | |||
1804 | if (!tp_features.hotkey_wlsw) | ||
1805 | return -ENODEV; | ||
1806 | |||
1539 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 1807 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
1540 | if (dbg_wlswemul) { | 1808 | if (dbg_wlswemul) |
1541 | *status = !!tpacpi_wlsw_emulstate; | 1809 | return (tpacpi_wlsw_emulstate) ? |
1542 | return 0; | 1810 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
1543 | } | ||
1544 | #endif | 1811 | #endif |
1545 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 1812 | |
1813 | if (!acpi_evalf(hkey_handle, &status, "WLSW", "d")) | ||
1546 | return -EIO; | 1814 | return -EIO; |
1547 | return 0; | 1815 | |
1816 | return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; | ||
1548 | } | 1817 | } |
1549 | 1818 | ||
1550 | static int hotkey_get_tablet_mode(int *status) | 1819 | static int hotkey_get_tablet_mode(int *status) |
@@ -2178,12 +2447,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, | |||
2178 | struct device_attribute *attr, | 2447 | struct device_attribute *attr, |
2179 | char *buf) | 2448 | char *buf) |
2180 | { | 2449 | { |
2181 | int res, s; | 2450 | int res; |
2182 | res = hotkey_get_wlsw(&s); | 2451 | res = hotkey_get_wlsw(); |
2183 | if (res < 0) | 2452 | if (res < 0) |
2184 | return res; | 2453 | return res; |
2185 | 2454 | ||
2186 | return snprintf(buf, PAGE_SIZE, "%d\n", !!s); | 2455 | /* Opportunistic update */ |
2456 | tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF)); | ||
2457 | |||
2458 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
2459 | (res == TPACPI_RFK_RADIO_OFF) ? 0 : 1); | ||
2187 | } | 2460 | } |
2188 | 2461 | ||
2189 | static struct device_attribute dev_attr_hotkey_radio_sw = | 2462 | static struct device_attribute dev_attr_hotkey_radio_sw = |
@@ -2294,30 +2567,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
2294 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | 2567 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, |
2295 | }; | 2568 | }; |
2296 | 2569 | ||
2297 | static void bluetooth_update_rfk(void); | 2570 | /* |
2298 | static void wan_update_rfk(void); | 2571 | * Sync both the hw and sw blocking state of all switches |
2299 | static void uwb_update_rfk(void); | 2572 | */ |
2300 | static void tpacpi_send_radiosw_update(void) | 2573 | static void tpacpi_send_radiosw_update(void) |
2301 | { | 2574 | { |
2302 | int wlsw; | 2575 | int wlsw; |
2303 | 2576 | ||
2304 | /* Sync these BEFORE sending any rfkill events */ | 2577 | /* |
2305 | if (tp_features.bluetooth) | 2578 | * We must sync all rfkill controllers *before* issuing any |
2306 | bluetooth_update_rfk(); | 2579 | * rfkill input events, or we will race the rfkill core input |
2307 | if (tp_features.wan) | 2580 | * handler. |
2308 | wan_update_rfk(); | 2581 | * |
2309 | if (tp_features.uwb) | 2582 | * tpacpi_inputdev_send_mutex works as a syncronization point |
2310 | uwb_update_rfk(); | 2583 | * for the above. |
2584 | * | ||
2585 | * We optimize to avoid numerous calls to hotkey_get_wlsw. | ||
2586 | */ | ||
2587 | |||
2588 | wlsw = hotkey_get_wlsw(); | ||
2589 | |||
2590 | /* Sync hw blocking state first if it is hw-blocked */ | ||
2591 | if (wlsw == TPACPI_RFK_RADIO_OFF) | ||
2592 | tpacpi_rfk_update_hwblock_state(true); | ||
2311 | 2593 | ||
2312 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | 2594 | /* Sync sw blocking state */ |
2595 | tpacpi_rfk_update_swstate_all(); | ||
2596 | |||
2597 | /* Sync hw blocking state last if it is hw-unblocked */ | ||
2598 | if (wlsw == TPACPI_RFK_RADIO_ON) | ||
2599 | tpacpi_rfk_update_hwblock_state(false); | ||
2600 | |||
2601 | /* Issue rfkill input event for WLSW switch */ | ||
2602 | if (!(wlsw < 0)) { | ||
2313 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2603 | mutex_lock(&tpacpi_inputdev_send_mutex); |
2314 | 2604 | ||
2315 | input_report_switch(tpacpi_inputdev, | 2605 | input_report_switch(tpacpi_inputdev, |
2316 | SW_RFKILL_ALL, !!wlsw); | 2606 | SW_RFKILL_ALL, (wlsw > 0)); |
2317 | input_sync(tpacpi_inputdev); | 2607 | input_sync(tpacpi_inputdev); |
2318 | 2608 | ||
2319 | mutex_unlock(&tpacpi_inputdev_send_mutex); | 2609 | mutex_unlock(&tpacpi_inputdev_send_mutex); |
2320 | } | 2610 | } |
2611 | |||
2612 | /* | ||
2613 | * this can be unconditional, as we will poll state again | ||
2614 | * if userspace uses the notify to read data | ||
2615 | */ | ||
2321 | hotkey_radio_sw_notify_change(); | 2616 | hotkey_radio_sw_notify_change(); |
2322 | } | 2617 | } |
2323 | 2618 | ||
@@ -3127,8 +3422,6 @@ enum { | |||
3127 | 3422 | ||
3128 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" | 3423 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" |
3129 | 3424 | ||
3130 | static struct rfkill *tpacpi_bluetooth_rfkill; | ||
3131 | |||
3132 | static void bluetooth_suspend(pm_message_t state) | 3425 | static void bluetooth_suspend(pm_message_t state) |
3133 | { | 3426 | { |
3134 | /* Try to make sure radio will resume powered off */ | 3427 | /* Try to make sure radio will resume powered off */ |
@@ -3138,83 +3431,47 @@ static void bluetooth_suspend(pm_message_t state) | |||
3138 | "bluetooth power down on resume request failed\n"); | 3431 | "bluetooth power down on resume request failed\n"); |
3139 | } | 3432 | } |
3140 | 3433 | ||
3141 | static int bluetooth_get_radiosw(void) | 3434 | static int bluetooth_get_status(void) |
3142 | { | 3435 | { |
3143 | int status; | 3436 | int status; |
3144 | 3437 | ||
3145 | if (!tp_features.bluetooth) | ||
3146 | return -ENODEV; | ||
3147 | |||
3148 | /* WLSW overrides bluetooth in firmware/hardware, reflect that */ | ||
3149 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3150 | return RFKILL_STATE_HARD_BLOCKED; | ||
3151 | |||
3152 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3438 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3153 | if (dbg_bluetoothemul) | 3439 | if (dbg_bluetoothemul) |
3154 | return (tpacpi_bluetooth_emulstate) ? | 3440 | return (tpacpi_bluetooth_emulstate) ? |
3155 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3441 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3156 | #endif | 3442 | #endif |
3157 | 3443 | ||
3158 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3444 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) |
3159 | return -EIO; | 3445 | return -EIO; |
3160 | 3446 | ||
3161 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? | 3447 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? |
3162 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3448 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3163 | } | ||
3164 | |||
3165 | static void bluetooth_update_rfk(void) | ||
3166 | { | ||
3167 | int status; | ||
3168 | |||
3169 | if (!tpacpi_bluetooth_rfkill) | ||
3170 | return; | ||
3171 | |||
3172 | status = bluetooth_get_radiosw(); | ||
3173 | if (status < 0) | ||
3174 | return; | ||
3175 | rfkill_force_state(tpacpi_bluetooth_rfkill, status); | ||
3176 | |||
3177 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3178 | "forced rfkill state to %d\n", | ||
3179 | status); | ||
3180 | } | 3449 | } |
3181 | 3450 | ||
3182 | static int bluetooth_set_radiosw(int radio_on, int update_rfk) | 3451 | static int bluetooth_set_status(enum tpacpi_rfkill_state state) |
3183 | { | 3452 | { |
3184 | int status; | 3453 | int status; |
3185 | 3454 | ||
3186 | if (!tp_features.bluetooth) | ||
3187 | return -ENODEV; | ||
3188 | |||
3189 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
3190 | * reason to risk weird behaviour. */ | ||
3191 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3192 | && radio_on) | ||
3193 | return -EPERM; | ||
3194 | |||
3195 | vdbg_printk(TPACPI_DBG_RFKILL, | 3455 | vdbg_printk(TPACPI_DBG_RFKILL, |
3196 | "will %s bluetooth\n", radio_on ? "enable" : "disable"); | 3456 | "will attempt to %s bluetooth\n", |
3457 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); | ||
3197 | 3458 | ||
3198 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3459 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3199 | if (dbg_bluetoothemul) { | 3460 | if (dbg_bluetoothemul) { |
3200 | tpacpi_bluetooth_emulstate = !!radio_on; | 3461 | tpacpi_bluetooth_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3201 | if (update_rfk) | ||
3202 | bluetooth_update_rfk(); | ||
3203 | return 0; | 3462 | return 0; |
3204 | } | 3463 | } |
3205 | #endif | 3464 | #endif |
3206 | 3465 | ||
3207 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | 3466 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ |
3208 | if (radio_on) | 3467 | if (state == TPACPI_RFK_RADIO_ON) |
3209 | status = TP_ACPI_BLUETOOTH_RADIOSSW; | 3468 | status = TP_ACPI_BLUETOOTH_RADIOSSW; |
3210 | else | 3469 | else |
3211 | status = 0; | 3470 | status = 0; |
3471 | |||
3212 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 3472 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
3213 | return -EIO; | 3473 | return -EIO; |
3214 | 3474 | ||
3215 | if (update_rfk) | ||
3216 | bluetooth_update_rfk(); | ||
3217 | |||
3218 | return 0; | 3475 | return 0; |
3219 | } | 3476 | } |
3220 | 3477 | ||
@@ -3223,35 +3480,16 @@ static ssize_t bluetooth_enable_show(struct device *dev, | |||
3223 | struct device_attribute *attr, | 3480 | struct device_attribute *attr, |
3224 | char *buf) | 3481 | char *buf) |
3225 | { | 3482 | { |
3226 | int status; | 3483 | return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_BLUETOOTH_SW_ID, |
3227 | 3484 | attr, buf); | |
3228 | printk_deprecated_rfkill_attribute("bluetooth_enable"); | ||
3229 | |||
3230 | status = bluetooth_get_radiosw(); | ||
3231 | if (status < 0) | ||
3232 | return status; | ||
3233 | |||
3234 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
3235 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
3236 | } | 3485 | } |
3237 | 3486 | ||
3238 | static ssize_t bluetooth_enable_store(struct device *dev, | 3487 | static ssize_t bluetooth_enable_store(struct device *dev, |
3239 | struct device_attribute *attr, | 3488 | struct device_attribute *attr, |
3240 | const char *buf, size_t count) | 3489 | const char *buf, size_t count) |
3241 | { | 3490 | { |
3242 | unsigned long t; | 3491 | return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_BLUETOOTH_SW_ID, |
3243 | int res; | 3492 | attr, buf, count); |
3244 | |||
3245 | printk_deprecated_rfkill_attribute("bluetooth_enable"); | ||
3246 | |||
3247 | if (parse_strtoul(buf, 1, &t)) | ||
3248 | return -EINVAL; | ||
3249 | |||
3250 | tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t); | ||
3251 | |||
3252 | res = bluetooth_set_radiosw(t, 1); | ||
3253 | |||
3254 | return (res) ? res : count; | ||
3255 | } | 3493 | } |
3256 | 3494 | ||
3257 | static struct device_attribute dev_attr_bluetooth_enable = | 3495 | static struct device_attribute dev_attr_bluetooth_enable = |
@@ -3269,23 +3507,10 @@ static const struct attribute_group bluetooth_attr_group = { | |||
3269 | .attrs = bluetooth_attributes, | 3507 | .attrs = bluetooth_attributes, |
3270 | }; | 3508 | }; |
3271 | 3509 | ||
3272 | static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) | 3510 | static const struct tpacpi_rfk_ops bluetooth_tprfk_ops = { |
3273 | { | 3511 | .get_status = bluetooth_get_status, |
3274 | int bts = bluetooth_get_radiosw(); | 3512 | .set_status = bluetooth_set_status, |
3275 | 3513 | }; | |
3276 | if (bts < 0) | ||
3277 | return bts; | ||
3278 | |||
3279 | *state = bts; | ||
3280 | return 0; | ||
3281 | } | ||
3282 | |||
3283 | static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | ||
3284 | { | ||
3285 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3286 | "request to change radio state to %d\n", state); | ||
3287 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3288 | } | ||
3289 | 3514 | ||
3290 | static void bluetooth_shutdown(void) | 3515 | static void bluetooth_shutdown(void) |
3291 | { | 3516 | { |
@@ -3301,13 +3526,12 @@ static void bluetooth_shutdown(void) | |||
3301 | 3526 | ||
3302 | static void bluetooth_exit(void) | 3527 | static void bluetooth_exit(void) |
3303 | { | 3528 | { |
3304 | bluetooth_shutdown(); | ||
3305 | |||
3306 | if (tpacpi_bluetooth_rfkill) | ||
3307 | rfkill_unregister(tpacpi_bluetooth_rfkill); | ||
3308 | |||
3309 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | 3529 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, |
3310 | &bluetooth_attr_group); | 3530 | &bluetooth_attr_group); |
3531 | |||
3532 | tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); | ||
3533 | |||
3534 | bluetooth_shutdown(); | ||
3311 | } | 3535 | } |
3312 | 3536 | ||
3313 | static int __init bluetooth_init(struct ibm_init_struct *iibm) | 3537 | static int __init bluetooth_init(struct ibm_init_struct *iibm) |
@@ -3348,20 +3572,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3348 | if (!tp_features.bluetooth) | 3572 | if (!tp_features.bluetooth) |
3349 | return 1; | 3573 | return 1; |
3350 | 3574 | ||
3351 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3352 | &bluetooth_attr_group); | ||
3353 | if (res) | ||
3354 | return res; | ||
3355 | |||
3356 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, | 3575 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, |
3357 | &tpacpi_bluetooth_rfkill, | 3576 | &bluetooth_tprfk_ops, |
3358 | RFKILL_TYPE_BLUETOOTH, | 3577 | RFKILL_TYPE_BLUETOOTH, |
3359 | TPACPI_RFK_BLUETOOTH_SW_NAME, | 3578 | TPACPI_RFK_BLUETOOTH_SW_NAME, |
3360 | true, | 3579 | true); |
3361 | tpacpi_bluetooth_rfk_set, | 3580 | if (res) |
3362 | tpacpi_bluetooth_rfk_get); | 3581 | return res; |
3582 | |||
3583 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3584 | &bluetooth_attr_group); | ||
3363 | if (res) { | 3585 | if (res) { |
3364 | bluetooth_exit(); | 3586 | tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); |
3365 | return res; | 3587 | return res; |
3366 | } | 3588 | } |
3367 | 3589 | ||
@@ -3371,46 +3593,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3371 | /* procfs -------------------------------------------------------------- */ | 3593 | /* procfs -------------------------------------------------------------- */ |
3372 | static int bluetooth_read(char *p) | 3594 | static int bluetooth_read(char *p) |
3373 | { | 3595 | { |
3374 | int len = 0; | 3596 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); |
3375 | int status = bluetooth_get_radiosw(); | ||
3376 | |||
3377 | if (!tp_features.bluetooth) | ||
3378 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
3379 | else { | ||
3380 | len += sprintf(p + len, "status:\t\t%s\n", | ||
3381 | (status == RFKILL_STATE_UNBLOCKED) ? | ||
3382 | "enabled" : "disabled"); | ||
3383 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
3384 | } | ||
3385 | |||
3386 | return len; | ||
3387 | } | 3597 | } |
3388 | 3598 | ||
3389 | static int bluetooth_write(char *buf) | 3599 | static int bluetooth_write(char *buf) |
3390 | { | 3600 | { |
3391 | char *cmd; | 3601 | return tpacpi_rfk_procfs_write(TPACPI_RFK_BLUETOOTH_SW_ID, buf); |
3392 | int state = -1; | ||
3393 | |||
3394 | if (!tp_features.bluetooth) | ||
3395 | return -ENODEV; | ||
3396 | |||
3397 | while ((cmd = next_cmd(&buf))) { | ||
3398 | if (strlencmp(cmd, "enable") == 0) { | ||
3399 | state = 1; | ||
3400 | } else if (strlencmp(cmd, "disable") == 0) { | ||
3401 | state = 0; | ||
3402 | } else | ||
3403 | return -EINVAL; | ||
3404 | } | ||
3405 | |||
3406 | if (state != -1) { | ||
3407 | tpacpi_disclose_usertask("procfs bluetooth", | ||
3408 | "attempt to %s\n", | ||
3409 | state ? "enable" : "disable"); | ||
3410 | bluetooth_set_radiosw(state, 1); | ||
3411 | } | ||
3412 | |||
3413 | return 0; | ||
3414 | } | 3602 | } |
3415 | 3603 | ||
3416 | static struct ibm_struct bluetooth_driver_data = { | 3604 | static struct ibm_struct bluetooth_driver_data = { |
@@ -3436,8 +3624,6 @@ enum { | |||
3436 | 3624 | ||
3437 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" | 3625 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" |
3438 | 3626 | ||
3439 | static struct rfkill *tpacpi_wan_rfkill; | ||
3440 | |||
3441 | static void wan_suspend(pm_message_t state) | 3627 | static void wan_suspend(pm_message_t state) |
3442 | { | 3628 | { |
3443 | /* Try to make sure radio will resume powered off */ | 3629 | /* Try to make sure radio will resume powered off */ |
@@ -3447,83 +3633,47 @@ static void wan_suspend(pm_message_t state) | |||
3447 | "WWAN power down on resume request failed\n"); | 3633 | "WWAN power down on resume request failed\n"); |
3448 | } | 3634 | } |
3449 | 3635 | ||
3450 | static int wan_get_radiosw(void) | 3636 | static int wan_get_status(void) |
3451 | { | 3637 | { |
3452 | int status; | 3638 | int status; |
3453 | 3639 | ||
3454 | if (!tp_features.wan) | ||
3455 | return -ENODEV; | ||
3456 | |||
3457 | /* WLSW overrides WWAN in firmware/hardware, reflect that */ | ||
3458 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3459 | return RFKILL_STATE_HARD_BLOCKED; | ||
3460 | |||
3461 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3640 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3462 | if (dbg_wwanemul) | 3641 | if (dbg_wwanemul) |
3463 | return (tpacpi_wwan_emulstate) ? | 3642 | return (tpacpi_wwan_emulstate) ? |
3464 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3643 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3465 | #endif | 3644 | #endif |
3466 | 3645 | ||
3467 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3646 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) |
3468 | return -EIO; | 3647 | return -EIO; |
3469 | 3648 | ||
3470 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? | 3649 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? |
3471 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3650 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3472 | } | ||
3473 | |||
3474 | static void wan_update_rfk(void) | ||
3475 | { | ||
3476 | int status; | ||
3477 | |||
3478 | if (!tpacpi_wan_rfkill) | ||
3479 | return; | ||
3480 | |||
3481 | status = wan_get_radiosw(); | ||
3482 | if (status < 0) | ||
3483 | return; | ||
3484 | rfkill_force_state(tpacpi_wan_rfkill, status); | ||
3485 | |||
3486 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3487 | "forced rfkill state to %d\n", | ||
3488 | status); | ||
3489 | } | 3651 | } |
3490 | 3652 | ||
3491 | static int wan_set_radiosw(int radio_on, int update_rfk) | 3653 | static int wan_set_status(enum tpacpi_rfkill_state state) |
3492 | { | 3654 | { |
3493 | int status; | 3655 | int status; |
3494 | 3656 | ||
3495 | if (!tp_features.wan) | ||
3496 | return -ENODEV; | ||
3497 | |||
3498 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
3499 | * reason to risk weird behaviour. */ | ||
3500 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3501 | && radio_on) | ||
3502 | return -EPERM; | ||
3503 | |||
3504 | vdbg_printk(TPACPI_DBG_RFKILL, | 3657 | vdbg_printk(TPACPI_DBG_RFKILL, |
3505 | "will %s WWAN\n", radio_on ? "enable" : "disable"); | 3658 | "will attempt to %s wwan\n", |
3659 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); | ||
3506 | 3660 | ||
3507 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3661 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3508 | if (dbg_wwanemul) { | 3662 | if (dbg_wwanemul) { |
3509 | tpacpi_wwan_emulstate = !!radio_on; | 3663 | tpacpi_wwan_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3510 | if (update_rfk) | ||
3511 | wan_update_rfk(); | ||
3512 | return 0; | 3664 | return 0; |
3513 | } | 3665 | } |
3514 | #endif | 3666 | #endif |
3515 | 3667 | ||
3516 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | 3668 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ |
3517 | if (radio_on) | 3669 | if (state == TPACPI_RFK_RADIO_ON) |
3518 | status = TP_ACPI_WANCARD_RADIOSSW; | 3670 | status = TP_ACPI_WANCARD_RADIOSSW; |
3519 | else | 3671 | else |
3520 | status = 0; | 3672 | status = 0; |
3673 | |||
3521 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3674 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) |
3522 | return -EIO; | 3675 | return -EIO; |
3523 | 3676 | ||
3524 | if (update_rfk) | ||
3525 | wan_update_rfk(); | ||
3526 | |||
3527 | return 0; | 3677 | return 0; |
3528 | } | 3678 | } |
3529 | 3679 | ||
@@ -3532,35 +3682,16 @@ static ssize_t wan_enable_show(struct device *dev, | |||
3532 | struct device_attribute *attr, | 3682 | struct device_attribute *attr, |
3533 | char *buf) | 3683 | char *buf) |
3534 | { | 3684 | { |
3535 | int status; | 3685 | return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_WWAN_SW_ID, |
3536 | 3686 | attr, buf); | |
3537 | printk_deprecated_rfkill_attribute("wwan_enable"); | ||
3538 | |||
3539 | status = wan_get_radiosw(); | ||
3540 | if (status < 0) | ||
3541 | return status; | ||
3542 | |||
3543 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
3544 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
3545 | } | 3687 | } |
3546 | 3688 | ||
3547 | static ssize_t wan_enable_store(struct device *dev, | 3689 | static ssize_t wan_enable_store(struct device *dev, |
3548 | struct device_attribute *attr, | 3690 | struct device_attribute *attr, |
3549 | const char *buf, size_t count) | 3691 | const char *buf, size_t count) |
3550 | { | 3692 | { |
3551 | unsigned long t; | 3693 | return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_WWAN_SW_ID, |
3552 | int res; | 3694 | attr, buf, count); |
3553 | |||
3554 | printk_deprecated_rfkill_attribute("wwan_enable"); | ||
3555 | |||
3556 | if (parse_strtoul(buf, 1, &t)) | ||
3557 | return -EINVAL; | ||
3558 | |||
3559 | tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t); | ||
3560 | |||
3561 | res = wan_set_radiosw(t, 1); | ||
3562 | |||
3563 | return (res) ? res : count; | ||
3564 | } | 3695 | } |
3565 | 3696 | ||
3566 | static struct device_attribute dev_attr_wan_enable = | 3697 | static struct device_attribute dev_attr_wan_enable = |
@@ -3578,23 +3709,10 @@ static const struct attribute_group wan_attr_group = { | |||
3578 | .attrs = wan_attributes, | 3709 | .attrs = wan_attributes, |
3579 | }; | 3710 | }; |
3580 | 3711 | ||
3581 | static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) | 3712 | static const struct tpacpi_rfk_ops wan_tprfk_ops = { |
3582 | { | 3713 | .get_status = wan_get_status, |
3583 | int wans = wan_get_radiosw(); | 3714 | .set_status = wan_set_status, |
3584 | 3715 | }; | |
3585 | if (wans < 0) | ||
3586 | return wans; | ||
3587 | |||
3588 | *state = wans; | ||
3589 | return 0; | ||
3590 | } | ||
3591 | |||
3592 | static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | ||
3593 | { | ||
3594 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3595 | "request to change radio state to %d\n", state); | ||
3596 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3597 | } | ||
3598 | 3716 | ||
3599 | static void wan_shutdown(void) | 3717 | static void wan_shutdown(void) |
3600 | { | 3718 | { |
@@ -3610,13 +3728,12 @@ static void wan_shutdown(void) | |||
3610 | 3728 | ||
3611 | static void wan_exit(void) | 3729 | static void wan_exit(void) |
3612 | { | 3730 | { |
3613 | wan_shutdown(); | ||
3614 | |||
3615 | if (tpacpi_wan_rfkill) | ||
3616 | rfkill_unregister(tpacpi_wan_rfkill); | ||
3617 | |||
3618 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | 3731 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, |
3619 | &wan_attr_group); | 3732 | &wan_attr_group); |
3733 | |||
3734 | tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); | ||
3735 | |||
3736 | wan_shutdown(); | ||
3620 | } | 3737 | } |
3621 | 3738 | ||
3622 | static int __init wan_init(struct ibm_init_struct *iibm) | 3739 | static int __init wan_init(struct ibm_init_struct *iibm) |
@@ -3655,20 +3772,19 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3655 | if (!tp_features.wan) | 3772 | if (!tp_features.wan) |
3656 | return 1; | 3773 | return 1; |
3657 | 3774 | ||
3658 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3659 | &wan_attr_group); | ||
3660 | if (res) | ||
3661 | return res; | ||
3662 | |||
3663 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, | 3775 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, |
3664 | &tpacpi_wan_rfkill, | 3776 | &wan_tprfk_ops, |
3665 | RFKILL_TYPE_WWAN, | 3777 | RFKILL_TYPE_WWAN, |
3666 | TPACPI_RFK_WWAN_SW_NAME, | 3778 | TPACPI_RFK_WWAN_SW_NAME, |
3667 | true, | 3779 | true); |
3668 | tpacpi_wan_rfk_set, | 3780 | if (res) |
3669 | tpacpi_wan_rfk_get); | 3781 | return res; |
3782 | |||
3783 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3784 | &wan_attr_group); | ||
3785 | |||
3670 | if (res) { | 3786 | if (res) { |
3671 | wan_exit(); | 3787 | tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); |
3672 | return res; | 3788 | return res; |
3673 | } | 3789 | } |
3674 | 3790 | ||
@@ -3678,48 +3794,12 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3678 | /* procfs -------------------------------------------------------------- */ | 3794 | /* procfs -------------------------------------------------------------- */ |
3679 | static int wan_read(char *p) | 3795 | static int wan_read(char *p) |
3680 | { | 3796 | { |
3681 | int len = 0; | 3797 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); |
3682 | int status = wan_get_radiosw(); | ||
3683 | |||
3684 | tpacpi_disclose_usertask("procfs wan", "read"); | ||
3685 | |||
3686 | if (!tp_features.wan) | ||
3687 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
3688 | else { | ||
3689 | len += sprintf(p + len, "status:\t\t%s\n", | ||
3690 | (status == RFKILL_STATE_UNBLOCKED) ? | ||
3691 | "enabled" : "disabled"); | ||
3692 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
3693 | } | ||
3694 | |||
3695 | return len; | ||
3696 | } | 3798 | } |
3697 | 3799 | ||
3698 | static int wan_write(char *buf) | 3800 | static int wan_write(char *buf) |
3699 | { | 3801 | { |
3700 | char *cmd; | 3802 | return tpacpi_rfk_procfs_write(TPACPI_RFK_WWAN_SW_ID, buf); |
3701 | int state = -1; | ||
3702 | |||
3703 | if (!tp_features.wan) | ||
3704 | return -ENODEV; | ||
3705 | |||
3706 | while ((cmd = next_cmd(&buf))) { | ||
3707 | if (strlencmp(cmd, "enable") == 0) { | ||
3708 | state = 1; | ||
3709 | } else if (strlencmp(cmd, "disable") == 0) { | ||
3710 | state = 0; | ||
3711 | } else | ||
3712 | return -EINVAL; | ||
3713 | } | ||
3714 | |||
3715 | if (state != -1) { | ||
3716 | tpacpi_disclose_usertask("procfs wan", | ||
3717 | "attempt to %s\n", | ||
3718 | state ? "enable" : "disable"); | ||
3719 | wan_set_radiosw(state, 1); | ||
3720 | } | ||
3721 | |||
3722 | return 0; | ||
3723 | } | 3803 | } |
3724 | 3804 | ||
3725 | static struct ibm_struct wan_driver_data = { | 3805 | static struct ibm_struct wan_driver_data = { |
@@ -3743,108 +3823,59 @@ enum { | |||
3743 | 3823 | ||
3744 | #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" | 3824 | #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" |
3745 | 3825 | ||
3746 | static struct rfkill *tpacpi_uwb_rfkill; | 3826 | static int uwb_get_status(void) |
3747 | |||
3748 | static int uwb_get_radiosw(void) | ||
3749 | { | 3827 | { |
3750 | int status; | 3828 | int status; |
3751 | 3829 | ||
3752 | if (!tp_features.uwb) | ||
3753 | return -ENODEV; | ||
3754 | |||
3755 | /* WLSW overrides UWB in firmware/hardware, reflect that */ | ||
3756 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3757 | return RFKILL_STATE_HARD_BLOCKED; | ||
3758 | |||
3759 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3830 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3760 | if (dbg_uwbemul) | 3831 | if (dbg_uwbemul) |
3761 | return (tpacpi_uwb_emulstate) ? | 3832 | return (tpacpi_uwb_emulstate) ? |
3762 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3833 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3763 | #endif | 3834 | #endif |
3764 | 3835 | ||
3765 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) | 3836 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) |
3766 | return -EIO; | 3837 | return -EIO; |
3767 | 3838 | ||
3768 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? | 3839 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? |
3769 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3840 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3770 | } | 3841 | } |
3771 | 3842 | ||
3772 | static void uwb_update_rfk(void) | 3843 | static int uwb_set_status(enum tpacpi_rfkill_state state) |
3773 | { | 3844 | { |
3774 | int status; | 3845 | int status; |
3775 | 3846 | ||
3776 | if (!tpacpi_uwb_rfkill) | ||
3777 | return; | ||
3778 | |||
3779 | status = uwb_get_radiosw(); | ||
3780 | if (status < 0) | ||
3781 | return; | ||
3782 | rfkill_force_state(tpacpi_uwb_rfkill, status); | ||
3783 | |||
3784 | vdbg_printk(TPACPI_DBG_RFKILL, | 3847 | vdbg_printk(TPACPI_DBG_RFKILL, |
3785 | "forced rfkill state to %d\n", | 3848 | "will attempt to %s UWB\n", |
3786 | status); | 3849 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); |
3787 | } | ||
3788 | |||
3789 | static int uwb_set_radiosw(int radio_on, int update_rfk) | ||
3790 | { | ||
3791 | int status; | ||
3792 | |||
3793 | if (!tp_features.uwb) | ||
3794 | return -ENODEV; | ||
3795 | |||
3796 | /* WLSW overrides UWB in firmware/hardware, but there is no | ||
3797 | * reason to risk weird behaviour. */ | ||
3798 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3799 | && radio_on) | ||
3800 | return -EPERM; | ||
3801 | |||
3802 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3803 | "will %s UWB\n", radio_on ? "enable" : "disable"); | ||
3804 | 3850 | ||
3805 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3851 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3806 | if (dbg_uwbemul) { | 3852 | if (dbg_uwbemul) { |
3807 | tpacpi_uwb_emulstate = !!radio_on; | 3853 | tpacpi_uwb_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3808 | if (update_rfk) | ||
3809 | uwb_update_rfk(); | ||
3810 | return 0; | 3854 | return 0; |
3811 | } | 3855 | } |
3812 | #endif | 3856 | #endif |
3813 | 3857 | ||
3814 | status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; | 3858 | if (state == TPACPI_RFK_RADIO_ON) |
3859 | status = TP_ACPI_UWB_RADIOSSW; | ||
3860 | else | ||
3861 | status = 0; | ||
3862 | |||
3815 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) | 3863 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) |
3816 | return -EIO; | 3864 | return -EIO; |
3817 | 3865 | ||
3818 | if (update_rfk) | ||
3819 | uwb_update_rfk(); | ||
3820 | |||
3821 | return 0; | 3866 | return 0; |
3822 | } | 3867 | } |
3823 | 3868 | ||
3824 | /* --------------------------------------------------------------------- */ | 3869 | /* --------------------------------------------------------------------- */ |
3825 | 3870 | ||
3826 | static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) | 3871 | static const struct tpacpi_rfk_ops uwb_tprfk_ops = { |
3827 | { | 3872 | .get_status = uwb_get_status, |
3828 | int uwbs = uwb_get_radiosw(); | 3873 | .set_status = uwb_set_status, |
3829 | 3874 | }; | |
3830 | if (uwbs < 0) | ||
3831 | return uwbs; | ||
3832 | |||
3833 | *state = uwbs; | ||
3834 | return 0; | ||
3835 | } | ||
3836 | |||
3837 | static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) | ||
3838 | { | ||
3839 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3840 | "request to change radio state to %d\n", state); | ||
3841 | return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3842 | } | ||
3843 | 3875 | ||
3844 | static void uwb_exit(void) | 3876 | static void uwb_exit(void) |
3845 | { | 3877 | { |
3846 | if (tpacpi_uwb_rfkill) | 3878 | tpacpi_destroy_rfkill(TPACPI_RFK_UWB_SW_ID); |
3847 | rfkill_unregister(tpacpi_uwb_rfkill); | ||
3848 | } | 3879 | } |
3849 | 3880 | ||
3850 | static int __init uwb_init(struct ibm_init_struct *iibm) | 3881 | static int __init uwb_init(struct ibm_init_struct *iibm) |
@@ -3884,13 +3915,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm) | |||
3884 | return 1; | 3915 | return 1; |
3885 | 3916 | ||
3886 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, | 3917 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, |
3887 | &tpacpi_uwb_rfkill, | 3918 | &uwb_tprfk_ops, |
3888 | RFKILL_TYPE_UWB, | 3919 | RFKILL_TYPE_UWB, |
3889 | TPACPI_RFK_UWB_SW_NAME, | 3920 | TPACPI_RFK_UWB_SW_NAME, |
3890 | false, | 3921 | false); |
3891 | tpacpi_uwb_rfk_set, | ||
3892 | tpacpi_uwb_rfk_get); | ||
3893 | |||
3894 | return res; | 3922 | return res; |
3895 | } | 3923 | } |
3896 | 3924 | ||
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 9f187265db8e..81d31ea507d1 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -45,7 +45,6 @@ | |||
45 | #include <linux/backlight.h> | 45 | #include <linux/backlight.h> |
46 | #include <linux/platform_device.h> | 46 | #include <linux/platform_device.h> |
47 | #include <linux/rfkill.h> | 47 | #include <linux/rfkill.h> |
48 | #include <linux/input-polldev.h> | ||
49 | 48 | ||
50 | #include <asm/uaccess.h> | 49 | #include <asm/uaccess.h> |
51 | 50 | ||
@@ -250,21 +249,15 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) | |||
250 | 249 | ||
251 | struct toshiba_acpi_dev { | 250 | struct toshiba_acpi_dev { |
252 | struct platform_device *p_dev; | 251 | struct platform_device *p_dev; |
253 | struct rfkill *rfk_dev; | 252 | struct rfkill *bt_rfk; |
254 | struct input_polled_dev *poll_dev; | ||
255 | 253 | ||
256 | const char *bt_name; | 254 | const char *bt_name; |
257 | const char *rfk_name; | ||
258 | |||
259 | bool last_rfk_state; | ||
260 | 255 | ||
261 | struct mutex mutex; | 256 | struct mutex mutex; |
262 | }; | 257 | }; |
263 | 258 | ||
264 | static struct toshiba_acpi_dev toshiba_acpi = { | 259 | static struct toshiba_acpi_dev toshiba_acpi = { |
265 | .bt_name = "Toshiba Bluetooth", | 260 | .bt_name = "Toshiba Bluetooth", |
266 | .rfk_name = "Toshiba RFKill Switch", | ||
267 | .last_rfk_state = false, | ||
268 | }; | 261 | }; |
269 | 262 | ||
270 | /* Bluetooth rfkill handlers */ | 263 | /* Bluetooth rfkill handlers */ |
@@ -283,21 +276,6 @@ static u32 hci_get_bt_present(bool *present) | |||
283 | return hci_result; | 276 | return hci_result; |
284 | } | 277 | } |
285 | 278 | ||
286 | static u32 hci_get_bt_on(bool *on) | ||
287 | { | ||
288 | u32 hci_result; | ||
289 | u32 value, value2; | ||
290 | |||
291 | value = 0; | ||
292 | value2 = 0x0001; | ||
293 | hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); | ||
294 | if (hci_result == HCI_SUCCESS) | ||
295 | *on = (value & HCI_WIRELESS_BT_POWER) && | ||
296 | (value & HCI_WIRELESS_BT_ATTACH); | ||
297 | |||
298 | return hci_result; | ||
299 | } | ||
300 | |||
301 | static u32 hci_get_radio_state(bool *radio_state) | 279 | static u32 hci_get_radio_state(bool *radio_state) |
302 | { | 280 | { |
303 | u32 hci_result; | 281 | u32 hci_result; |
@@ -311,70 +289,67 @@ static u32 hci_get_radio_state(bool *radio_state) | |||
311 | return hci_result; | 289 | return hci_result; |
312 | } | 290 | } |
313 | 291 | ||
314 | static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state) | 292 | static int bt_rfkill_set_block(void *data, bool blocked) |
315 | { | 293 | { |
294 | struct toshiba_acpi_dev *dev = data; | ||
316 | u32 result1, result2; | 295 | u32 result1, result2; |
317 | u32 value; | 296 | u32 value; |
297 | int err; | ||
318 | bool radio_state; | 298 | bool radio_state; |
319 | struct toshiba_acpi_dev *dev = data; | ||
320 | 299 | ||
321 | value = (state == RFKILL_STATE_UNBLOCKED); | 300 | value = (blocked == false); |
322 | 301 | ||
323 | if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) | 302 | mutex_lock(&dev->mutex); |
324 | return -EFAULT; | 303 | if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) { |
304 | err = -EBUSY; | ||
305 | goto out; | ||
306 | } | ||
325 | 307 | ||
326 | switch (state) { | 308 | if (!radio_state) { |
327 | case RFKILL_STATE_UNBLOCKED: | 309 | err = 0; |
328 | if (!radio_state) | 310 | goto out; |
329 | return -EPERM; | ||
330 | break; | ||
331 | case RFKILL_STATE_SOFT_BLOCKED: | ||
332 | break; | ||
333 | default: | ||
334 | return -EINVAL; | ||
335 | } | 311 | } |
336 | 312 | ||
337 | mutex_lock(&dev->mutex); | ||
338 | hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); | 313 | hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); |
339 | hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); | 314 | hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); |
340 | mutex_unlock(&dev->mutex); | ||
341 | 315 | ||
342 | if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) | 316 | if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) |
343 | return -EFAULT; | 317 | err = -EBUSY; |
344 | 318 | else | |
345 | return 0; | 319 | err = 0; |
320 | out: | ||
321 | mutex_unlock(&dev->mutex); | ||
322 | return err; | ||
346 | } | 323 | } |
347 | 324 | ||
348 | static void bt_poll_rfkill(struct input_polled_dev *poll_dev) | 325 | static void bt_rfkill_poll(struct rfkill *rfkill, void *data) |
349 | { | 326 | { |
350 | bool state_changed; | ||
351 | bool new_rfk_state; | 327 | bool new_rfk_state; |
352 | bool value; | 328 | bool value; |
353 | u32 hci_result; | 329 | u32 hci_result; |
354 | struct toshiba_acpi_dev *dev = poll_dev->private; | 330 | struct toshiba_acpi_dev *dev = data; |
331 | |||
332 | mutex_lock(&dev->mutex); | ||
355 | 333 | ||
356 | hci_result = hci_get_radio_state(&value); | 334 | hci_result = hci_get_radio_state(&value); |
357 | if (hci_result != HCI_SUCCESS) | 335 | if (hci_result != HCI_SUCCESS) { |
358 | return; /* Can't do anything useful */ | 336 | /* Can't do anything useful */ |
337 | mutex_unlock(&dev->mutex); | ||
338 | } | ||
359 | 339 | ||
360 | new_rfk_state = value; | 340 | new_rfk_state = value; |
361 | 341 | ||
362 | mutex_lock(&dev->mutex); | ||
363 | state_changed = new_rfk_state != dev->last_rfk_state; | ||
364 | dev->last_rfk_state = new_rfk_state; | ||
365 | mutex_unlock(&dev->mutex); | 342 | mutex_unlock(&dev->mutex); |
366 | 343 | ||
367 | if (unlikely(state_changed)) { | 344 | if (rfkill_set_hw_state(rfkill, !new_rfk_state)) |
368 | rfkill_force_state(dev->rfk_dev, | 345 | bt_rfkill_set_block(data, true); |
369 | new_rfk_state ? | ||
370 | RFKILL_STATE_SOFT_BLOCKED : | ||
371 | RFKILL_STATE_HARD_BLOCKED); | ||
372 | input_report_switch(poll_dev->input, SW_RFKILL_ALL, | ||
373 | new_rfk_state); | ||
374 | input_sync(poll_dev->input); | ||
375 | } | ||
376 | } | 346 | } |
377 | 347 | ||
348 | static const struct rfkill_ops toshiba_rfk_ops = { | ||
349 | .set_block = bt_rfkill_set_block, | ||
350 | .poll = bt_rfkill_poll, | ||
351 | }; | ||
352 | |||
378 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; | 353 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; |
379 | static struct backlight_device *toshiba_backlight_device; | 354 | static struct backlight_device *toshiba_backlight_device; |
380 | static int force_fan; | 355 | static int force_fan; |
@@ -702,14 +677,11 @@ static struct backlight_ops toshiba_backlight_data = { | |||
702 | 677 | ||
703 | static void toshiba_acpi_exit(void) | 678 | static void toshiba_acpi_exit(void) |
704 | { | 679 | { |
705 | if (toshiba_acpi.poll_dev) { | 680 | if (toshiba_acpi.bt_rfk) { |
706 | input_unregister_polled_device(toshiba_acpi.poll_dev); | 681 | rfkill_unregister(toshiba_acpi.bt_rfk); |
707 | input_free_polled_device(toshiba_acpi.poll_dev); | 682 | rfkill_destroy(toshiba_acpi.bt_rfk); |
708 | } | 683 | } |
709 | 684 | ||
710 | if (toshiba_acpi.rfk_dev) | ||
711 | rfkill_unregister(toshiba_acpi.rfk_dev); | ||
712 | |||
713 | if (toshiba_backlight_device) | 685 | if (toshiba_backlight_device) |
714 | backlight_device_unregister(toshiba_backlight_device); | 686 | backlight_device_unregister(toshiba_backlight_device); |
715 | 687 | ||
@@ -728,8 +700,6 @@ static int __init toshiba_acpi_init(void) | |||
728 | acpi_status status = AE_OK; | 700 | acpi_status status = AE_OK; |
729 | u32 hci_result; | 701 | u32 hci_result; |
730 | bool bt_present; | 702 | bool bt_present; |
731 | bool bt_on; | ||
732 | bool radio_on; | ||
733 | int ret = 0; | 703 | int ret = 0; |
734 | 704 | ||
735 | if (acpi_disabled) | 705 | if (acpi_disabled) |
@@ -793,61 +763,21 @@ static int __init toshiba_acpi_init(void) | |||
793 | 763 | ||
794 | /* Register rfkill switch for Bluetooth */ | 764 | /* Register rfkill switch for Bluetooth */ |
795 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { | 765 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { |
796 | toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev, | 766 | toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name, |
797 | RFKILL_TYPE_BLUETOOTH); | 767 | &toshiba_acpi.p_dev->dev, |
798 | if (!toshiba_acpi.rfk_dev) { | 768 | RFKILL_TYPE_BLUETOOTH, |
769 | &toshiba_rfk_ops, | ||
770 | &toshiba_acpi); | ||
771 | if (!toshiba_acpi.bt_rfk) { | ||
799 | printk(MY_ERR "unable to allocate rfkill device\n"); | 772 | printk(MY_ERR "unable to allocate rfkill device\n"); |
800 | toshiba_acpi_exit(); | 773 | toshiba_acpi_exit(); |
801 | return -ENOMEM; | 774 | return -ENOMEM; |
802 | } | 775 | } |
803 | 776 | ||
804 | toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name; | 777 | ret = rfkill_register(toshiba_acpi.bt_rfk); |
805 | toshiba_acpi.rfk_dev->toggle_radio = bt_rfkill_toggle_radio; | ||
806 | toshiba_acpi.rfk_dev->user_claim_unsupported = 1; | ||
807 | toshiba_acpi.rfk_dev->data = &toshiba_acpi; | ||
808 | |||
809 | if (hci_get_bt_on(&bt_on) == HCI_SUCCESS && bt_on) { | ||
810 | toshiba_acpi.rfk_dev->state = RFKILL_STATE_UNBLOCKED; | ||
811 | } else if (hci_get_radio_state(&radio_on) == HCI_SUCCESS && | ||
812 | radio_on) { | ||
813 | toshiba_acpi.rfk_dev->state = RFKILL_STATE_SOFT_BLOCKED; | ||
814 | } else { | ||
815 | toshiba_acpi.rfk_dev->state = RFKILL_STATE_HARD_BLOCKED; | ||
816 | } | ||
817 | |||
818 | ret = rfkill_register(toshiba_acpi.rfk_dev); | ||
819 | if (ret) { | 778 | if (ret) { |
820 | printk(MY_ERR "unable to register rfkill device\n"); | 779 | printk(MY_ERR "unable to register rfkill device\n"); |
821 | toshiba_acpi_exit(); | 780 | rfkill_destroy(toshiba_acpi.bt_rfk); |
822 | return -ENOMEM; | ||
823 | } | ||
824 | |||
825 | /* Register input device for kill switch */ | ||
826 | toshiba_acpi.poll_dev = input_allocate_polled_device(); | ||
827 | if (!toshiba_acpi.poll_dev) { | ||
828 | printk(MY_ERR | ||
829 | "unable to allocate kill-switch input device\n"); | ||
830 | toshiba_acpi_exit(); | ||
831 | return -ENOMEM; | ||
832 | } | ||
833 | toshiba_acpi.poll_dev->private = &toshiba_acpi; | ||
834 | toshiba_acpi.poll_dev->poll = bt_poll_rfkill; | ||
835 | toshiba_acpi.poll_dev->poll_interval = 1000; /* msecs */ | ||
836 | |||
837 | toshiba_acpi.poll_dev->input->name = toshiba_acpi.rfk_name; | ||
838 | toshiba_acpi.poll_dev->input->id.bustype = BUS_HOST; | ||
839 | /* Toshiba USB ID */ | ||
840 | toshiba_acpi.poll_dev->input->id.vendor = 0x0930; | ||
841 | set_bit(EV_SW, toshiba_acpi.poll_dev->input->evbit); | ||
842 | set_bit(SW_RFKILL_ALL, toshiba_acpi.poll_dev->input->swbit); | ||
843 | input_report_switch(toshiba_acpi.poll_dev->input, | ||
844 | SW_RFKILL_ALL, TRUE); | ||
845 | input_sync(toshiba_acpi.poll_dev->input); | ||
846 | |||
847 | ret = input_register_polled_device(toshiba_acpi.poll_dev); | ||
848 | if (ret) { | ||
849 | printk(MY_ERR | ||
850 | "unable to register kill-switch input device\n"); | ||
851 | toshiba_acpi_exit(); | 781 | toshiba_acpi_exit(); |
852 | return ret; | 782 | return ret; |
853 | } | 783 | } |