diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-07-26 11:48:49 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-26 11:48:49 -0400 |
commit | c3cc99ff5d24e2eeaf7ec2032e720681916990e3 (patch) | |
tree | c3e74171bbbd2adde9d60b9db1c440415c8d2831 /drivers/misc | |
parent | 38ffbe66d59051fd9cfcfc8545f164700e2fa3bc (diff) | |
parent | 024e8ac04453b3525448c31ef39848cf675ba6db (diff) |
Merge branch 'linus' into x86/xen
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 19 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/atmel_pwm.c | 3 | ||||
-rw-r--r-- | drivers/misc/hp-wmi.c | 494 | ||||
-rw-r--r-- | drivers/misc/phantom.c | 2 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 475 |
6 files changed, 848 insertions, 146 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ce67d973d349..321eb9134635 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -77,11 +77,13 @@ config IBM_ASM | |||
77 | for your IBM server. | 77 | for your IBM server. |
78 | 78 | ||
79 | config PHANTOM | 79 | config PHANTOM |
80 | tristate "Sensable PHANToM" | 80 | tristate "Sensable PHANToM (PCI)" |
81 | depends on PCI | 81 | depends on PCI |
82 | help | 82 | help |
83 | Say Y here if you want to build a driver for Sensable PHANToM device. | 83 | Say Y here if you want to build a driver for Sensable PHANToM device. |
84 | 84 | ||
85 | This driver is only for PCI PHANToMs. | ||
86 | |||
85 | If you choose to build module, its name will be phantom. If unsure, | 87 | If you choose to build module, its name will be phantom. If unsure, |
86 | say N here. | 88 | say N here. |
87 | 89 | ||
@@ -212,6 +214,18 @@ config TC1100_WMI | |||
212 | This is a driver for the WMI extensions (wireless and bluetooth power | 214 | This is a driver for the WMI extensions (wireless and bluetooth power |
213 | control) of the HP Compaq TC1100 tablet. | 215 | control) of the HP Compaq TC1100 tablet. |
214 | 216 | ||
217 | config HP_WMI | ||
218 | tristate "HP WMI extras" | ||
219 | depends on ACPI_WMI | ||
220 | depends on INPUT | ||
221 | depends on RFKILL | ||
222 | help | ||
223 | Say Y here if you want to support WMI-based hotkeys on HP laptops and | ||
224 | to read data from WMI such as docking or ambient light sensor state. | ||
225 | |||
226 | To compile this driver as a module, choose M here: the module will | ||
227 | be called hp-wmi. | ||
228 | |||
215 | config MSI_LAPTOP | 229 | config MSI_LAPTOP |
216 | tristate "MSI Laptop Extras" | 230 | tristate "MSI Laptop Extras" |
217 | depends on X86 | 231 | depends on X86 |
@@ -279,6 +293,8 @@ config THINKPAD_ACPI | |||
279 | select INPUT | 293 | select INPUT |
280 | select NEW_LEDS | 294 | select NEW_LEDS |
281 | select LEDS_CLASS | 295 | select LEDS_CLASS |
296 | select NET | ||
297 | select RFKILL | ||
282 | ---help--- | 298 | ---help--- |
283 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds | 299 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds |
284 | support for Fn-Fx key combinations, Bluetooth control, video | 300 | support for Fn-Fx key combinations, Bluetooth control, video |
@@ -422,6 +438,7 @@ config SGI_XP | |||
422 | 438 | ||
423 | config HP_ILO | 439 | config HP_ILO |
424 | tristate "Channel interface driver for HP iLO/iLO2 processor" | 440 | tristate "Channel interface driver for HP iLO/iLO2 processor" |
441 | depends on PCI | ||
425 | default n | 442 | default n |
426 | help | 443 | help |
427 | The channel interface driver allows applications to communicate | 444 | The channel interface driver allows applications to communicate |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 688fe76135e0..f5e273420c09 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -13,6 +13,7 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o | |||
13 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o | 13 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o |
14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o | 14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
16 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | ||
16 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 17 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
17 | obj-$(CONFIG_LKDTM) += lkdtm.o | 18 | obj-$(CONFIG_LKDTM) += lkdtm.o |
18 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o | 19 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o |
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 5b5a14dab3d3..6aa5294dfec4 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c | |||
@@ -211,8 +211,7 @@ int pwm_clk_alloc(unsigned prescale, unsigned div) | |||
211 | if ((mr & 0xffff) == 0) { | 211 | if ((mr & 0xffff) == 0) { |
212 | mr |= val; | 212 | mr |= val; |
213 | ret = PWM_CPR_CLKA; | 213 | ret = PWM_CPR_CLKA; |
214 | } | 214 | } else if ((mr & (0xffff << 16)) == 0) { |
215 | if ((mr & (0xffff << 16)) == 0) { | ||
216 | mr |= val << 16; | 215 | mr |= val << 16; |
217 | ret = PWM_CPR_CLKB; | 216 | ret = PWM_CPR_CLKB; |
218 | } | 217 | } |
diff --git a/drivers/misc/hp-wmi.c b/drivers/misc/hp-wmi.c new file mode 100644 index 000000000000..1dbcbcb323a2 --- /dev/null +++ b/drivers/misc/hp-wmi.c | |||
@@ -0,0 +1,494 @@ | |||
1 | /* | ||
2 | * HP WMI hotkeys | ||
3 | * | ||
4 | * Copyright (C) 2008 Red Hat <mjg@redhat.com> | ||
5 | * | ||
6 | * Portions based on wistron_btns.c: | ||
7 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | ||
8 | * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | ||
9 | * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/input.h> | ||
31 | #include <acpi/acpi_drivers.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/acpi.h> | ||
34 | #include <linux/rfkill.h> | ||
35 | #include <linux/string.h> | ||
36 | |||
37 | MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>"); | ||
38 | MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | |||
41 | MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); | ||
42 | MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | ||
43 | |||
44 | #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" | ||
45 | #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" | ||
46 | |||
47 | #define HPWMI_DISPLAY_QUERY 0x1 | ||
48 | #define HPWMI_HDDTEMP_QUERY 0x2 | ||
49 | #define HPWMI_ALS_QUERY 0x3 | ||
50 | #define HPWMI_DOCK_QUERY 0x4 | ||
51 | #define HPWMI_WIRELESS_QUERY 0x5 | ||
52 | |||
53 | static int __init hp_wmi_bios_setup(struct platform_device *device); | ||
54 | static int __exit hp_wmi_bios_remove(struct platform_device *device); | ||
55 | |||
56 | struct bios_args { | ||
57 | u32 signature; | ||
58 | u32 command; | ||
59 | u32 commandtype; | ||
60 | u32 datasize; | ||
61 | u32 data; | ||
62 | }; | ||
63 | |||
64 | struct bios_return { | ||
65 | u32 sigpass; | ||
66 | u32 return_code; | ||
67 | u32 value; | ||
68 | }; | ||
69 | |||
70 | struct key_entry { | ||
71 | char type; /* See KE_* below */ | ||
72 | u8 code; | ||
73 | u16 keycode; | ||
74 | }; | ||
75 | |||
76 | enum { KE_KEY, KE_SW, KE_END }; | ||
77 | |||
78 | static struct key_entry hp_wmi_keymap[] = { | ||
79 | {KE_SW, 0x01, SW_DOCK}, | ||
80 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | ||
81 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | ||
82 | {KE_KEY, 0x04, KEY_HELP}, | ||
83 | {KE_END, 0} | ||
84 | }; | ||
85 | |||
86 | static struct input_dev *hp_wmi_input_dev; | ||
87 | static struct platform_device *hp_wmi_platform_dev; | ||
88 | |||
89 | static struct rfkill *wifi_rfkill; | ||
90 | static struct rfkill *bluetooth_rfkill; | ||
91 | static struct rfkill *wwan_rfkill; | ||
92 | |||
93 | static struct platform_driver hp_wmi_driver = { | ||
94 | .driver = { | ||
95 | .name = "hp-wmi", | ||
96 | .owner = THIS_MODULE, | ||
97 | }, | ||
98 | .probe = hp_wmi_bios_setup, | ||
99 | .remove = hp_wmi_bios_remove, | ||
100 | }; | ||
101 | |||
102 | static int hp_wmi_perform_query(int query, int write, int value) | ||
103 | { | ||
104 | struct bios_return bios_return; | ||
105 | acpi_status status; | ||
106 | union acpi_object *obj; | ||
107 | struct bios_args args = { | ||
108 | .signature = 0x55434553, | ||
109 | .command = write ? 0x2 : 0x1, | ||
110 | .commandtype = query, | ||
111 | .datasize = write ? 0x4 : 0, | ||
112 | .data = value, | ||
113 | }; | ||
114 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; | ||
115 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
116 | |||
117 | status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); | ||
118 | |||
119 | obj = output.pointer; | ||
120 | |||
121 | if (!obj || obj->type != ACPI_TYPE_BUFFER) | ||
122 | return -EINVAL; | ||
123 | |||
124 | bios_return = *((struct bios_return *)obj->buffer.pointer); | ||
125 | if (bios_return.return_code > 0) | ||
126 | return bios_return.return_code * -1; | ||
127 | else | ||
128 | return bios_return.value; | ||
129 | } | ||
130 | |||
131 | static int hp_wmi_display_state(void) | ||
132 | { | ||
133 | return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0); | ||
134 | } | ||
135 | |||
136 | static int hp_wmi_hddtemp_state(void) | ||
137 | { | ||
138 | return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0); | ||
139 | } | ||
140 | |||
141 | static int hp_wmi_als_state(void) | ||
142 | { | ||
143 | return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0); | ||
144 | } | ||
145 | |||
146 | static int hp_wmi_dock_state(void) | ||
147 | { | ||
148 | return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); | ||
149 | } | ||
150 | |||
151 | static int hp_wmi_wifi_set(void *data, enum rfkill_state state) | ||
152 | { | ||
153 | if (state) | ||
154 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101); | ||
155 | else | ||
156 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100); | ||
157 | } | ||
158 | |||
159 | static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state) | ||
160 | { | ||
161 | if (state) | ||
162 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202); | ||
163 | else | ||
164 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200); | ||
165 | } | ||
166 | |||
167 | static int hp_wmi_wwan_set(void *data, enum rfkill_state state) | ||
168 | { | ||
169 | if (state) | ||
170 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404); | ||
171 | else | ||
172 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400); | ||
173 | } | ||
174 | |||
175 | static int hp_wmi_wifi_state(void) | ||
176 | { | ||
177 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
178 | |||
179 | if (wireless & 0x100) | ||
180 | return 1; | ||
181 | else | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int hp_wmi_bluetooth_state(void) | ||
186 | { | ||
187 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
188 | |||
189 | if (wireless & 0x10000) | ||
190 | return 1; | ||
191 | else | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int hp_wmi_wwan_state(void) | ||
196 | { | ||
197 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
198 | |||
199 | if (wireless & 0x1000000) | ||
200 | return 1; | ||
201 | else | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, | ||
206 | char *buf) | ||
207 | { | ||
208 | int value = hp_wmi_display_state(); | ||
209 | if (value < 0) | ||
210 | return -EINVAL; | ||
211 | return sprintf(buf, "%d\n", value); | ||
212 | } | ||
213 | |||
214 | static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr, | ||
215 | char *buf) | ||
216 | { | ||
217 | int value = hp_wmi_hddtemp_state(); | ||
218 | if (value < 0) | ||
219 | return -EINVAL; | ||
220 | return sprintf(buf, "%d\n", value); | ||
221 | } | ||
222 | |||
223 | static ssize_t show_als(struct device *dev, struct device_attribute *attr, | ||
224 | char *buf) | ||
225 | { | ||
226 | int value = hp_wmi_als_state(); | ||
227 | if (value < 0) | ||
228 | return -EINVAL; | ||
229 | return sprintf(buf, "%d\n", value); | ||
230 | } | ||
231 | |||
232 | static ssize_t show_dock(struct device *dev, struct device_attribute *attr, | ||
233 | char *buf) | ||
234 | { | ||
235 | int value = hp_wmi_dock_state(); | ||
236 | if (value < 0) | ||
237 | return -EINVAL; | ||
238 | return sprintf(buf, "%d\n", value); | ||
239 | } | ||
240 | |||
241 | static ssize_t set_als(struct device *dev, struct device_attribute *attr, | ||
242 | const char *buf, size_t count) | ||
243 | { | ||
244 | u32 tmp = simple_strtoul(buf, NULL, 10); | ||
245 | hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp); | ||
246 | return count; | ||
247 | } | ||
248 | |||
249 | static DEVICE_ATTR(display, S_IRUGO, show_display, NULL); | ||
250 | static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); | ||
251 | static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | ||
252 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | ||
253 | |||
254 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) | ||
255 | { | ||
256 | struct key_entry *key; | ||
257 | |||
258 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
259 | if (code == key->code) | ||
260 | return key; | ||
261 | |||
262 | return NULL; | ||
263 | } | ||
264 | |||
265 | static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) | ||
266 | { | ||
267 | struct key_entry *key; | ||
268 | |||
269 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
270 | if (key->type == KE_KEY && keycode == key->keycode) | ||
271 | return key; | ||
272 | |||
273 | return NULL; | ||
274 | } | ||
275 | |||
276 | static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) | ||
277 | { | ||
278 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); | ||
279 | |||
280 | if (key && key->type == KE_KEY) { | ||
281 | *keycode = key->keycode; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) | ||
289 | { | ||
290 | struct key_entry *key; | ||
291 | int old_keycode; | ||
292 | |||
293 | if (keycode < 0 || keycode > KEY_MAX) | ||
294 | return -EINVAL; | ||
295 | |||
296 | key = hp_wmi_get_entry_by_scancode(scancode); | ||
297 | if (key && key->type == KE_KEY) { | ||
298 | old_keycode = key->keycode; | ||
299 | key->keycode = keycode; | ||
300 | set_bit(keycode, dev->keybit); | ||
301 | if (!hp_wmi_get_entry_by_keycode(old_keycode)) | ||
302 | clear_bit(old_keycode, dev->keybit); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | return -EINVAL; | ||
307 | } | ||
308 | |||
309 | void hp_wmi_notify(u32 value, void *context) | ||
310 | { | ||
311 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
312 | static struct key_entry *key; | ||
313 | union acpi_object *obj; | ||
314 | |||
315 | wmi_get_event_data(value, &response); | ||
316 | |||
317 | obj = (union acpi_object *)response.pointer; | ||
318 | |||
319 | if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) { | ||
320 | int eventcode = *((u8 *) obj->buffer.pointer); | ||
321 | key = hp_wmi_get_entry_by_scancode(eventcode); | ||
322 | if (key) { | ||
323 | switch (key->type) { | ||
324 | case KE_KEY: | ||
325 | input_report_key(hp_wmi_input_dev, | ||
326 | key->keycode, 1); | ||
327 | input_sync(hp_wmi_input_dev); | ||
328 | input_report_key(hp_wmi_input_dev, | ||
329 | key->keycode, 0); | ||
330 | input_sync(hp_wmi_input_dev); | ||
331 | break; | ||
332 | case KE_SW: | ||
333 | input_report_switch(hp_wmi_input_dev, | ||
334 | key->keycode, | ||
335 | hp_wmi_dock_state()); | ||
336 | input_sync(hp_wmi_input_dev); | ||
337 | break; | ||
338 | } | ||
339 | } else if (eventcode == 0x5) { | ||
340 | if (wifi_rfkill) | ||
341 | wifi_rfkill->state = hp_wmi_wifi_state(); | ||
342 | if (bluetooth_rfkill) | ||
343 | bluetooth_rfkill->state = | ||
344 | hp_wmi_bluetooth_state(); | ||
345 | if (wwan_rfkill) | ||
346 | wwan_rfkill->state = hp_wmi_wwan_state(); | ||
347 | } else | ||
348 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", | ||
349 | eventcode); | ||
350 | } else | ||
351 | printk(KERN_INFO "HP WMI: Unknown response received\n"); | ||
352 | } | ||
353 | |||
354 | static int __init hp_wmi_input_setup(void) | ||
355 | { | ||
356 | struct key_entry *key; | ||
357 | int err; | ||
358 | |||
359 | hp_wmi_input_dev = input_allocate_device(); | ||
360 | |||
361 | hp_wmi_input_dev->name = "HP WMI hotkeys"; | ||
362 | hp_wmi_input_dev->phys = "wmi/input0"; | ||
363 | hp_wmi_input_dev->id.bustype = BUS_HOST; | ||
364 | hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; | ||
365 | hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; | ||
366 | |||
367 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | ||
368 | switch (key->type) { | ||
369 | case KE_KEY: | ||
370 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | ||
371 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | ||
372 | break; | ||
373 | case KE_SW: | ||
374 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | ||
375 | set_bit(key->keycode, hp_wmi_input_dev->swbit); | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | err = input_register_device(hp_wmi_input_dev); | ||
381 | |||
382 | if (err) { | ||
383 | input_free_device(hp_wmi_input_dev); | ||
384 | return err; | ||
385 | } | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static void cleanup_sysfs(struct platform_device *device) | ||
391 | { | ||
392 | device_remove_file(&device->dev, &dev_attr_display); | ||
393 | device_remove_file(&device->dev, &dev_attr_hddtemp); | ||
394 | device_remove_file(&device->dev, &dev_attr_als); | ||
395 | device_remove_file(&device->dev, &dev_attr_dock); | ||
396 | } | ||
397 | |||
398 | static int __init hp_wmi_bios_setup(struct platform_device *device) | ||
399 | { | ||
400 | int err; | ||
401 | |||
402 | err = device_create_file(&device->dev, &dev_attr_display); | ||
403 | if (err) | ||
404 | goto add_sysfs_error; | ||
405 | err = device_create_file(&device->dev, &dev_attr_hddtemp); | ||
406 | if (err) | ||
407 | goto add_sysfs_error; | ||
408 | err = device_create_file(&device->dev, &dev_attr_als); | ||
409 | if (err) | ||
410 | goto add_sysfs_error; | ||
411 | err = device_create_file(&device->dev, &dev_attr_dock); | ||
412 | if (err) | ||
413 | goto add_sysfs_error; | ||
414 | |||
415 | wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); | ||
416 | wifi_rfkill->name = "hp-wifi"; | ||
417 | wifi_rfkill->state = hp_wmi_wifi_state(); | ||
418 | wifi_rfkill->toggle_radio = hp_wmi_wifi_set; | ||
419 | wifi_rfkill->user_claim_unsupported = 1; | ||
420 | |||
421 | bluetooth_rfkill = rfkill_allocate(&device->dev, | ||
422 | RFKILL_TYPE_BLUETOOTH); | ||
423 | bluetooth_rfkill->name = "hp-bluetooth"; | ||
424 | bluetooth_rfkill->state = hp_wmi_bluetooth_state(); | ||
425 | bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; | ||
426 | bluetooth_rfkill->user_claim_unsupported = 1; | ||
427 | |||
428 | wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); | ||
429 | wwan_rfkill->name = "hp-wwan"; | ||
430 | wwan_rfkill->state = hp_wmi_wwan_state(); | ||
431 | wwan_rfkill->toggle_radio = hp_wmi_wwan_set; | ||
432 | wwan_rfkill->user_claim_unsupported = 1; | ||
433 | |||
434 | rfkill_register(wifi_rfkill); | ||
435 | rfkill_register(bluetooth_rfkill); | ||
436 | rfkill_register(wwan_rfkill); | ||
437 | |||
438 | return 0; | ||
439 | add_sysfs_error: | ||
440 | cleanup_sysfs(device); | ||
441 | return err; | ||
442 | } | ||
443 | |||
444 | static int __exit hp_wmi_bios_remove(struct platform_device *device) | ||
445 | { | ||
446 | cleanup_sysfs(device); | ||
447 | |||
448 | rfkill_unregister(wifi_rfkill); | ||
449 | rfkill_unregister(bluetooth_rfkill); | ||
450 | rfkill_unregister(wwan_rfkill); | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | static int __init hp_wmi_init(void) | ||
456 | { | ||
457 | int err; | ||
458 | |||
459 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | ||
460 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | ||
461 | hp_wmi_notify, NULL); | ||
462 | if (!err) | ||
463 | hp_wmi_input_setup(); | ||
464 | } | ||
465 | |||
466 | if (wmi_has_guid(HPWMI_BIOS_GUID)) { | ||
467 | err = platform_driver_register(&hp_wmi_driver); | ||
468 | if (err) | ||
469 | return 0; | ||
470 | hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); | ||
471 | if (!hp_wmi_platform_dev) { | ||
472 | platform_driver_unregister(&hp_wmi_driver); | ||
473 | return 0; | ||
474 | } | ||
475 | platform_device_add(hp_wmi_platform_dev); | ||
476 | } | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static void __exit hp_wmi_exit(void) | ||
482 | { | ||
483 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | ||
484 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
485 | input_unregister_device(hp_wmi_input_dev); | ||
486 | } | ||
487 | if (hp_wmi_platform_dev) { | ||
488 | platform_device_del(hp_wmi_platform_dev); | ||
489 | platform_driver_unregister(&hp_wmi_driver); | ||
490 | } | ||
491 | } | ||
492 | |||
493 | module_init(hp_wmi_init); | ||
494 | module_exit(hp_wmi_exit); | ||
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 4ce3bdc2f959..daf585689ce3 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c | |||
@@ -563,6 +563,6 @@ module_init(phantom_init); | |||
563 | module_exit(phantom_exit); | 563 | module_exit(phantom_exit); |
564 | 564 | ||
565 | MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); | 565 | MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); |
566 | MODULE_DESCRIPTION("Sensable Phantom driver"); | 566 | MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)"); |
567 | MODULE_LICENSE("GPL"); | 567 | MODULE_LICENSE("GPL"); |
568 | MODULE_VERSION(PHANTOM_VERSION); | 568 | MODULE_VERSION(PHANTOM_VERSION); |
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index b5969298f3d3..d3eb7903c346 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.20" | 24 | #define TPACPI_VERSION "0.21" |
25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
26 | 26 | ||
27 | /* | 27 | /* |
@@ -68,6 +68,7 @@ | |||
68 | #include <linux/hwmon-sysfs.h> | 68 | #include <linux/hwmon-sysfs.h> |
69 | #include <linux/input.h> | 69 | #include <linux/input.h> |
70 | #include <linux/leds.h> | 70 | #include <linux/leds.h> |
71 | #include <linux/rfkill.h> | ||
71 | #include <asm/uaccess.h> | 72 | #include <asm/uaccess.h> |
72 | 73 | ||
73 | #include <linux/dmi.h> | 74 | #include <linux/dmi.h> |
@@ -144,6 +145,12 @@ enum { | |||
144 | 145 | ||
145 | #define TPACPI_MAX_ACPI_ARGS 3 | 146 | #define TPACPI_MAX_ACPI_ARGS 3 |
146 | 147 | ||
148 | /* rfkill switches */ | ||
149 | enum { | ||
150 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
151 | TPACPI_RFK_WWAN_SW_ID, | ||
152 | }; | ||
153 | |||
147 | /* Debugging */ | 154 | /* Debugging */ |
148 | #define TPACPI_LOG TPACPI_FILE ": " | 155 | #define TPACPI_LOG TPACPI_FILE ": " |
149 | #define TPACPI_ERR KERN_ERR TPACPI_LOG | 156 | #define TPACPI_ERR KERN_ERR TPACPI_LOG |
@@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
905 | return 0; | 912 | return 0; |
906 | } | 913 | } |
907 | 914 | ||
915 | static int __init tpacpi_new_rfkill(const unsigned int id, | ||
916 | struct rfkill **rfk, | ||
917 | const enum rfkill_type rfktype, | ||
918 | const char *name, | ||
919 | int (*toggle_radio)(void *, enum rfkill_state), | ||
920 | int (*get_state)(void *, enum rfkill_state *)) | ||
921 | { | ||
922 | int res; | ||
923 | enum rfkill_state initial_state; | ||
924 | |||
925 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | ||
926 | if (!*rfk) { | ||
927 | printk(TPACPI_ERR | ||
928 | "failed to allocate memory for rfkill class\n"); | ||
929 | return -ENOMEM; | ||
930 | } | ||
931 | |||
932 | (*rfk)->name = name; | ||
933 | (*rfk)->get_state = get_state; | ||
934 | (*rfk)->toggle_radio = toggle_radio; | ||
935 | |||
936 | if (!get_state(NULL, &initial_state)) | ||
937 | (*rfk)->state = initial_state; | ||
938 | |||
939 | res = rfkill_register(*rfk); | ||
940 | if (res < 0) { | ||
941 | printk(TPACPI_ERR | ||
942 | "failed to register %s rfkill switch: %d\n", | ||
943 | name, res); | ||
944 | rfkill_free(*rfk); | ||
945 | *rfk = NULL; | ||
946 | return res; | ||
947 | } | ||
948 | |||
949 | return 0; | ||
950 | } | ||
951 | |||
908 | /************************************************************************* | 952 | /************************************************************************* |
909 | * thinkpad-acpi driver attributes | 953 | * thinkpad-acpi driver attributes |
910 | */ | 954 | */ |
@@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status) | |||
1285 | return 0; | 1329 | return 0; |
1286 | } | 1330 | } |
1287 | 1331 | ||
1288 | static void tpacpi_input_send_radiosw(void) | ||
1289 | { | ||
1290 | int wlsw; | ||
1291 | |||
1292 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | ||
1293 | mutex_lock(&tpacpi_inputdev_send_mutex); | ||
1294 | |||
1295 | input_report_switch(tpacpi_inputdev, | ||
1296 | SW_RFKILL_ALL, !!wlsw); | ||
1297 | input_sync(tpacpi_inputdev); | ||
1298 | |||
1299 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
1300 | } | ||
1301 | } | ||
1302 | |||
1303 | static void tpacpi_input_send_tabletsw(void) | 1332 | static void tpacpi_input_send_tabletsw(void) |
1304 | { | 1333 | { |
1305 | int state; | 1334 | int state; |
@@ -1921,6 +1950,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
1921 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | 1950 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, |
1922 | }; | 1951 | }; |
1923 | 1952 | ||
1953 | static void bluetooth_update_rfk(void); | ||
1954 | static void wan_update_rfk(void); | ||
1955 | static void tpacpi_send_radiosw_update(void) | ||
1956 | { | ||
1957 | int wlsw; | ||
1958 | |||
1959 | /* Sync these BEFORE sending any rfkill events */ | ||
1960 | if (tp_features.bluetooth) | ||
1961 | bluetooth_update_rfk(); | ||
1962 | if (tp_features.wan) | ||
1963 | wan_update_rfk(); | ||
1964 | |||
1965 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | ||
1966 | mutex_lock(&tpacpi_inputdev_send_mutex); | ||
1967 | |||
1968 | input_report_switch(tpacpi_inputdev, | ||
1969 | SW_RFKILL_ALL, !!wlsw); | ||
1970 | input_sync(tpacpi_inputdev); | ||
1971 | |||
1972 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
1973 | } | ||
1974 | hotkey_radio_sw_notify_change(); | ||
1975 | } | ||
1976 | |||
1924 | static void hotkey_exit(void) | 1977 | static void hotkey_exit(void) |
1925 | { | 1978 | { |
1926 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | 1979 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL |
@@ -2167,9 +2220,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2167 | printk(TPACPI_INFO | 2220 | printk(TPACPI_INFO |
2168 | "radio switch found; radios are %s\n", | 2221 | "radio switch found; radios are %s\n", |
2169 | enabled(status, 0)); | 2222 | enabled(status, 0)); |
2223 | } | ||
2224 | if (tp_features.hotkey_wlsw) | ||
2170 | res = add_to_attr_set(hotkey_dev_attributes, | 2225 | res = add_to_attr_set(hotkey_dev_attributes, |
2171 | &dev_attr_hotkey_radio_sw.attr); | 2226 | &dev_attr_hotkey_radio_sw.attr); |
2172 | } | ||
2173 | 2227 | ||
2174 | /* For X41t, X60t, X61t Tablets... */ | 2228 | /* For X41t, X60t, X61t Tablets... */ |
2175 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 2229 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
@@ -2287,7 +2341,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2287 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 2341 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
2288 | 2342 | ||
2289 | hotkey_poll_setup_safe(1); | 2343 | hotkey_poll_setup_safe(1); |
2290 | tpacpi_input_send_radiosw(); | 2344 | tpacpi_send_radiosw_update(); |
2291 | tpacpi_input_send_tabletsw(); | 2345 | tpacpi_input_send_tabletsw(); |
2292 | 2346 | ||
2293 | return 0; | 2347 | return 0; |
@@ -2419,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2419 | case 7: | 2473 | case 7: |
2420 | /* 0x7000-0x7FFF: misc */ | 2474 | /* 0x7000-0x7FFF: misc */ |
2421 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { | 2475 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { |
2422 | tpacpi_input_send_radiosw(); | 2476 | tpacpi_send_radiosw_update(); |
2423 | hotkey_radio_sw_notify_change(); | ||
2424 | send_acpi_ev = 0; | 2477 | send_acpi_ev = 0; |
2425 | break; | 2478 | break; |
2426 | } | 2479 | } |
@@ -2463,8 +2516,7 @@ static void hotkey_resume(void) | |||
2463 | printk(TPACPI_ERR | 2516 | printk(TPACPI_ERR |
2464 | "error while trying to read hot key mask " | 2517 | "error while trying to read hot key mask " |
2465 | "from firmware\n"); | 2518 | "from firmware\n"); |
2466 | tpacpi_input_send_radiosw(); | 2519 | tpacpi_send_radiosw_update(); |
2467 | hotkey_radio_sw_notify_change(); | ||
2468 | hotkey_tablet_mode_notify_change(); | 2520 | hotkey_tablet_mode_notify_change(); |
2469 | hotkey_wakeup_reason_notify_change(); | 2521 | hotkey_wakeup_reason_notify_change(); |
2470 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2522 | hotkey_wakeup_hotunplug_complete_notify_change(); |
@@ -2581,8 +2633,66 @@ enum { | |||
2581 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ | 2633 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ |
2582 | }; | 2634 | }; |
2583 | 2635 | ||
2584 | static int bluetooth_get_radiosw(void); | 2636 | static struct rfkill *tpacpi_bluetooth_rfkill; |
2585 | static int bluetooth_set_radiosw(int radio_on); | 2637 | |
2638 | static int bluetooth_get_radiosw(void) | ||
2639 | { | ||
2640 | int status; | ||
2641 | |||
2642 | if (!tp_features.bluetooth) | ||
2643 | return -ENODEV; | ||
2644 | |||
2645 | /* WLSW overrides bluetooth in firmware/hardware, reflect that */ | ||
2646 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
2647 | return RFKILL_STATE_HARD_BLOCKED; | ||
2648 | |||
2649 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
2650 | return -EIO; | ||
2651 | |||
2652 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? | ||
2653 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
2654 | } | ||
2655 | |||
2656 | static void bluetooth_update_rfk(void) | ||
2657 | { | ||
2658 | int status; | ||
2659 | |||
2660 | if (!tpacpi_bluetooth_rfkill) | ||
2661 | return; | ||
2662 | |||
2663 | status = bluetooth_get_radiosw(); | ||
2664 | if (status < 0) | ||
2665 | return; | ||
2666 | rfkill_force_state(tpacpi_bluetooth_rfkill, status); | ||
2667 | } | ||
2668 | |||
2669 | static int bluetooth_set_radiosw(int radio_on, int update_rfk) | ||
2670 | { | ||
2671 | int status; | ||
2672 | |||
2673 | if (!tp_features.bluetooth) | ||
2674 | return -ENODEV; | ||
2675 | |||
2676 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
2677 | * reason to risk weird behaviour. */ | ||
2678 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
2679 | && radio_on) | ||
2680 | return -EPERM; | ||
2681 | |||
2682 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
2683 | return -EIO; | ||
2684 | if (radio_on) | ||
2685 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | ||
2686 | else | ||
2687 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | ||
2688 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | ||
2689 | return -EIO; | ||
2690 | |||
2691 | if (update_rfk) | ||
2692 | bluetooth_update_rfk(); | ||
2693 | |||
2694 | return 0; | ||
2695 | } | ||
2586 | 2696 | ||
2587 | /* sysfs bluetooth enable ---------------------------------------------- */ | 2697 | /* sysfs bluetooth enable ---------------------------------------------- */ |
2588 | static ssize_t bluetooth_enable_show(struct device *dev, | 2698 | static ssize_t bluetooth_enable_show(struct device *dev, |
@@ -2595,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev, | |||
2595 | if (status < 0) | 2705 | if (status < 0) |
2596 | return status; | 2706 | return status; |
2597 | 2707 | ||
2598 | return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); | 2708 | return snprintf(buf, PAGE_SIZE, "%d\n", |
2709 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
2599 | } | 2710 | } |
2600 | 2711 | ||
2601 | static ssize_t bluetooth_enable_store(struct device *dev, | 2712 | static ssize_t bluetooth_enable_store(struct device *dev, |
@@ -2608,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev, | |||
2608 | if (parse_strtoul(buf, 1, &t)) | 2719 | if (parse_strtoul(buf, 1, &t)) |
2609 | return -EINVAL; | 2720 | return -EINVAL; |
2610 | 2721 | ||
2611 | res = bluetooth_set_radiosw(t); | 2722 | res = bluetooth_set_radiosw(t, 1); |
2612 | 2723 | ||
2613 | return (res) ? res : count; | 2724 | return (res) ? res : count; |
2614 | } | 2725 | } |
@@ -2628,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = { | |||
2628 | .attrs = bluetooth_attributes, | 2739 | .attrs = bluetooth_attributes, |
2629 | }; | 2740 | }; |
2630 | 2741 | ||
2742 | static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) | ||
2743 | { | ||
2744 | int bts = bluetooth_get_radiosw(); | ||
2745 | |||
2746 | if (bts < 0) | ||
2747 | return bts; | ||
2748 | |||
2749 | *state = bts; | ||
2750 | return 0; | ||
2751 | } | ||
2752 | |||
2753 | static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | ||
2754 | { | ||
2755 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
2756 | } | ||
2757 | |||
2758 | static void bluetooth_exit(void) | ||
2759 | { | ||
2760 | if (tpacpi_bluetooth_rfkill) | ||
2761 | rfkill_unregister(tpacpi_bluetooth_rfkill); | ||
2762 | |||
2763 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2764 | &bluetooth_attr_group); | ||
2765 | } | ||
2766 | |||
2631 | static int __init bluetooth_init(struct ibm_init_struct *iibm) | 2767 | static int __init bluetooth_init(struct ibm_init_struct *iibm) |
2632 | { | 2768 | { |
2633 | int res; | 2769 | int res; |
@@ -2646,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
2646 | str_supported(tp_features.bluetooth), | 2782 | str_supported(tp_features.bluetooth), |
2647 | status); | 2783 | status); |
2648 | 2784 | ||
2649 | if (tp_features.bluetooth) { | 2785 | if (tp_features.bluetooth && |
2650 | if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { | 2786 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { |
2651 | /* no bluetooth hardware present in system */ | 2787 | /* no bluetooth hardware present in system */ |
2652 | tp_features.bluetooth = 0; | 2788 | tp_features.bluetooth = 0; |
2653 | dbg_printk(TPACPI_DBG_INIT, | 2789 | dbg_printk(TPACPI_DBG_INIT, |
2654 | "bluetooth hardware not installed\n"); | 2790 | "bluetooth hardware not installed\n"); |
2655 | } else { | ||
2656 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
2657 | &bluetooth_attr_group); | ||
2658 | if (res) | ||
2659 | return res; | ||
2660 | } | ||
2661 | } | 2791 | } |
2662 | 2792 | ||
2663 | return (tp_features.bluetooth)? 0 : 1; | ||
2664 | } | ||
2665 | |||
2666 | static void bluetooth_exit(void) | ||
2667 | { | ||
2668 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2669 | &bluetooth_attr_group); | ||
2670 | } | ||
2671 | |||
2672 | static int bluetooth_get_radiosw(void) | ||
2673 | { | ||
2674 | int status; | ||
2675 | |||
2676 | if (!tp_features.bluetooth) | 2793 | if (!tp_features.bluetooth) |
2677 | return -ENODEV; | 2794 | return 1; |
2678 | |||
2679 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
2680 | return -EIO; | ||
2681 | |||
2682 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); | ||
2683 | } | ||
2684 | |||
2685 | static int bluetooth_set_radiosw(int radio_on) | ||
2686 | { | ||
2687 | int status; | ||
2688 | 2795 | ||
2689 | if (!tp_features.bluetooth) | 2796 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, |
2690 | return -ENODEV; | 2797 | &bluetooth_attr_group); |
2798 | if (res) | ||
2799 | return res; | ||
2691 | 2800 | ||
2692 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 2801 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, |
2693 | return -EIO; | 2802 | &tpacpi_bluetooth_rfkill, |
2694 | if (radio_on) | 2803 | RFKILL_TYPE_BLUETOOTH, |
2695 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | 2804 | "tpacpi_bluetooth_sw", |
2696 | else | 2805 | tpacpi_bluetooth_rfk_set, |
2697 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | 2806 | tpacpi_bluetooth_rfk_get); |
2698 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 2807 | if (res) { |
2699 | return -EIO; | 2808 | bluetooth_exit(); |
2809 | return res; | ||
2810 | } | ||
2700 | 2811 | ||
2701 | return 0; | 2812 | return 0; |
2702 | } | 2813 | } |
@@ -2711,7 +2822,8 @@ static int bluetooth_read(char *p) | |||
2711 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 2822 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
2712 | else { | 2823 | else { |
2713 | len += sprintf(p + len, "status:\t\t%s\n", | 2824 | len += sprintf(p + len, "status:\t\t%s\n", |
2714 | (status)? "enabled" : "disabled"); | 2825 | (status == RFKILL_STATE_UNBLOCKED) ? |
2826 | "enabled" : "disabled"); | ||
2715 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 2827 | len += sprintf(p + len, "commands:\tenable, disable\n"); |
2716 | } | 2828 | } |
2717 | 2829 | ||
@@ -2727,9 +2839,9 @@ static int bluetooth_write(char *buf) | |||
2727 | 2839 | ||
2728 | while ((cmd = next_cmd(&buf))) { | 2840 | while ((cmd = next_cmd(&buf))) { |
2729 | if (strlencmp(cmd, "enable") == 0) { | 2841 | if (strlencmp(cmd, "enable") == 0) { |
2730 | bluetooth_set_radiosw(1); | 2842 | bluetooth_set_radiosw(1, 1); |
2731 | } else if (strlencmp(cmd, "disable") == 0) { | 2843 | } else if (strlencmp(cmd, "disable") == 0) { |
2732 | bluetooth_set_radiosw(0); | 2844 | bluetooth_set_radiosw(0, 1); |
2733 | } else | 2845 | } else |
2734 | return -EINVAL; | 2846 | return -EINVAL; |
2735 | } | 2847 | } |
@@ -2755,8 +2867,66 @@ enum { | |||
2755 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ | 2867 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ |
2756 | }; | 2868 | }; |
2757 | 2869 | ||
2758 | static int wan_get_radiosw(void); | 2870 | static struct rfkill *tpacpi_wan_rfkill; |
2759 | static int wan_set_radiosw(int radio_on); | 2871 | |
2872 | static int wan_get_radiosw(void) | ||
2873 | { | ||
2874 | int status; | ||
2875 | |||
2876 | if (!tp_features.wan) | ||
2877 | return -ENODEV; | ||
2878 | |||
2879 | /* WLSW overrides WWAN in firmware/hardware, reflect that */ | ||
2880 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
2881 | return RFKILL_STATE_HARD_BLOCKED; | ||
2882 | |||
2883 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
2884 | return -EIO; | ||
2885 | |||
2886 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? | ||
2887 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
2888 | } | ||
2889 | |||
2890 | static void wan_update_rfk(void) | ||
2891 | { | ||
2892 | int status; | ||
2893 | |||
2894 | if (!tpacpi_wan_rfkill) | ||
2895 | return; | ||
2896 | |||
2897 | status = wan_get_radiosw(); | ||
2898 | if (status < 0) | ||
2899 | return; | ||
2900 | rfkill_force_state(tpacpi_wan_rfkill, status); | ||
2901 | } | ||
2902 | |||
2903 | static int wan_set_radiosw(int radio_on, int update_rfk) | ||
2904 | { | ||
2905 | int status; | ||
2906 | |||
2907 | if (!tp_features.wan) | ||
2908 | return -ENODEV; | ||
2909 | |||
2910 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
2911 | * reason to risk weird behaviour. */ | ||
2912 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
2913 | && radio_on) | ||
2914 | return -EPERM; | ||
2915 | |||
2916 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
2917 | return -EIO; | ||
2918 | if (radio_on) | ||
2919 | status |= TP_ACPI_WANCARD_RADIOSSW; | ||
2920 | else | ||
2921 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | ||
2922 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | ||
2923 | return -EIO; | ||
2924 | |||
2925 | if (update_rfk) | ||
2926 | wan_update_rfk(); | ||
2927 | |||
2928 | return 0; | ||
2929 | } | ||
2760 | 2930 | ||
2761 | /* sysfs wan enable ---------------------------------------------------- */ | 2931 | /* sysfs wan enable ---------------------------------------------------- */ |
2762 | static ssize_t wan_enable_show(struct device *dev, | 2932 | static ssize_t wan_enable_show(struct device *dev, |
@@ -2769,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev, | |||
2769 | if (status < 0) | 2939 | if (status < 0) |
2770 | return status; | 2940 | return status; |
2771 | 2941 | ||
2772 | return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); | 2942 | return snprintf(buf, PAGE_SIZE, "%d\n", |
2943 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
2773 | } | 2944 | } |
2774 | 2945 | ||
2775 | static ssize_t wan_enable_store(struct device *dev, | 2946 | static ssize_t wan_enable_store(struct device *dev, |
@@ -2782,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev, | |||
2782 | if (parse_strtoul(buf, 1, &t)) | 2953 | if (parse_strtoul(buf, 1, &t)) |
2783 | return -EINVAL; | 2954 | return -EINVAL; |
2784 | 2955 | ||
2785 | res = wan_set_radiosw(t); | 2956 | res = wan_set_radiosw(t, 1); |
2786 | 2957 | ||
2787 | return (res) ? res : count; | 2958 | return (res) ? res : count; |
2788 | } | 2959 | } |
@@ -2802,6 +2973,31 @@ static const struct attribute_group wan_attr_group = { | |||
2802 | .attrs = wan_attributes, | 2973 | .attrs = wan_attributes, |
2803 | }; | 2974 | }; |
2804 | 2975 | ||
2976 | static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) | ||
2977 | { | ||
2978 | int wans = wan_get_radiosw(); | ||
2979 | |||
2980 | if (wans < 0) | ||
2981 | return wans; | ||
2982 | |||
2983 | *state = wans; | ||
2984 | return 0; | ||
2985 | } | ||
2986 | |||
2987 | static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | ||
2988 | { | ||
2989 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
2990 | } | ||
2991 | |||
2992 | static void wan_exit(void) | ||
2993 | { | ||
2994 | if (tpacpi_wan_rfkill) | ||
2995 | rfkill_unregister(tpacpi_wan_rfkill); | ||
2996 | |||
2997 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2998 | &wan_attr_group); | ||
2999 | } | ||
3000 | |||
2805 | static int __init wan_init(struct ibm_init_struct *iibm) | 3001 | static int __init wan_init(struct ibm_init_struct *iibm) |
2806 | { | 3002 | { |
2807 | int res; | 3003 | int res; |
@@ -2818,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
2818 | str_supported(tp_features.wan), | 3014 | str_supported(tp_features.wan), |
2819 | status); | 3015 | status); |
2820 | 3016 | ||
2821 | if (tp_features.wan) { | 3017 | if (tp_features.wan && |
2822 | if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { | 3018 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { |
2823 | /* no wan hardware present in system */ | 3019 | /* no wan hardware present in system */ |
2824 | tp_features.wan = 0; | 3020 | tp_features.wan = 0; |
2825 | dbg_printk(TPACPI_DBG_INIT, | 3021 | dbg_printk(TPACPI_DBG_INIT, |
2826 | "wan hardware not installed\n"); | 3022 | "wan hardware not installed\n"); |
2827 | } else { | ||
2828 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
2829 | &wan_attr_group); | ||
2830 | if (res) | ||
2831 | return res; | ||
2832 | } | ||
2833 | } | 3023 | } |
2834 | 3024 | ||
2835 | return (tp_features.wan)? 0 : 1; | ||
2836 | } | ||
2837 | |||
2838 | static void wan_exit(void) | ||
2839 | { | ||
2840 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2841 | &wan_attr_group); | ||
2842 | } | ||
2843 | |||
2844 | static int wan_get_radiosw(void) | ||
2845 | { | ||
2846 | int status; | ||
2847 | |||
2848 | if (!tp_features.wan) | 3025 | if (!tp_features.wan) |
2849 | return -ENODEV; | 3026 | return 1; |
2850 | |||
2851 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
2852 | return -EIO; | ||
2853 | |||
2854 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); | ||
2855 | } | ||
2856 | |||
2857 | static int wan_set_radiosw(int radio_on) | ||
2858 | { | ||
2859 | int status; | ||
2860 | 3027 | ||
2861 | if (!tp_features.wan) | 3028 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, |
2862 | return -ENODEV; | 3029 | &wan_attr_group); |
3030 | if (res) | ||
3031 | return res; | ||
2863 | 3032 | ||
2864 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3033 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, |
2865 | return -EIO; | 3034 | &tpacpi_wan_rfkill, |
2866 | if (radio_on) | 3035 | RFKILL_TYPE_WWAN, |
2867 | status |= TP_ACPI_WANCARD_RADIOSSW; | 3036 | "tpacpi_wwan_sw", |
2868 | else | 3037 | tpacpi_wan_rfk_set, |
2869 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | 3038 | tpacpi_wan_rfk_get); |
2870 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3039 | if (res) { |
2871 | return -EIO; | 3040 | wan_exit(); |
3041 | return res; | ||
3042 | } | ||
2872 | 3043 | ||
2873 | return 0; | 3044 | return 0; |
2874 | } | 3045 | } |
@@ -2883,7 +3054,8 @@ static int wan_read(char *p) | |||
2883 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3054 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
2884 | else { | 3055 | else { |
2885 | len += sprintf(p + len, "status:\t\t%s\n", | 3056 | len += sprintf(p + len, "status:\t\t%s\n", |
2886 | (status)? "enabled" : "disabled"); | 3057 | (status == RFKILL_STATE_UNBLOCKED) ? |
3058 | "enabled" : "disabled"); | ||
2887 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 3059 | len += sprintf(p + len, "commands:\tenable, disable\n"); |
2888 | } | 3060 | } |
2889 | 3061 | ||
@@ -2899,9 +3071,9 @@ static int wan_write(char *buf) | |||
2899 | 3071 | ||
2900 | while ((cmd = next_cmd(&buf))) { | 3072 | while ((cmd = next_cmd(&buf))) { |
2901 | if (strlencmp(cmd, "enable") == 0) { | 3073 | if (strlencmp(cmd, "enable") == 0) { |
2902 | wan_set_radiosw(1); | 3074 | wan_set_radiosw(1, 1); |
2903 | } else if (strlencmp(cmd, "disable") == 0) { | 3075 | } else if (strlencmp(cmd, "disable") == 0) { |
2904 | wan_set_radiosw(0); | 3076 | wan_set_radiosw(0, 1); |
2905 | } else | 3077 | } else |
2906 | return -EINVAL; | 3078 | return -EINVAL; |
2907 | } | 3079 | } |
@@ -6168,13 +6340,18 @@ err_out: | |||
6168 | 6340 | ||
6169 | /* Probing */ | 6341 | /* Probing */ |
6170 | 6342 | ||
6171 | static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | 6343 | /* returns 0 - probe ok, or < 0 - probe error. |
6344 | * Probe ok doesn't mean thinkpad found. | ||
6345 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ | ||
6346 | static int __must_check __init get_thinkpad_model_data( | ||
6347 | struct thinkpad_id_data *tp) | ||
6172 | { | 6348 | { |
6173 | const struct dmi_device *dev = NULL; | 6349 | const struct dmi_device *dev = NULL; |
6174 | char ec_fw_string[18]; | 6350 | char ec_fw_string[18]; |
6351 | char const *s; | ||
6175 | 6352 | ||
6176 | if (!tp) | 6353 | if (!tp) |
6177 | return; | 6354 | return -EINVAL; |
6178 | 6355 | ||
6179 | memset(tp, 0, sizeof(*tp)); | 6356 | memset(tp, 0, sizeof(*tp)); |
6180 | 6357 | ||
@@ -6183,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
6183 | else if (dmi_name_in_vendors("LENOVO")) | 6360 | else if (dmi_name_in_vendors("LENOVO")) |
6184 | tp->vendor = PCI_VENDOR_ID_LENOVO; | 6361 | tp->vendor = PCI_VENDOR_ID_LENOVO; |
6185 | else | 6362 | else |
6186 | return; | 6363 | return 0; |
6187 | 6364 | ||
6188 | tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), | 6365 | s = dmi_get_system_info(DMI_BIOS_VERSION); |
6189 | GFP_KERNEL); | 6366 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); |
6367 | if (s && !tp->bios_version_str) | ||
6368 | return -ENOMEM; | ||
6190 | if (!tp->bios_version_str) | 6369 | if (!tp->bios_version_str) |
6191 | return; | 6370 | return 0; |
6192 | tp->bios_model = tp->bios_version_str[0] | 6371 | tp->bios_model = tp->bios_version_str[0] |
6193 | | (tp->bios_version_str[1] << 8); | 6372 | | (tp->bios_version_str[1] << 8); |
6194 | 6373 | ||
@@ -6207,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
6207 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; | 6386 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; |
6208 | 6387 | ||
6209 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); | 6388 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
6389 | if (!tp->ec_version_str) | ||
6390 | return -ENOMEM; | ||
6210 | tp->ec_model = ec_fw_string[0] | 6391 | tp->ec_model = ec_fw_string[0] |
6211 | | (ec_fw_string[1] << 8); | 6392 | | (ec_fw_string[1] << 8); |
6212 | break; | 6393 | break; |
6213 | } | 6394 | } |
6214 | } | 6395 | } |
6215 | 6396 | ||
6216 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), | 6397 | s = dmi_get_system_info(DMI_PRODUCT_VERSION); |
6217 | GFP_KERNEL); | 6398 | if (s && !strnicmp(s, "ThinkPad", 8)) { |
6218 | if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { | 6399 | tp->model_str = kstrdup(s, GFP_KERNEL); |
6219 | kfree(tp->model_str); | 6400 | if (!tp->model_str) |
6220 | tp->model_str = NULL; | 6401 | return -ENOMEM; |
6221 | } | 6402 | } |
6222 | 6403 | ||
6223 | tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), | 6404 | s = dmi_get_system_info(DMI_PRODUCT_NAME); |
6224 | GFP_KERNEL); | 6405 | tp->nummodel_str = kstrdup(s, GFP_KERNEL); |
6406 | if (s && !tp->nummodel_str) | ||
6407 | return -ENOMEM; | ||
6408 | |||
6409 | return 0; | ||
6225 | } | 6410 | } |
6226 | 6411 | ||
6227 | static int __init probe_for_thinkpad(void) | 6412 | static int __init probe_for_thinkpad(void) |
@@ -6484,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void) | |||
6484 | 6669 | ||
6485 | /* Driver-level probe */ | 6670 | /* Driver-level probe */ |
6486 | 6671 | ||
6487 | get_thinkpad_model_data(&thinkpad_id); | 6672 | ret = get_thinkpad_model_data(&thinkpad_id); |
6673 | if (ret) { | ||
6674 | printk(TPACPI_ERR | ||
6675 | "unable to get DMI data: %d\n", ret); | ||
6676 | thinkpad_acpi_module_exit(); | ||
6677 | return ret; | ||
6678 | } | ||
6488 | ret = probe_for_thinkpad(); | 6679 | ret = probe_for_thinkpad(); |
6489 | if (ret) { | 6680 | if (ret) { |
6490 | thinkpad_acpi_module_exit(); | 6681 | thinkpad_acpi_module_exit(); |