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 | 1287 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 160 |
11 files changed, 1837 insertions, 973 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 912be65b6261..a463fd72c495 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -22,7 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.23" | 24 | #define TPACPI_VERSION "0.23" |
25 | #define TPACPI_SYSFS_VERSION 0x020300 | 25 | #define TPACPI_SYSFS_VERSION 0x020400 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -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 |
@@ -264,6 +257,8 @@ static struct { | |||
264 | u32 wan:1; | 257 | u32 wan:1; |
265 | u32 uwb:1; | 258 | u32 uwb:1; |
266 | u32 fan_ctrl_status_undef:1; | 259 | u32 fan_ctrl_status_undef:1; |
260 | u32 second_fan:1; | ||
261 | u32 beep_needs_two_args:1; | ||
267 | u32 input_device_registered:1; | 262 | u32 input_device_registered:1; |
268 | u32 platform_drv_registered:1; | 263 | u32 platform_drv_registered:1; |
269 | u32 platform_drv_attrs_registered:1; | 264 | u32 platform_drv_attrs_registered:1; |
@@ -284,8 +279,10 @@ struct thinkpad_id_data { | |||
284 | char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ | 279 | char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ |
285 | char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ | 280 | char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ |
286 | 281 | ||
287 | u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ | 282 | u16 bios_model; /* 1Y = 0x5931, 0 = unknown */ |
288 | u16 ec_model; | 283 | u16 ec_model; |
284 | u16 bios_release; /* 1ZETK1WW = 0x314b, 0 = unknown */ | ||
285 | u16 ec_release; | ||
289 | 286 | ||
290 | char *model_str; /* ThinkPad T43 */ | 287 | char *model_str; /* ThinkPad T43 */ |
291 | char *nummodel_str; /* 9384A9C for a 9384-A9C model */ | 288 | char *nummodel_str; /* 9384A9C for a 9384-A9C model */ |
@@ -362,6 +359,73 @@ static void tpacpi_log_usertask(const char * const what) | |||
362 | } \ | 359 | } \ |
363 | } while (0) | 360 | } while (0) |
364 | 361 | ||
362 | /* | ||
363 | * Quirk handling helpers | ||
364 | * | ||
365 | * ThinkPad IDs and versions seen in the field so far | ||
366 | * are two-characters from the set [0-9A-Z], i.e. base 36. | ||
367 | * | ||
368 | * We use values well outside that range as specials. | ||
369 | */ | ||
370 | |||
371 | #define TPACPI_MATCH_ANY 0xffffU | ||
372 | #define TPACPI_MATCH_UNKNOWN 0U | ||
373 | |||
374 | /* TPID('1', 'Y') == 0x5931 */ | ||
375 | #define TPID(__c1, __c2) (((__c2) << 8) | (__c1)) | ||
376 | |||
377 | #define TPACPI_Q_IBM(__id1, __id2, __quirk) \ | ||
378 | { .vendor = PCI_VENDOR_ID_IBM, \ | ||
379 | .bios = TPID(__id1, __id2), \ | ||
380 | .ec = TPACPI_MATCH_ANY, \ | ||
381 | .quirks = (__quirk) } | ||
382 | |||
383 | #define TPACPI_Q_LNV(__id1, __id2, __quirk) \ | ||
384 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
385 | .bios = TPID(__id1, __id2), \ | ||
386 | .ec = TPACPI_MATCH_ANY, \ | ||
387 | .quirks = (__quirk) } | ||
388 | |||
389 | struct tpacpi_quirk { | ||
390 | unsigned int vendor; | ||
391 | u16 bios; | ||
392 | u16 ec; | ||
393 | unsigned long quirks; | ||
394 | }; | ||
395 | |||
396 | /** | ||
397 | * tpacpi_check_quirks() - search BIOS/EC version on a list | ||
398 | * @qlist: array of &struct tpacpi_quirk | ||
399 | * @qlist_size: number of elements in @qlist | ||
400 | * | ||
401 | * Iterates over a quirks list until one is found that matches the | ||
402 | * ThinkPad's vendor, BIOS and EC model. | ||
403 | * | ||
404 | * Returns 0 if nothing matches, otherwise returns the quirks field of | ||
405 | * the matching &struct tpacpi_quirk entry. | ||
406 | * | ||
407 | * The match criteria is: vendor, ec and bios much match. | ||
408 | */ | ||
409 | static unsigned long __init tpacpi_check_quirks( | ||
410 | const struct tpacpi_quirk *qlist, | ||
411 | unsigned int qlist_size) | ||
412 | { | ||
413 | while (qlist_size) { | ||
414 | if ((qlist->vendor == thinkpad_id.vendor || | ||
415 | qlist->vendor == TPACPI_MATCH_ANY) && | ||
416 | (qlist->bios == thinkpad_id.bios_model || | ||
417 | qlist->bios == TPACPI_MATCH_ANY) && | ||
418 | (qlist->ec == thinkpad_id.ec_model || | ||
419 | qlist->ec == TPACPI_MATCH_ANY)) | ||
420 | return qlist->quirks; | ||
421 | |||
422 | qlist_size--; | ||
423 | qlist++; | ||
424 | } | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | |||
365 | /**************************************************************************** | 429 | /**************************************************************************** |
366 | **************************************************************************** | 430 | **************************************************************************** |
367 | * | 431 | * |
@@ -1005,67 +1069,234 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
1005 | return 0; | 1069 | return 0; |
1006 | } | 1070 | } |
1007 | 1071 | ||
1008 | static int __init tpacpi_new_rfkill(const unsigned int id, | 1072 | static void printk_deprecated_attribute(const char * const what, |
1009 | 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, | ||
1010 | const enum rfkill_type rfktype, | 1231 | const enum rfkill_type rfktype, |
1011 | const char *name, | 1232 | const char *name, |
1012 | const bool set_default, | 1233 | const bool set_default) |
1013 | int (*toggle_radio)(void *, enum rfkill_state), | ||
1014 | int (*get_state)(void *, enum rfkill_state *)) | ||
1015 | { | 1234 | { |
1235 | struct tpacpi_rfk *atp_rfk; | ||
1016 | int res; | 1236 | int res; |
1017 | enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; | 1237 | bool sw_state = false; |
1018 | 1238 | int sw_status; | |
1019 | res = get_state(NULL, &initial_state); | 1239 | |
1020 | if (res < 0) { | 1240 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); |
1021 | printk(TPACPI_ERR | 1241 | |
1022 | "failed to read initial state for %s, error %d; " | 1242 | atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL); |
1023 | "will turn radio off\n", name, res); | 1243 | if (atp_rfk) |
1024 | } else if (set_default) { | 1244 | atp_rfk->rfkill = rfkill_alloc(name, |
1025 | /* try to set the initial state as the default for the rfkill | 1245 | &tpacpi_pdev->dev, |
1026 | * type, since we ask the firmware to preserve it across S5 in | 1246 | rfktype, |
1027 | * NVRAM */ | 1247 | &tpacpi_rfk_rfkill_ops, |
1028 | if (rfkill_set_default(rfktype, | 1248 | atp_rfk); |
1029 | (initial_state == RFKILL_STATE_UNBLOCKED) ? | 1249 | if (!atp_rfk || !atp_rfk->rfkill) { |
1030 | RFKILL_STATE_UNBLOCKED : | ||
1031 | RFKILL_STATE_SOFT_BLOCKED) == -EPERM) | ||
1032 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
1033 | "Default state for %s cannot be changed\n", | ||
1034 | name); | ||
1035 | } | ||
1036 | |||
1037 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | ||
1038 | if (!*rfk) { | ||
1039 | printk(TPACPI_ERR | 1250 | printk(TPACPI_ERR |
1040 | "failed to allocate memory for rfkill class\n"); | 1251 | "failed to allocate memory for rfkill class\n"); |
1252 | kfree(atp_rfk); | ||
1041 | return -ENOMEM; | 1253 | return -ENOMEM; |
1042 | } | 1254 | } |
1043 | 1255 | ||
1044 | (*rfk)->name = name; | 1256 | atp_rfk->id = id; |
1045 | (*rfk)->get_state = get_state; | 1257 | atp_rfk->ops = tp_rfkops; |
1046 | (*rfk)->toggle_radio = toggle_radio; | 1258 | |
1047 | (*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()); | ||
1048 | 1273 | ||
1049 | res = rfkill_register(*rfk); | 1274 | res = rfkill_register(atp_rfk->rfkill); |
1050 | if (res < 0) { | 1275 | if (res < 0) { |
1051 | printk(TPACPI_ERR | 1276 | printk(TPACPI_ERR |
1052 | "failed to register %s rfkill switch: %d\n", | 1277 | "failed to register %s rfkill switch: %d\n", |
1053 | name, res); | 1278 | name, res); |
1054 | rfkill_free(*rfk); | 1279 | rfkill_destroy(atp_rfk->rfkill); |
1055 | *rfk = NULL; | 1280 | kfree(atp_rfk); |
1056 | return res; | 1281 | return res; |
1057 | } | 1282 | } |
1058 | 1283 | ||
1284 | tpacpi_rfkill_switches[id] = atp_rfk; | ||
1059 | return 0; | 1285 | return 0; |
1060 | } | 1286 | } |
1061 | 1287 | ||
1062 | static void printk_deprecated_attribute(const char * const what, | 1288 | static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id) |
1063 | const char * const details) | ||
1064 | { | 1289 | { |
1065 | tpacpi_log_usertask("deprecated sysfs attribute"); | 1290 | struct tpacpi_rfk *tp_rfk; |
1066 | printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " | 1291 | |
1067 | "will be removed. %s\n", | 1292 | BUG_ON(id >= TPACPI_RFK_SW_MAX); |
1068 | 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 | } | ||
1069 | } | 1300 | } |
1070 | 1301 | ||
1071 | static void printk_deprecated_rfkill_attribute(const char * const what) | 1302 | static void printk_deprecated_rfkill_attribute(const char * const what) |
@@ -1074,6 +1305,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what) | |||
1074 | "Please switch to generic rfkill before year 2010"); | 1305 | "Please switch to generic rfkill before year 2010"); |
1075 | } | 1306 | } |
1076 | 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 | |||
1077 | /************************************************************************* | 1414 | /************************************************************************* |
1078 | * thinkpad-acpi driver attributes | 1415 | * thinkpad-acpi driver attributes |
1079 | */ | 1416 | */ |
@@ -1127,8 +1464,6 @@ static DRIVER_ATTR(version, S_IRUGO, | |||
1127 | 1464 | ||
1128 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 1465 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
1129 | 1466 | ||
1130 | static void tpacpi_send_radiosw_update(void); | ||
1131 | |||
1132 | /* wlsw_emulstate ------------------------------------------------------ */ | 1467 | /* wlsw_emulstate ------------------------------------------------------ */ |
1133 | 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, |
1134 | char *buf) | 1469 | char *buf) |
@@ -1144,11 +1479,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, | |||
1144 | if (parse_strtoul(buf, 1, &t)) | 1479 | if (parse_strtoul(buf, 1, &t)) |
1145 | return -EINVAL; | 1480 | return -EINVAL; |
1146 | 1481 | ||
1147 | if (tpacpi_wlsw_emulstate != t) { | 1482 | if (tpacpi_wlsw_emulstate != !!t) { |
1148 | tpacpi_wlsw_emulstate = !!t; | ||
1149 | tpacpi_send_radiosw_update(); | ||
1150 | } else | ||
1151 | tpacpi_wlsw_emulstate = !!t; | 1483 | tpacpi_wlsw_emulstate = !!t; |
1484 | tpacpi_rfk_update_hwblock_state(!t); /* negative logic */ | ||
1485 | } | ||
1152 | 1486 | ||
1153 | return count; | 1487 | return count; |
1154 | } | 1488 | } |
@@ -1463,17 +1797,23 @@ static struct attribute_set *hotkey_dev_attributes; | |||
1463 | /* HKEY.MHKG() return bits */ | 1797 | /* HKEY.MHKG() return bits */ |
1464 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | 1798 | #define TP_HOTKEY_TABLET_MASK (1 << 3) |
1465 | 1799 | ||
1466 | static int hotkey_get_wlsw(int *status) | 1800 | static int hotkey_get_wlsw(void) |
1467 | { | 1801 | { |
1802 | int status; | ||
1803 | |||
1804 | if (!tp_features.hotkey_wlsw) | ||
1805 | return -ENODEV; | ||
1806 | |||
1468 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 1807 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
1469 | if (dbg_wlswemul) { | 1808 | if (dbg_wlswemul) |
1470 | *status = !!tpacpi_wlsw_emulstate; | 1809 | return (tpacpi_wlsw_emulstate) ? |
1471 | return 0; | 1810 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
1472 | } | ||
1473 | #endif | 1811 | #endif |
1474 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 1812 | |
1813 | if (!acpi_evalf(hkey_handle, &status, "WLSW", "d")) | ||
1475 | return -EIO; | 1814 | return -EIO; |
1476 | return 0; | 1815 | |
1816 | return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; | ||
1477 | } | 1817 | } |
1478 | 1818 | ||
1479 | static int hotkey_get_tablet_mode(int *status) | 1819 | static int hotkey_get_tablet_mode(int *status) |
@@ -2107,12 +2447,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, | |||
2107 | struct device_attribute *attr, | 2447 | struct device_attribute *attr, |
2108 | char *buf) | 2448 | char *buf) |
2109 | { | 2449 | { |
2110 | int res, s; | 2450 | int res; |
2111 | res = hotkey_get_wlsw(&s); | 2451 | res = hotkey_get_wlsw(); |
2112 | if (res < 0) | 2452 | if (res < 0) |
2113 | return res; | 2453 | return res; |
2114 | 2454 | ||
2115 | 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); | ||
2116 | } | 2460 | } |
2117 | 2461 | ||
2118 | static struct device_attribute dev_attr_hotkey_radio_sw = | 2462 | static struct device_attribute dev_attr_hotkey_radio_sw = |
@@ -2223,30 +2567,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
2223 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | 2567 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, |
2224 | }; | 2568 | }; |
2225 | 2569 | ||
2226 | static void bluetooth_update_rfk(void); | 2570 | /* |
2227 | static void wan_update_rfk(void); | 2571 | * Sync both the hw and sw blocking state of all switches |
2228 | static void uwb_update_rfk(void); | 2572 | */ |
2229 | static void tpacpi_send_radiosw_update(void) | 2573 | static void tpacpi_send_radiosw_update(void) |
2230 | { | 2574 | { |
2231 | int wlsw; | 2575 | int wlsw; |
2232 | 2576 | ||
2233 | /* Sync these BEFORE sending any rfkill events */ | 2577 | /* |
2234 | if (tp_features.bluetooth) | 2578 | * We must sync all rfkill controllers *before* issuing any |
2235 | bluetooth_update_rfk(); | 2579 | * rfkill input events, or we will race the rfkill core input |
2236 | if (tp_features.wan) | 2580 | * handler. |
2237 | wan_update_rfk(); | 2581 | * |
2238 | if (tp_features.uwb) | 2582 | * tpacpi_inputdev_send_mutex works as a syncronization point |
2239 | 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); | ||
2593 | |||
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); | ||
2240 | 2600 | ||
2241 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | 2601 | /* Issue rfkill input event for WLSW switch */ |
2602 | if (!(wlsw < 0)) { | ||
2242 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2603 | mutex_lock(&tpacpi_inputdev_send_mutex); |
2243 | 2604 | ||
2244 | input_report_switch(tpacpi_inputdev, | 2605 | input_report_switch(tpacpi_inputdev, |
2245 | SW_RFKILL_ALL, !!wlsw); | 2606 | SW_RFKILL_ALL, (wlsw > 0)); |
2246 | input_sync(tpacpi_inputdev); | 2607 | input_sync(tpacpi_inputdev); |
2247 | 2608 | ||
2248 | mutex_unlock(&tpacpi_inputdev_send_mutex); | 2609 | mutex_unlock(&tpacpi_inputdev_send_mutex); |
2249 | } | 2610 | } |
2611 | |||
2612 | /* | ||
2613 | * this can be unconditional, as we will poll state again | ||
2614 | * if userspace uses the notify to read data | ||
2615 | */ | ||
2250 | hotkey_radio_sw_notify_change(); | 2616 | hotkey_radio_sw_notify_change(); |
2251 | } | 2617 | } |
2252 | 2618 | ||
@@ -2585,7 +2951,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2585 | /* update bright_acpimode... */ | 2951 | /* update bright_acpimode... */ |
2586 | tpacpi_check_std_acpi_brightness_support(); | 2952 | tpacpi_check_std_acpi_brightness_support(); |
2587 | 2953 | ||
2588 | if (tp_features.bright_acpimode) { | 2954 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { |
2589 | printk(TPACPI_INFO | 2955 | printk(TPACPI_INFO |
2590 | "This ThinkPad has standard ACPI backlight " | 2956 | "This ThinkPad has standard ACPI backlight " |
2591 | "brightness control, supported by the ACPI " | 2957 | "brightness control, supported by the ACPI " |
@@ -3056,8 +3422,6 @@ enum { | |||
3056 | 3422 | ||
3057 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" | 3423 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" |
3058 | 3424 | ||
3059 | static struct rfkill *tpacpi_bluetooth_rfkill; | ||
3060 | |||
3061 | static void bluetooth_suspend(pm_message_t state) | 3425 | static void bluetooth_suspend(pm_message_t state) |
3062 | { | 3426 | { |
3063 | /* Try to make sure radio will resume powered off */ | 3427 | /* Try to make sure radio will resume powered off */ |
@@ -3067,83 +3431,47 @@ static void bluetooth_suspend(pm_message_t state) | |||
3067 | "bluetooth power down on resume request failed\n"); | 3431 | "bluetooth power down on resume request failed\n"); |
3068 | } | 3432 | } |
3069 | 3433 | ||
3070 | static int bluetooth_get_radiosw(void) | 3434 | static int bluetooth_get_status(void) |
3071 | { | 3435 | { |
3072 | int status; | 3436 | int status; |
3073 | 3437 | ||
3074 | if (!tp_features.bluetooth) | ||
3075 | return -ENODEV; | ||
3076 | |||
3077 | /* WLSW overrides bluetooth in firmware/hardware, reflect that */ | ||
3078 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3079 | return RFKILL_STATE_HARD_BLOCKED; | ||
3080 | |||
3081 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3438 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3082 | if (dbg_bluetoothemul) | 3439 | if (dbg_bluetoothemul) |
3083 | return (tpacpi_bluetooth_emulstate) ? | 3440 | return (tpacpi_bluetooth_emulstate) ? |
3084 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3441 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3085 | #endif | 3442 | #endif |
3086 | 3443 | ||
3087 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3444 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) |
3088 | return -EIO; | 3445 | return -EIO; |
3089 | 3446 | ||
3090 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? | 3447 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? |
3091 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3448 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3092 | } | 3449 | } |
3093 | 3450 | ||
3094 | static void bluetooth_update_rfk(void) | 3451 | static int bluetooth_set_status(enum tpacpi_rfkill_state state) |
3095 | { | 3452 | { |
3096 | int status; | 3453 | int status; |
3097 | 3454 | ||
3098 | if (!tpacpi_bluetooth_rfkill) | ||
3099 | return; | ||
3100 | |||
3101 | status = bluetooth_get_radiosw(); | ||
3102 | if (status < 0) | ||
3103 | return; | ||
3104 | rfkill_force_state(tpacpi_bluetooth_rfkill, status); | ||
3105 | |||
3106 | vdbg_printk(TPACPI_DBG_RFKILL, | 3455 | vdbg_printk(TPACPI_DBG_RFKILL, |
3107 | "forced rfkill state to %d\n", | 3456 | "will attempt to %s bluetooth\n", |
3108 | status); | 3457 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); |
3109 | } | ||
3110 | |||
3111 | static int bluetooth_set_radiosw(int radio_on, int update_rfk) | ||
3112 | { | ||
3113 | int status; | ||
3114 | |||
3115 | if (!tp_features.bluetooth) | ||
3116 | return -ENODEV; | ||
3117 | |||
3118 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
3119 | * reason to risk weird behaviour. */ | ||
3120 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3121 | && radio_on) | ||
3122 | return -EPERM; | ||
3123 | |||
3124 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3125 | "will %s bluetooth\n", radio_on ? "enable" : "disable"); | ||
3126 | 3458 | ||
3127 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3459 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3128 | if (dbg_bluetoothemul) { | 3460 | if (dbg_bluetoothemul) { |
3129 | tpacpi_bluetooth_emulstate = !!radio_on; | 3461 | tpacpi_bluetooth_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3130 | if (update_rfk) | ||
3131 | bluetooth_update_rfk(); | ||
3132 | return 0; | 3462 | return 0; |
3133 | } | 3463 | } |
3134 | #endif | 3464 | #endif |
3135 | 3465 | ||
3136 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | 3466 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ |
3137 | if (radio_on) | 3467 | if (state == TPACPI_RFK_RADIO_ON) |
3138 | status = TP_ACPI_BLUETOOTH_RADIOSSW; | 3468 | status = TP_ACPI_BLUETOOTH_RADIOSSW; |
3139 | else | 3469 | else |
3140 | status = 0; | 3470 | status = 0; |
3471 | |||
3141 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 3472 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
3142 | return -EIO; | 3473 | return -EIO; |
3143 | 3474 | ||
3144 | if (update_rfk) | ||
3145 | bluetooth_update_rfk(); | ||
3146 | |||
3147 | return 0; | 3475 | return 0; |
3148 | } | 3476 | } |
3149 | 3477 | ||
@@ -3152,35 +3480,16 @@ static ssize_t bluetooth_enable_show(struct device *dev, | |||
3152 | struct device_attribute *attr, | 3480 | struct device_attribute *attr, |
3153 | char *buf) | 3481 | char *buf) |
3154 | { | 3482 | { |
3155 | int status; | 3483 | return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_BLUETOOTH_SW_ID, |
3156 | 3484 | attr, buf); | |
3157 | printk_deprecated_rfkill_attribute("bluetooth_enable"); | ||
3158 | |||
3159 | status = bluetooth_get_radiosw(); | ||
3160 | if (status < 0) | ||
3161 | return status; | ||
3162 | |||
3163 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
3164 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
3165 | } | 3485 | } |
3166 | 3486 | ||
3167 | static ssize_t bluetooth_enable_store(struct device *dev, | 3487 | static ssize_t bluetooth_enable_store(struct device *dev, |
3168 | struct device_attribute *attr, | 3488 | struct device_attribute *attr, |
3169 | const char *buf, size_t count) | 3489 | const char *buf, size_t count) |
3170 | { | 3490 | { |
3171 | unsigned long t; | 3491 | return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_BLUETOOTH_SW_ID, |
3172 | int res; | 3492 | attr, buf, count); |
3173 | |||
3174 | printk_deprecated_rfkill_attribute("bluetooth_enable"); | ||
3175 | |||
3176 | if (parse_strtoul(buf, 1, &t)) | ||
3177 | return -EINVAL; | ||
3178 | |||
3179 | tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t); | ||
3180 | |||
3181 | res = bluetooth_set_radiosw(t, 1); | ||
3182 | |||
3183 | return (res) ? res : count; | ||
3184 | } | 3493 | } |
3185 | 3494 | ||
3186 | static struct device_attribute dev_attr_bluetooth_enable = | 3495 | static struct device_attribute dev_attr_bluetooth_enable = |
@@ -3198,23 +3507,10 @@ static const struct attribute_group bluetooth_attr_group = { | |||
3198 | .attrs = bluetooth_attributes, | 3507 | .attrs = bluetooth_attributes, |
3199 | }; | 3508 | }; |
3200 | 3509 | ||
3201 | static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) | 3510 | static const struct tpacpi_rfk_ops bluetooth_tprfk_ops = { |
3202 | { | 3511 | .get_status = bluetooth_get_status, |
3203 | int bts = bluetooth_get_radiosw(); | 3512 | .set_status = bluetooth_set_status, |
3204 | 3513 | }; | |
3205 | if (bts < 0) | ||
3206 | return bts; | ||
3207 | |||
3208 | *state = bts; | ||
3209 | return 0; | ||
3210 | } | ||
3211 | |||
3212 | static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | ||
3213 | { | ||
3214 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3215 | "request to change radio state to %d\n", state); | ||
3216 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3217 | } | ||
3218 | 3514 | ||
3219 | static void bluetooth_shutdown(void) | 3515 | static void bluetooth_shutdown(void) |
3220 | { | 3516 | { |
@@ -3230,13 +3526,12 @@ static void bluetooth_shutdown(void) | |||
3230 | 3526 | ||
3231 | static void bluetooth_exit(void) | 3527 | static void bluetooth_exit(void) |
3232 | { | 3528 | { |
3233 | bluetooth_shutdown(); | ||
3234 | |||
3235 | if (tpacpi_bluetooth_rfkill) | ||
3236 | rfkill_unregister(tpacpi_bluetooth_rfkill); | ||
3237 | |||
3238 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | 3529 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, |
3239 | &bluetooth_attr_group); | 3530 | &bluetooth_attr_group); |
3531 | |||
3532 | tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); | ||
3533 | |||
3534 | bluetooth_shutdown(); | ||
3240 | } | 3535 | } |
3241 | 3536 | ||
3242 | static int __init bluetooth_init(struct ibm_init_struct *iibm) | 3537 | static int __init bluetooth_init(struct ibm_init_struct *iibm) |
@@ -3277,20 +3572,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3277 | if (!tp_features.bluetooth) | 3572 | if (!tp_features.bluetooth) |
3278 | return 1; | 3573 | return 1; |
3279 | 3574 | ||
3280 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3281 | &bluetooth_attr_group); | ||
3282 | if (res) | ||
3283 | return res; | ||
3284 | |||
3285 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, | 3575 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, |
3286 | &tpacpi_bluetooth_rfkill, | 3576 | &bluetooth_tprfk_ops, |
3287 | RFKILL_TYPE_BLUETOOTH, | 3577 | RFKILL_TYPE_BLUETOOTH, |
3288 | TPACPI_RFK_BLUETOOTH_SW_NAME, | 3578 | TPACPI_RFK_BLUETOOTH_SW_NAME, |
3289 | true, | 3579 | true); |
3290 | tpacpi_bluetooth_rfk_set, | 3580 | if (res) |
3291 | tpacpi_bluetooth_rfk_get); | 3581 | return res; |
3582 | |||
3583 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3584 | &bluetooth_attr_group); | ||
3292 | if (res) { | 3585 | if (res) { |
3293 | bluetooth_exit(); | 3586 | tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); |
3294 | return res; | 3587 | return res; |
3295 | } | 3588 | } |
3296 | 3589 | ||
@@ -3300,46 +3593,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3300 | /* procfs -------------------------------------------------------------- */ | 3593 | /* procfs -------------------------------------------------------------- */ |
3301 | static int bluetooth_read(char *p) | 3594 | static int bluetooth_read(char *p) |
3302 | { | 3595 | { |
3303 | int len = 0; | 3596 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); |
3304 | int status = bluetooth_get_radiosw(); | ||
3305 | |||
3306 | if (!tp_features.bluetooth) | ||
3307 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
3308 | else { | ||
3309 | len += sprintf(p + len, "status:\t\t%s\n", | ||
3310 | (status == RFKILL_STATE_UNBLOCKED) ? | ||
3311 | "enabled" : "disabled"); | ||
3312 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
3313 | } | ||
3314 | |||
3315 | return len; | ||
3316 | } | 3597 | } |
3317 | 3598 | ||
3318 | static int bluetooth_write(char *buf) | 3599 | static int bluetooth_write(char *buf) |
3319 | { | 3600 | { |
3320 | char *cmd; | 3601 | return tpacpi_rfk_procfs_write(TPACPI_RFK_BLUETOOTH_SW_ID, buf); |
3321 | int state = -1; | ||
3322 | |||
3323 | if (!tp_features.bluetooth) | ||
3324 | return -ENODEV; | ||
3325 | |||
3326 | while ((cmd = next_cmd(&buf))) { | ||
3327 | if (strlencmp(cmd, "enable") == 0) { | ||
3328 | state = 1; | ||
3329 | } else if (strlencmp(cmd, "disable") == 0) { | ||
3330 | state = 0; | ||
3331 | } else | ||
3332 | return -EINVAL; | ||
3333 | } | ||
3334 | |||
3335 | if (state != -1) { | ||
3336 | tpacpi_disclose_usertask("procfs bluetooth", | ||
3337 | "attempt to %s\n", | ||
3338 | state ? "enable" : "disable"); | ||
3339 | bluetooth_set_radiosw(state, 1); | ||
3340 | } | ||
3341 | |||
3342 | return 0; | ||
3343 | } | 3602 | } |
3344 | 3603 | ||
3345 | static struct ibm_struct bluetooth_driver_data = { | 3604 | static struct ibm_struct bluetooth_driver_data = { |
@@ -3365,8 +3624,6 @@ enum { | |||
3365 | 3624 | ||
3366 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" | 3625 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" |
3367 | 3626 | ||
3368 | static struct rfkill *tpacpi_wan_rfkill; | ||
3369 | |||
3370 | static void wan_suspend(pm_message_t state) | 3627 | static void wan_suspend(pm_message_t state) |
3371 | { | 3628 | { |
3372 | /* Try to make sure radio will resume powered off */ | 3629 | /* Try to make sure radio will resume powered off */ |
@@ -3376,83 +3633,47 @@ static void wan_suspend(pm_message_t state) | |||
3376 | "WWAN power down on resume request failed\n"); | 3633 | "WWAN power down on resume request failed\n"); |
3377 | } | 3634 | } |
3378 | 3635 | ||
3379 | static int wan_get_radiosw(void) | 3636 | static int wan_get_status(void) |
3380 | { | 3637 | { |
3381 | int status; | 3638 | int status; |
3382 | 3639 | ||
3383 | if (!tp_features.wan) | ||
3384 | return -ENODEV; | ||
3385 | |||
3386 | /* WLSW overrides WWAN in firmware/hardware, reflect that */ | ||
3387 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3388 | return RFKILL_STATE_HARD_BLOCKED; | ||
3389 | |||
3390 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3640 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3391 | if (dbg_wwanemul) | 3641 | if (dbg_wwanemul) |
3392 | return (tpacpi_wwan_emulstate) ? | 3642 | return (tpacpi_wwan_emulstate) ? |
3393 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3643 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3394 | #endif | 3644 | #endif |
3395 | 3645 | ||
3396 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3646 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) |
3397 | return -EIO; | 3647 | return -EIO; |
3398 | 3648 | ||
3399 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? | 3649 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? |
3400 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3650 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3401 | } | ||
3402 | |||
3403 | static void wan_update_rfk(void) | ||
3404 | { | ||
3405 | int status; | ||
3406 | |||
3407 | if (!tpacpi_wan_rfkill) | ||
3408 | return; | ||
3409 | |||
3410 | status = wan_get_radiosw(); | ||
3411 | if (status < 0) | ||
3412 | return; | ||
3413 | rfkill_force_state(tpacpi_wan_rfkill, status); | ||
3414 | |||
3415 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3416 | "forced rfkill state to %d\n", | ||
3417 | status); | ||
3418 | } | 3651 | } |
3419 | 3652 | ||
3420 | static int wan_set_radiosw(int radio_on, int update_rfk) | 3653 | static int wan_set_status(enum tpacpi_rfkill_state state) |
3421 | { | 3654 | { |
3422 | int status; | 3655 | int status; |
3423 | 3656 | ||
3424 | if (!tp_features.wan) | ||
3425 | return -ENODEV; | ||
3426 | |||
3427 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
3428 | * reason to risk weird behaviour. */ | ||
3429 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3430 | && radio_on) | ||
3431 | return -EPERM; | ||
3432 | |||
3433 | vdbg_printk(TPACPI_DBG_RFKILL, | 3657 | vdbg_printk(TPACPI_DBG_RFKILL, |
3434 | "will %s WWAN\n", radio_on ? "enable" : "disable"); | 3658 | "will attempt to %s wwan\n", |
3659 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); | ||
3435 | 3660 | ||
3436 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3661 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3437 | if (dbg_wwanemul) { | 3662 | if (dbg_wwanemul) { |
3438 | tpacpi_wwan_emulstate = !!radio_on; | 3663 | tpacpi_wwan_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3439 | if (update_rfk) | ||
3440 | wan_update_rfk(); | ||
3441 | return 0; | 3664 | return 0; |
3442 | } | 3665 | } |
3443 | #endif | 3666 | #endif |
3444 | 3667 | ||
3445 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | 3668 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ |
3446 | if (radio_on) | 3669 | if (state == TPACPI_RFK_RADIO_ON) |
3447 | status = TP_ACPI_WANCARD_RADIOSSW; | 3670 | status = TP_ACPI_WANCARD_RADIOSSW; |
3448 | else | 3671 | else |
3449 | status = 0; | 3672 | status = 0; |
3673 | |||
3450 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3674 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) |
3451 | return -EIO; | 3675 | return -EIO; |
3452 | 3676 | ||
3453 | if (update_rfk) | ||
3454 | wan_update_rfk(); | ||
3455 | |||
3456 | return 0; | 3677 | return 0; |
3457 | } | 3678 | } |
3458 | 3679 | ||
@@ -3461,35 +3682,16 @@ static ssize_t wan_enable_show(struct device *dev, | |||
3461 | struct device_attribute *attr, | 3682 | struct device_attribute *attr, |
3462 | char *buf) | 3683 | char *buf) |
3463 | { | 3684 | { |
3464 | int status; | 3685 | return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_WWAN_SW_ID, |
3465 | 3686 | attr, buf); | |
3466 | printk_deprecated_rfkill_attribute("wwan_enable"); | ||
3467 | |||
3468 | status = wan_get_radiosw(); | ||
3469 | if (status < 0) | ||
3470 | return status; | ||
3471 | |||
3472 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
3473 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
3474 | } | 3687 | } |
3475 | 3688 | ||
3476 | static ssize_t wan_enable_store(struct device *dev, | 3689 | static ssize_t wan_enable_store(struct device *dev, |
3477 | struct device_attribute *attr, | 3690 | struct device_attribute *attr, |
3478 | const char *buf, size_t count) | 3691 | const char *buf, size_t count) |
3479 | { | 3692 | { |
3480 | unsigned long t; | 3693 | return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_WWAN_SW_ID, |
3481 | int res; | 3694 | attr, buf, count); |
3482 | |||
3483 | printk_deprecated_rfkill_attribute("wwan_enable"); | ||
3484 | |||
3485 | if (parse_strtoul(buf, 1, &t)) | ||
3486 | return -EINVAL; | ||
3487 | |||
3488 | tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t); | ||
3489 | |||
3490 | res = wan_set_radiosw(t, 1); | ||
3491 | |||
3492 | return (res) ? res : count; | ||
3493 | } | 3695 | } |
3494 | 3696 | ||
3495 | static struct device_attribute dev_attr_wan_enable = | 3697 | static struct device_attribute dev_attr_wan_enable = |
@@ -3507,23 +3709,10 @@ static const struct attribute_group wan_attr_group = { | |||
3507 | .attrs = wan_attributes, | 3709 | .attrs = wan_attributes, |
3508 | }; | 3710 | }; |
3509 | 3711 | ||
3510 | static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) | 3712 | static const struct tpacpi_rfk_ops wan_tprfk_ops = { |
3511 | { | 3713 | .get_status = wan_get_status, |
3512 | int wans = wan_get_radiosw(); | 3714 | .set_status = wan_set_status, |
3513 | 3715 | }; | |
3514 | if (wans < 0) | ||
3515 | return wans; | ||
3516 | |||
3517 | *state = wans; | ||
3518 | return 0; | ||
3519 | } | ||
3520 | |||
3521 | static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | ||
3522 | { | ||
3523 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3524 | "request to change radio state to %d\n", state); | ||
3525 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3526 | } | ||
3527 | 3716 | ||
3528 | static void wan_shutdown(void) | 3717 | static void wan_shutdown(void) |
3529 | { | 3718 | { |
@@ -3539,13 +3728,12 @@ static void wan_shutdown(void) | |||
3539 | 3728 | ||
3540 | static void wan_exit(void) | 3729 | static void wan_exit(void) |
3541 | { | 3730 | { |
3542 | wan_shutdown(); | ||
3543 | |||
3544 | if (tpacpi_wan_rfkill) | ||
3545 | rfkill_unregister(tpacpi_wan_rfkill); | ||
3546 | |||
3547 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | 3731 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, |
3548 | &wan_attr_group); | 3732 | &wan_attr_group); |
3733 | |||
3734 | tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); | ||
3735 | |||
3736 | wan_shutdown(); | ||
3549 | } | 3737 | } |
3550 | 3738 | ||
3551 | static int __init wan_init(struct ibm_init_struct *iibm) | 3739 | static int __init wan_init(struct ibm_init_struct *iibm) |
@@ -3584,20 +3772,19 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3584 | if (!tp_features.wan) | 3772 | if (!tp_features.wan) |
3585 | return 1; | 3773 | return 1; |
3586 | 3774 | ||
3587 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3588 | &wan_attr_group); | ||
3589 | if (res) | ||
3590 | return res; | ||
3591 | |||
3592 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, | 3775 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, |
3593 | &tpacpi_wan_rfkill, | 3776 | &wan_tprfk_ops, |
3594 | RFKILL_TYPE_WWAN, | 3777 | RFKILL_TYPE_WWAN, |
3595 | TPACPI_RFK_WWAN_SW_NAME, | 3778 | TPACPI_RFK_WWAN_SW_NAME, |
3596 | true, | 3779 | true); |
3597 | tpacpi_wan_rfk_set, | 3780 | if (res) |
3598 | tpacpi_wan_rfk_get); | 3781 | return res; |
3782 | |||
3783 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3784 | &wan_attr_group); | ||
3785 | |||
3599 | if (res) { | 3786 | if (res) { |
3600 | wan_exit(); | 3787 | tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); |
3601 | return res; | 3788 | return res; |
3602 | } | 3789 | } |
3603 | 3790 | ||
@@ -3607,48 +3794,12 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3607 | /* procfs -------------------------------------------------------------- */ | 3794 | /* procfs -------------------------------------------------------------- */ |
3608 | static int wan_read(char *p) | 3795 | static int wan_read(char *p) |
3609 | { | 3796 | { |
3610 | int len = 0; | 3797 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); |
3611 | int status = wan_get_radiosw(); | ||
3612 | |||
3613 | tpacpi_disclose_usertask("procfs wan", "read"); | ||
3614 | |||
3615 | if (!tp_features.wan) | ||
3616 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
3617 | else { | ||
3618 | len += sprintf(p + len, "status:\t\t%s\n", | ||
3619 | (status == RFKILL_STATE_UNBLOCKED) ? | ||
3620 | "enabled" : "disabled"); | ||
3621 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
3622 | } | ||
3623 | |||
3624 | return len; | ||
3625 | } | 3798 | } |
3626 | 3799 | ||
3627 | static int wan_write(char *buf) | 3800 | static int wan_write(char *buf) |
3628 | { | 3801 | { |
3629 | char *cmd; | 3802 | return tpacpi_rfk_procfs_write(TPACPI_RFK_WWAN_SW_ID, buf); |
3630 | int state = -1; | ||
3631 | |||
3632 | if (!tp_features.wan) | ||
3633 | return -ENODEV; | ||
3634 | |||
3635 | while ((cmd = next_cmd(&buf))) { | ||
3636 | if (strlencmp(cmd, "enable") == 0) { | ||
3637 | state = 1; | ||
3638 | } else if (strlencmp(cmd, "disable") == 0) { | ||
3639 | state = 0; | ||
3640 | } else | ||
3641 | return -EINVAL; | ||
3642 | } | ||
3643 | |||
3644 | if (state != -1) { | ||
3645 | tpacpi_disclose_usertask("procfs wan", | ||
3646 | "attempt to %s\n", | ||
3647 | state ? "enable" : "disable"); | ||
3648 | wan_set_radiosw(state, 1); | ||
3649 | } | ||
3650 | |||
3651 | return 0; | ||
3652 | } | 3803 | } |
3653 | 3804 | ||
3654 | static struct ibm_struct wan_driver_data = { | 3805 | static struct ibm_struct wan_driver_data = { |
@@ -3672,108 +3823,59 @@ enum { | |||
3672 | 3823 | ||
3673 | #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" | 3824 | #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" |
3674 | 3825 | ||
3675 | static struct rfkill *tpacpi_uwb_rfkill; | 3826 | static int uwb_get_status(void) |
3676 | |||
3677 | static int uwb_get_radiosw(void) | ||
3678 | { | 3827 | { |
3679 | int status; | 3828 | int status; |
3680 | 3829 | ||
3681 | if (!tp_features.uwb) | ||
3682 | return -ENODEV; | ||
3683 | |||
3684 | /* WLSW overrides UWB in firmware/hardware, reflect that */ | ||
3685 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3686 | return RFKILL_STATE_HARD_BLOCKED; | ||
3687 | |||
3688 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3830 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3689 | if (dbg_uwbemul) | 3831 | if (dbg_uwbemul) |
3690 | return (tpacpi_uwb_emulstate) ? | 3832 | return (tpacpi_uwb_emulstate) ? |
3691 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3833 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3692 | #endif | 3834 | #endif |
3693 | 3835 | ||
3694 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) | 3836 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) |
3695 | return -EIO; | 3837 | return -EIO; |
3696 | 3838 | ||
3697 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? | 3839 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? |
3698 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3840 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3699 | } | 3841 | } |
3700 | 3842 | ||
3701 | static void uwb_update_rfk(void) | 3843 | static int uwb_set_status(enum tpacpi_rfkill_state state) |
3702 | { | 3844 | { |
3703 | int status; | 3845 | int status; |
3704 | 3846 | ||
3705 | if (!tpacpi_uwb_rfkill) | ||
3706 | return; | ||
3707 | |||
3708 | status = uwb_get_radiosw(); | ||
3709 | if (status < 0) | ||
3710 | return; | ||
3711 | rfkill_force_state(tpacpi_uwb_rfkill, status); | ||
3712 | |||
3713 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3714 | "forced rfkill state to %d\n", | ||
3715 | status); | ||
3716 | } | ||
3717 | |||
3718 | static int uwb_set_radiosw(int radio_on, int update_rfk) | ||
3719 | { | ||
3720 | int status; | ||
3721 | |||
3722 | if (!tp_features.uwb) | ||
3723 | return -ENODEV; | ||
3724 | |||
3725 | /* WLSW overrides UWB in firmware/hardware, but there is no | ||
3726 | * reason to risk weird behaviour. */ | ||
3727 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3728 | && radio_on) | ||
3729 | return -EPERM; | ||
3730 | |||
3731 | vdbg_printk(TPACPI_DBG_RFKILL, | 3847 | vdbg_printk(TPACPI_DBG_RFKILL, |
3732 | "will %s UWB\n", radio_on ? "enable" : "disable"); | 3848 | "will attempt to %s UWB\n", |
3849 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); | ||
3733 | 3850 | ||
3734 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3851 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3735 | if (dbg_uwbemul) { | 3852 | if (dbg_uwbemul) { |
3736 | tpacpi_uwb_emulstate = !!radio_on; | 3853 | tpacpi_uwb_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3737 | if (update_rfk) | ||
3738 | uwb_update_rfk(); | ||
3739 | return 0; | 3854 | return 0; |
3740 | } | 3855 | } |
3741 | #endif | 3856 | #endif |
3742 | 3857 | ||
3743 | 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 | |||
3744 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) | 3863 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) |
3745 | return -EIO; | 3864 | return -EIO; |
3746 | 3865 | ||
3747 | if (update_rfk) | ||
3748 | uwb_update_rfk(); | ||
3749 | |||
3750 | return 0; | 3866 | return 0; |
3751 | } | 3867 | } |
3752 | 3868 | ||
3753 | /* --------------------------------------------------------------------- */ | 3869 | /* --------------------------------------------------------------------- */ |
3754 | 3870 | ||
3755 | static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) | 3871 | static const struct tpacpi_rfk_ops uwb_tprfk_ops = { |
3756 | { | 3872 | .get_status = uwb_get_status, |
3757 | int uwbs = uwb_get_radiosw(); | 3873 | .set_status = uwb_set_status, |
3758 | 3874 | }; | |
3759 | if (uwbs < 0) | ||
3760 | return uwbs; | ||
3761 | |||
3762 | *state = uwbs; | ||
3763 | return 0; | ||
3764 | } | ||
3765 | |||
3766 | static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) | ||
3767 | { | ||
3768 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3769 | "request to change radio state to %d\n", state); | ||
3770 | return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3771 | } | ||
3772 | 3875 | ||
3773 | static void uwb_exit(void) | 3876 | static void uwb_exit(void) |
3774 | { | 3877 | { |
3775 | if (tpacpi_uwb_rfkill) | 3878 | tpacpi_destroy_rfkill(TPACPI_RFK_UWB_SW_ID); |
3776 | rfkill_unregister(tpacpi_uwb_rfkill); | ||
3777 | } | 3879 | } |
3778 | 3880 | ||
3779 | static int __init uwb_init(struct ibm_init_struct *iibm) | 3881 | static int __init uwb_init(struct ibm_init_struct *iibm) |
@@ -3813,13 +3915,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm) | |||
3813 | return 1; | 3915 | return 1; |
3814 | 3916 | ||
3815 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, | 3917 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, |
3816 | &tpacpi_uwb_rfkill, | 3918 | &uwb_tprfk_ops, |
3817 | RFKILL_TYPE_UWB, | 3919 | RFKILL_TYPE_UWB, |
3818 | TPACPI_RFK_UWB_SW_NAME, | 3920 | TPACPI_RFK_UWB_SW_NAME, |
3819 | false, | 3921 | false); |
3820 | tpacpi_uwb_rfk_set, | ||
3821 | tpacpi_uwb_rfk_get); | ||
3822 | |||
3823 | return res; | 3922 | return res; |
3824 | } | 3923 | } |
3825 | 3924 | ||
@@ -4745,7 +4844,7 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | |||
4745 | "LED", /* all others */ | 4844 | "LED", /* all others */ |
4746 | ); /* R30, R31 */ | 4845 | ); /* R30, R31 */ |
4747 | 4846 | ||
4748 | #define TPACPI_LED_NUMLEDS 8 | 4847 | #define TPACPI_LED_NUMLEDS 16 |
4749 | static struct tpacpi_led_classdev *tpacpi_leds; | 4848 | static struct tpacpi_led_classdev *tpacpi_leds; |
4750 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; | 4849 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; |
4751 | static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | 4850 | static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { |
@@ -4758,15 +4857,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | |||
4758 | "tpacpi::dock_batt", | 4857 | "tpacpi::dock_batt", |
4759 | "tpacpi::unknown_led", | 4858 | "tpacpi::unknown_led", |
4760 | "tpacpi::standby", | 4859 | "tpacpi::standby", |
4860 | "tpacpi::dock_status1", | ||
4861 | "tpacpi::dock_status2", | ||
4862 | "tpacpi::unknown_led2", | ||
4863 | "tpacpi::unknown_led3", | ||
4864 | "tpacpi::thinkvantage", | ||
4761 | }; | 4865 | }; |
4762 | #define TPACPI_SAFE_LEDS 0x0081U | 4866 | #define TPACPI_SAFE_LEDS 0x1081U |
4763 | 4867 | ||
4764 | static inline bool tpacpi_is_led_restricted(const unsigned int led) | 4868 | static inline bool tpacpi_is_led_restricted(const unsigned int led) |
4765 | { | 4869 | { |
4766 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 4870 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
4767 | return false; | 4871 | return false; |
4768 | #else | 4872 | #else |
4769 | return (TPACPI_SAFE_LEDS & (1 << led)) == 0; | 4873 | return (1U & (TPACPI_SAFE_LEDS >> led)) == 0; |
4770 | #endif | 4874 | #endif |
4771 | } | 4875 | } |
4772 | 4876 | ||
@@ -4928,6 +5032,10 @@ static int __init tpacpi_init_led(unsigned int led) | |||
4928 | 5032 | ||
4929 | tpacpi_leds[led].led = led; | 5033 | tpacpi_leds[led].led = led; |
4930 | 5034 | ||
5035 | /* LEDs with no name don't get registered */ | ||
5036 | if (!tpacpi_led_names[led]) | ||
5037 | return 0; | ||
5038 | |||
4931 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; | 5039 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; |
4932 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; | 5040 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; |
4933 | if (led_supported == TPACPI_LED_570) | 5041 | if (led_supported == TPACPI_LED_570) |
@@ -4946,10 +5054,59 @@ static int __init tpacpi_init_led(unsigned int led) | |||
4946 | return rc; | 5054 | return rc; |
4947 | } | 5055 | } |
4948 | 5056 | ||
5057 | static const struct tpacpi_quirk led_useful_qtable[] __initconst = { | ||
5058 | TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */ | ||
5059 | TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */ | ||
5060 | TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */ | ||
5061 | |||
5062 | TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */ | ||
5063 | TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */ | ||
5064 | TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */ | ||
5065 | TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */ | ||
5066 | TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */ | ||
5067 | TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */ | ||
5068 | TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */ | ||
5069 | TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */ | ||
5070 | |||
5071 | TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */ | ||
5072 | TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */ | ||
5073 | TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */ | ||
5074 | TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */ | ||
5075 | TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */ | ||
5076 | |||
5077 | TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */ | ||
5078 | TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */ | ||
5079 | TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */ | ||
5080 | TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */ | ||
5081 | |||
5082 | /* (1) - may have excess leds enabled on MSB */ | ||
5083 | |||
5084 | /* Defaults (order matters, keep last, don't reorder!) */ | ||
5085 | { /* Lenovo */ | ||
5086 | .vendor = PCI_VENDOR_ID_LENOVO, | ||
5087 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, | ||
5088 | .quirks = 0x1fffU, | ||
5089 | }, | ||
5090 | { /* IBM ThinkPads with no EC version string */ | ||
5091 | .vendor = PCI_VENDOR_ID_IBM, | ||
5092 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN, | ||
5093 | .quirks = 0x00ffU, | ||
5094 | }, | ||
5095 | { /* IBM ThinkPads with EC version string */ | ||
5096 | .vendor = PCI_VENDOR_ID_IBM, | ||
5097 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, | ||
5098 | .quirks = 0x00bfU, | ||
5099 | }, | ||
5100 | }; | ||
5101 | |||
5102 | #undef TPACPI_LEDQ_IBM | ||
5103 | #undef TPACPI_LEDQ_LNV | ||
5104 | |||
4949 | static int __init led_init(struct ibm_init_struct *iibm) | 5105 | static int __init led_init(struct ibm_init_struct *iibm) |
4950 | { | 5106 | { |
4951 | unsigned int i; | 5107 | unsigned int i; |
4952 | int rc; | 5108 | int rc; |
5109 | unsigned long useful_leds; | ||
4953 | 5110 | ||
4954 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 5111 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
4955 | 5112 | ||
@@ -4971,6 +5128,9 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
4971 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 5128 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
4972 | str_supported(led_supported), led_supported); | 5129 | str_supported(led_supported), led_supported); |
4973 | 5130 | ||
5131 | if (led_supported == TPACPI_LED_NONE) | ||
5132 | return 1; | ||
5133 | |||
4974 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | 5134 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, |
4975 | GFP_KERNEL); | 5135 | GFP_KERNEL); |
4976 | if (!tpacpi_leds) { | 5136 | if (!tpacpi_leds) { |
@@ -4978,8 +5138,12 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
4978 | return -ENOMEM; | 5138 | return -ENOMEM; |
4979 | } | 5139 | } |
4980 | 5140 | ||
5141 | useful_leds = tpacpi_check_quirks(led_useful_qtable, | ||
5142 | ARRAY_SIZE(led_useful_qtable)); | ||
5143 | |||
4981 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | 5144 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { |
4982 | if (!tpacpi_is_led_restricted(i)) { | 5145 | if (!tpacpi_is_led_restricted(i) && |
5146 | test_bit(i, &useful_leds)) { | ||
4983 | rc = tpacpi_init_led(i); | 5147 | rc = tpacpi_init_led(i); |
4984 | if (rc < 0) { | 5148 | if (rc < 0) { |
4985 | led_exit(); | 5149 | led_exit(); |
@@ -4989,12 +5153,11 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
4989 | } | 5153 | } |
4990 | 5154 | ||
4991 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 5155 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
4992 | if (led_supported != TPACPI_LED_NONE) | 5156 | printk(TPACPI_NOTICE |
4993 | printk(TPACPI_NOTICE | 5157 | "warning: userspace override of important " |
4994 | "warning: userspace override of important " | 5158 | "firmware LEDs is enabled\n"); |
4995 | "firmware LEDs is enabled\n"); | ||
4996 | #endif | 5159 | #endif |
4997 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 5160 | return 0; |
4998 | } | 5161 | } |
4999 | 5162 | ||
5000 | #define str_led_status(s) \ | 5163 | #define str_led_status(s) \ |
@@ -5024,7 +5187,7 @@ static int led_read(char *p) | |||
5024 | } | 5187 | } |
5025 | 5188 | ||
5026 | len += sprintf(p + len, "commands:\t" | 5189 | len += sprintf(p + len, "commands:\t" |
5027 | "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); | 5190 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); |
5028 | 5191 | ||
5029 | return len; | 5192 | return len; |
5030 | } | 5193 | } |
@@ -5039,7 +5202,7 @@ static int led_write(char *buf) | |||
5039 | return -ENODEV; | 5202 | return -ENODEV; |
5040 | 5203 | ||
5041 | while ((cmd = next_cmd(&buf))) { | 5204 | while ((cmd = next_cmd(&buf))) { |
5042 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) | 5205 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15) |
5043 | return -EINVAL; | 5206 | return -EINVAL; |
5044 | 5207 | ||
5045 | if (strstr(cmd, "off")) { | 5208 | if (strstr(cmd, "off")) { |
@@ -5073,8 +5236,17 @@ static struct ibm_struct led_driver_data = { | |||
5073 | 5236 | ||
5074 | TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ | 5237 | TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ |
5075 | 5238 | ||
5239 | #define TPACPI_BEEP_Q1 0x0001 | ||
5240 | |||
5241 | static const struct tpacpi_quirk beep_quirk_table[] __initconst = { | ||
5242 | TPACPI_Q_IBM('I', 'M', TPACPI_BEEP_Q1), /* 570 */ | ||
5243 | TPACPI_Q_IBM('I', 'U', TPACPI_BEEP_Q1), /* 570E - unverified */ | ||
5244 | }; | ||
5245 | |||
5076 | static int __init beep_init(struct ibm_init_struct *iibm) | 5246 | static int __init beep_init(struct ibm_init_struct *iibm) |
5077 | { | 5247 | { |
5248 | unsigned long quirks; | ||
5249 | |||
5078 | vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); | 5250 | vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); |
5079 | 5251 | ||
5080 | TPACPI_ACPIHANDLE_INIT(beep); | 5252 | TPACPI_ACPIHANDLE_INIT(beep); |
@@ -5082,6 +5254,11 @@ static int __init beep_init(struct ibm_init_struct *iibm) | |||
5082 | vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", | 5254 | vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", |
5083 | str_supported(beep_handle != NULL)); | 5255 | str_supported(beep_handle != NULL)); |
5084 | 5256 | ||
5257 | quirks = tpacpi_check_quirks(beep_quirk_table, | ||
5258 | ARRAY_SIZE(beep_quirk_table)); | ||
5259 | |||
5260 | tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1); | ||
5261 | |||
5085 | return (beep_handle)? 0 : 1; | 5262 | return (beep_handle)? 0 : 1; |
5086 | } | 5263 | } |
5087 | 5264 | ||
@@ -5113,8 +5290,15 @@ static int beep_write(char *buf) | |||
5113 | /* beep_cmd set */ | 5290 | /* beep_cmd set */ |
5114 | } else | 5291 | } else |
5115 | return -EINVAL; | 5292 | return -EINVAL; |
5116 | if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) | 5293 | if (tp_features.beep_needs_two_args) { |
5117 | return -EIO; | 5294 | if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", |
5295 | beep_cmd, 0)) | ||
5296 | return -EIO; | ||
5297 | } else { | ||
5298 | if (!acpi_evalf(beep_handle, NULL, NULL, "vd", | ||
5299 | beep_cmd)) | ||
5300 | return -EIO; | ||
5301 | } | ||
5118 | } | 5302 | } |
5119 | 5303 | ||
5120 | return 0; | 5304 | return 0; |
@@ -5541,6 +5725,10 @@ static struct ibm_struct ecdump_driver_data = { | |||
5541 | * Bit 3-0: backlight brightness level | 5725 | * Bit 3-0: backlight brightness level |
5542 | * | 5726 | * |
5543 | * brightness_get_raw returns status data in the HBRV layout | 5727 | * brightness_get_raw returns status data in the HBRV layout |
5728 | * | ||
5729 | * WARNING: The X61 has been verified to use HBRV for something else, so | ||
5730 | * this should be used _only_ on IBM ThinkPads, and maybe with some careful | ||
5731 | * testing on the very early *60 Lenovo models... | ||
5544 | */ | 5732 | */ |
5545 | 5733 | ||
5546 | enum { | 5734 | enum { |
@@ -5841,6 +6029,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
5841 | brightness_mode); | 6029 | brightness_mode); |
5842 | } | 6030 | } |
5843 | 6031 | ||
6032 | /* Safety */ | ||
6033 | if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM && | ||
6034 | (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || | ||
6035 | brightness_mode == TPACPI_BRGHT_MODE_EC)) | ||
6036 | return -EINVAL; | ||
6037 | |||
5844 | if (tpacpi_brightness_get_raw(&b) < 0) | 6038 | if (tpacpi_brightness_get_raw(&b) < 0) |
5845 | return 1; | 6039 | return 1; |
5846 | 6040 | ||
@@ -6133,6 +6327,21 @@ static struct ibm_struct volume_driver_data = { | |||
6133 | * For firmware bugs, refer to: | 6327 | * For firmware bugs, refer to: |
6134 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues | 6328 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
6135 | * | 6329 | * |
6330 | * ---- | ||
6331 | * | ||
6332 | * ThinkPad EC register 0x31 bit 0 (only on select models) | ||
6333 | * | ||
6334 | * When bit 0 of EC register 0x31 is zero, the tachometer registers | ||
6335 | * show the speed of the main fan. When bit 0 of EC register 0x31 | ||
6336 | * is one, the tachometer registers show the speed of the auxiliary | ||
6337 | * fan. | ||
6338 | * | ||
6339 | * Fan control seems to affect both fans, regardless of the state | ||
6340 | * of this bit. | ||
6341 | * | ||
6342 | * So far, only the firmware for the X60/X61 non-tablet versions | ||
6343 | * seem to support this (firmware TP-7M). | ||
6344 | * | ||
6136 | * TPACPI_FAN_WR_ACPI_FANS: | 6345 | * TPACPI_FAN_WR_ACPI_FANS: |
6137 | * ThinkPad X31, X40, X41. Not available in the X60. | 6346 | * ThinkPad X31, X40, X41. Not available in the X60. |
6138 | * | 6347 | * |
@@ -6159,6 +6368,8 @@ enum { /* Fan control constants */ | |||
6159 | fan_status_offset = 0x2f, /* EC register 0x2f */ | 6368 | fan_status_offset = 0x2f, /* EC register 0x2f */ |
6160 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) | 6369 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) |
6161 | * 0x84 must be read before 0x85 */ | 6370 | * 0x84 must be read before 0x85 */ |
6371 | fan_select_offset = 0x31, /* EC register 0x31 (Firmware 7M) | ||
6372 | bit 0 selects which fan is active */ | ||
6162 | 6373 | ||
6163 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ | 6374 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ |
6164 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ | 6375 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ |
@@ -6221,30 +6432,18 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
6221 | * We assume 0x07 really means auto mode while this quirk is active, | 6432 | * We assume 0x07 really means auto mode while this quirk is active, |
6222 | * as this is far more likely than the ThinkPad being in level 7, | 6433 | * as this is far more likely than the ThinkPad being in level 7, |
6223 | * which is only used by the firmware during thermal emergencies. | 6434 | * which is only used by the firmware during thermal emergencies. |
6435 | * | ||
6436 | * Enable for TP-1Y (T43), TP-78 (R51e), TP-76 (R52), | ||
6437 | * TP-70 (T43, R52), which are known to be buggy. | ||
6224 | */ | 6438 | */ |
6225 | 6439 | ||
6226 | static void fan_quirk1_detect(void) | 6440 | static void fan_quirk1_setup(void) |
6227 | { | 6441 | { |
6228 | /* In some ThinkPads, neither the EC nor the ACPI | ||
6229 | * DSDT initialize the HFSP register, and it ends up | ||
6230 | * being initially set to 0x07 when it *could* be | ||
6231 | * either 0x07 or 0x80. | ||
6232 | * | ||
6233 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
6234 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
6235 | * to be buggy. */ | ||
6236 | if (fan_control_initial_status == 0x07) { | 6442 | if (fan_control_initial_status == 0x07) { |
6237 | switch (thinkpad_id.ec_model) { | 6443 | printk(TPACPI_NOTICE |
6238 | case 0x5931: /* TP-1Y */ | 6444 | "fan_init: initial fan status is unknown, " |
6239 | case 0x3837: /* TP-78 */ | 6445 | "assuming it is in auto mode\n"); |
6240 | case 0x3637: /* TP-76 */ | 6446 | tp_features.fan_ctrl_status_undef = 1; |
6241 | case 0x3037: /* TP-70 */ | ||
6242 | printk(TPACPI_NOTICE | ||
6243 | "fan_init: initial fan status is unknown, " | ||
6244 | "assuming it is in auto mode\n"); | ||
6245 | tp_features.fan_ctrl_status_undef = 1; | ||
6246 | ;; | ||
6247 | } | ||
6248 | } | 6447 | } |
6249 | } | 6448 | } |
6250 | 6449 | ||
@@ -6264,6 +6463,38 @@ static void fan_quirk1_handle(u8 *fan_status) | |||
6264 | } | 6463 | } |
6265 | } | 6464 | } |
6266 | 6465 | ||
6466 | /* Select main fan on X60/X61, NOOP on others */ | ||
6467 | static bool fan_select_fan1(void) | ||
6468 | { | ||
6469 | if (tp_features.second_fan) { | ||
6470 | u8 val; | ||
6471 | |||
6472 | if (ec_read(fan_select_offset, &val) < 0) | ||
6473 | return false; | ||
6474 | val &= 0xFEU; | ||
6475 | if (ec_write(fan_select_offset, val) < 0) | ||
6476 | return false; | ||
6477 | } | ||
6478 | return true; | ||
6479 | } | ||
6480 | |||
6481 | /* Select secondary fan on X60/X61 */ | ||
6482 | static bool fan_select_fan2(void) | ||
6483 | { | ||
6484 | u8 val; | ||
6485 | |||
6486 | if (!tp_features.second_fan) | ||
6487 | return false; | ||
6488 | |||
6489 | if (ec_read(fan_select_offset, &val) < 0) | ||
6490 | return false; | ||
6491 | val |= 0x01U; | ||
6492 | if (ec_write(fan_select_offset, val) < 0) | ||
6493 | return false; | ||
6494 | |||
6495 | return true; | ||
6496 | } | ||
6497 | |||
6267 | /* | 6498 | /* |
6268 | * Call with fan_mutex held | 6499 | * Call with fan_mutex held |
6269 | */ | 6500 | */ |
@@ -6341,6 +6572,8 @@ static int fan_get_speed(unsigned int *speed) | |||
6341 | switch (fan_status_access_mode) { | 6572 | switch (fan_status_access_mode) { |
6342 | case TPACPI_FAN_RD_TPEC: | 6573 | case TPACPI_FAN_RD_TPEC: |
6343 | /* all except 570, 600e/x, 770e, 770x */ | 6574 | /* all except 570, 600e/x, 770e, 770x */ |
6575 | if (unlikely(!fan_select_fan1())) | ||
6576 | return -EIO; | ||
6344 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || | 6577 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || |
6345 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) | 6578 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) |
6346 | return -EIO; | 6579 | return -EIO; |
@@ -6357,6 +6590,34 @@ static int fan_get_speed(unsigned int *speed) | |||
6357 | return 0; | 6590 | return 0; |
6358 | } | 6591 | } |
6359 | 6592 | ||
6593 | static int fan2_get_speed(unsigned int *speed) | ||
6594 | { | ||
6595 | u8 hi, lo; | ||
6596 | bool rc; | ||
6597 | |||
6598 | switch (fan_status_access_mode) { | ||
6599 | case TPACPI_FAN_RD_TPEC: | ||
6600 | /* all except 570, 600e/x, 770e, 770x */ | ||
6601 | if (unlikely(!fan_select_fan2())) | ||
6602 | return -EIO; | ||
6603 | rc = !acpi_ec_read(fan_rpm_offset, &lo) || | ||
6604 | !acpi_ec_read(fan_rpm_offset + 1, &hi); | ||
6605 | fan_select_fan1(); /* play it safe */ | ||
6606 | if (rc) | ||
6607 | return -EIO; | ||
6608 | |||
6609 | if (likely(speed)) | ||
6610 | *speed = (hi << 8) | lo; | ||
6611 | |||
6612 | break; | ||
6613 | |||
6614 | default: | ||
6615 | return -ENXIO; | ||
6616 | } | ||
6617 | |||
6618 | return 0; | ||
6619 | } | ||
6620 | |||
6360 | static int fan_set_level(int level) | 6621 | static int fan_set_level(int level) |
6361 | { | 6622 | { |
6362 | if (!fan_control_allowed) | 6623 | if (!fan_control_allowed) |
@@ -6762,6 +7023,25 @@ static struct device_attribute dev_attr_fan_fan1_input = | |||
6762 | __ATTR(fan1_input, S_IRUGO, | 7023 | __ATTR(fan1_input, S_IRUGO, |
6763 | fan_fan1_input_show, NULL); | 7024 | fan_fan1_input_show, NULL); |
6764 | 7025 | ||
7026 | /* sysfs fan fan2_input ------------------------------------------------ */ | ||
7027 | static ssize_t fan_fan2_input_show(struct device *dev, | ||
7028 | struct device_attribute *attr, | ||
7029 | char *buf) | ||
7030 | { | ||
7031 | int res; | ||
7032 | unsigned int speed; | ||
7033 | |||
7034 | res = fan2_get_speed(&speed); | ||
7035 | if (res < 0) | ||
7036 | return res; | ||
7037 | |||
7038 | return snprintf(buf, PAGE_SIZE, "%u\n", speed); | ||
7039 | } | ||
7040 | |||
7041 | static struct device_attribute dev_attr_fan_fan2_input = | ||
7042 | __ATTR(fan2_input, S_IRUGO, | ||
7043 | fan_fan2_input_show, NULL); | ||
7044 | |||
6765 | /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ | 7045 | /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ |
6766 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, | 7046 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, |
6767 | char *buf) | 7047 | char *buf) |
@@ -6795,6 +7075,7 @@ static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, | |||
6795 | static struct attribute *fan_attributes[] = { | 7075 | static struct attribute *fan_attributes[] = { |
6796 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, | 7076 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, |
6797 | &dev_attr_fan_fan1_input.attr, | 7077 | &dev_attr_fan_fan1_input.attr, |
7078 | NULL, /* for fan2_input */ | ||
6798 | NULL | 7079 | NULL |
6799 | }; | 7080 | }; |
6800 | 7081 | ||
@@ -6802,9 +7083,36 @@ static const struct attribute_group fan_attr_group = { | |||
6802 | .attrs = fan_attributes, | 7083 | .attrs = fan_attributes, |
6803 | }; | 7084 | }; |
6804 | 7085 | ||
7086 | #define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ | ||
7087 | #define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ | ||
7088 | |||
7089 | #define TPACPI_FAN_QI(__id1, __id2, __quirks) \ | ||
7090 | { .vendor = PCI_VENDOR_ID_IBM, \ | ||
7091 | .bios = TPACPI_MATCH_ANY, \ | ||
7092 | .ec = TPID(__id1, __id2), \ | ||
7093 | .quirks = __quirks } | ||
7094 | |||
7095 | #define TPACPI_FAN_QL(__id1, __id2, __quirks) \ | ||
7096 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
7097 | .bios = TPACPI_MATCH_ANY, \ | ||
7098 | .ec = TPID(__id1, __id2), \ | ||
7099 | .quirks = __quirks } | ||
7100 | |||
7101 | static const struct tpacpi_quirk fan_quirk_table[] __initconst = { | ||
7102 | TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), | ||
7103 | TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), | ||
7104 | TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), | ||
7105 | TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), | ||
7106 | TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), | ||
7107 | }; | ||
7108 | |||
7109 | #undef TPACPI_FAN_QL | ||
7110 | #undef TPACPI_FAN_QI | ||
7111 | |||
6805 | static int __init fan_init(struct ibm_init_struct *iibm) | 7112 | static int __init fan_init(struct ibm_init_struct *iibm) |
6806 | { | 7113 | { |
6807 | int rc; | 7114 | int rc; |
7115 | unsigned long quirks; | ||
6808 | 7116 | ||
6809 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, | 7117 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, |
6810 | "initializing fan subdriver\n"); | 7118 | "initializing fan subdriver\n"); |
@@ -6815,12 +7123,16 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
6815 | fan_control_commands = 0; | 7123 | fan_control_commands = 0; |
6816 | fan_watchdog_maxinterval = 0; | 7124 | fan_watchdog_maxinterval = 0; |
6817 | tp_features.fan_ctrl_status_undef = 0; | 7125 | tp_features.fan_ctrl_status_undef = 0; |
7126 | tp_features.second_fan = 0; | ||
6818 | fan_control_desired_level = 7; | 7127 | fan_control_desired_level = 7; |
6819 | 7128 | ||
6820 | TPACPI_ACPIHANDLE_INIT(fans); | 7129 | TPACPI_ACPIHANDLE_INIT(fans); |
6821 | TPACPI_ACPIHANDLE_INIT(gfan); | 7130 | TPACPI_ACPIHANDLE_INIT(gfan); |
6822 | TPACPI_ACPIHANDLE_INIT(sfan); | 7131 | TPACPI_ACPIHANDLE_INIT(sfan); |
6823 | 7132 | ||
7133 | quirks = tpacpi_check_quirks(fan_quirk_table, | ||
7134 | ARRAY_SIZE(fan_quirk_table)); | ||
7135 | |||
6824 | if (gfan_handle) { | 7136 | if (gfan_handle) { |
6825 | /* 570, 600e/x, 770e, 770x */ | 7137 | /* 570, 600e/x, 770e, 770x */ |
6826 | fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; | 7138 | fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; |
@@ -6830,7 +7142,13 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
6830 | if (likely(acpi_ec_read(fan_status_offset, | 7142 | if (likely(acpi_ec_read(fan_status_offset, |
6831 | &fan_control_initial_status))) { | 7143 | &fan_control_initial_status))) { |
6832 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; | 7144 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; |
6833 | fan_quirk1_detect(); | 7145 | if (quirks & TPACPI_FAN_Q1) |
7146 | fan_quirk1_setup(); | ||
7147 | if (quirks & TPACPI_FAN_2FAN) { | ||
7148 | tp_features.second_fan = 1; | ||
7149 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, | ||
7150 | "secondary fan support enabled\n"); | ||
7151 | } | ||
6834 | } else { | 7152 | } else { |
6835 | printk(TPACPI_ERR | 7153 | printk(TPACPI_ERR |
6836 | "ThinkPad ACPI EC access misbehaving, " | 7154 | "ThinkPad ACPI EC access misbehaving, " |
@@ -6886,6 +7204,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
6886 | 7204 | ||
6887 | if (fan_status_access_mode != TPACPI_FAN_NONE || | 7205 | if (fan_status_access_mode != TPACPI_FAN_NONE || |
6888 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { | 7206 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { |
7207 | if (tp_features.second_fan) { | ||
7208 | /* attach second fan tachometer */ | ||
7209 | fan_attributes[ARRAY_SIZE(fan_attributes)-2] = | ||
7210 | &dev_attr_fan_fan2_input.attr; | ||
7211 | } | ||
6889 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, | 7212 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, |
6890 | &fan_attr_group); | 7213 | &fan_attr_group); |
6891 | if (rc < 0) | 7214 | if (rc < 0) |
@@ -7357,6 +7680,24 @@ err_out: | |||
7357 | 7680 | ||
7358 | /* Probing */ | 7681 | /* Probing */ |
7359 | 7682 | ||
7683 | static bool __pure __init tpacpi_is_fw_digit(const char c) | ||
7684 | { | ||
7685 | return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); | ||
7686 | } | ||
7687 | |||
7688 | /* Most models: xxyTkkWW (#.##c); Ancient 570/600 and -SL lacks (#.##c) */ | ||
7689 | static bool __pure __init tpacpi_is_valid_fw_id(const char* const s, | ||
7690 | const char t) | ||
7691 | { | ||
7692 | return s && strlen(s) >= 8 && | ||
7693 | tpacpi_is_fw_digit(s[0]) && | ||
7694 | tpacpi_is_fw_digit(s[1]) && | ||
7695 | s[2] == t && s[3] == 'T' && | ||
7696 | tpacpi_is_fw_digit(s[4]) && | ||
7697 | tpacpi_is_fw_digit(s[5]) && | ||
7698 | s[6] == 'W' && s[7] == 'W'; | ||
7699 | } | ||
7700 | |||
7360 | /* returns 0 - probe ok, or < 0 - probe error. | 7701 | /* returns 0 - probe ok, or < 0 - probe error. |
7361 | * Probe ok doesn't mean thinkpad found. | 7702 | * Probe ok doesn't mean thinkpad found. |
7362 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ | 7703 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ |
@@ -7383,10 +7724,15 @@ static int __must_check __init get_thinkpad_model_data( | |||
7383 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); | 7724 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); |
7384 | if (s && !tp->bios_version_str) | 7725 | if (s && !tp->bios_version_str) |
7385 | return -ENOMEM; | 7726 | return -ENOMEM; |
7386 | if (!tp->bios_version_str) | 7727 | |
7728 | /* Really ancient ThinkPad 240X will fail this, which is fine */ | ||
7729 | if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E')) | ||
7387 | return 0; | 7730 | return 0; |
7731 | |||
7388 | tp->bios_model = tp->bios_version_str[0] | 7732 | tp->bios_model = tp->bios_version_str[0] |
7389 | | (tp->bios_version_str[1] << 8); | 7733 | | (tp->bios_version_str[1] << 8); |
7734 | tp->bios_release = (tp->bios_version_str[4] << 8) | ||
7735 | | tp->bios_version_str[5]; | ||
7390 | 7736 | ||
7391 | /* | 7737 | /* |
7392 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, | 7738 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, |
@@ -7405,8 +7751,21 @@ static int __must_check __init get_thinkpad_model_data( | |||
7405 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); | 7751 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
7406 | if (!tp->ec_version_str) | 7752 | if (!tp->ec_version_str) |
7407 | return -ENOMEM; | 7753 | return -ENOMEM; |
7408 | tp->ec_model = ec_fw_string[0] | 7754 | |
7409 | | (ec_fw_string[1] << 8); | 7755 | if (tpacpi_is_valid_fw_id(ec_fw_string, 'H')) { |
7756 | tp->ec_model = ec_fw_string[0] | ||
7757 | | (ec_fw_string[1] << 8); | ||
7758 | tp->ec_release = (ec_fw_string[4] << 8) | ||
7759 | | ec_fw_string[5]; | ||
7760 | } else { | ||
7761 | printk(TPACPI_NOTICE | ||
7762 | "ThinkPad firmware release %s " | ||
7763 | "doesn't match the known patterns\n", | ||
7764 | ec_fw_string); | ||
7765 | printk(TPACPI_NOTICE | ||
7766 | "please report this to %s\n", | ||
7767 | TPACPI_MAIL); | ||
7768 | } | ||
7410 | break; | 7769 | break; |
7411 | } | 7770 | } |
7412 | } | 7771 | } |
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 | } |