aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2008-12-11 14:37:26 -0500
committerLen Brown <len.brown@intel.com>2008-12-19 04:42:33 -0500
commitb4f9fe12157a33351d0df78e925dcacd13252783 (patch)
tree611d04e71e307c1e6fcfe0d71fe3c3e5be29e8a4 /drivers/platform
parent41b16dce390510f550a4d2b12b98e0258bbed6e2 (diff)
ACPI: move wmi, asus_acpi, toshiba_acpi to drivers/platform/x86
These are platform specific drivers that happen to use ACPI, while drivers/acpi/ is for code that implements ACPI itself. Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig85
-rw-r--r--drivers/platform/x86/Makefile3
-rw-r--r--drivers/platform/x86/asus_acpi.c1460
-rw-r--r--drivers/platform/x86/toshiba_acpi.c863
-rw-r--r--drivers/platform/x86/wmi.c747
5 files changed, 3158 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 0a9a5b9440af..31f3ce2ac01c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -287,4 +287,89 @@ config EEEPC_LAPTOP
287 287
288 If you have an Eee PC laptop, say Y or M here. 288 If you have an Eee PC laptop, say Y or M here.
289 289
290
291config ACPI_WMI
292 tristate "WMI (EXPERIMENTAL)"
293 depends on ACPI
294 depends on EXPERIMENTAL
295 help
296 This driver adds support for the ACPI-WMI (Windows Management
297 Instrumentation) mapper device (PNP0C14) found on some systems.
298
299 ACPI-WMI is a proprietary extension to ACPI to expose parts of the
300 ACPI firmware to userspace - this is done through various vendor
301 defined methods and data blocks in a PNP0C14 device, which are then
302 made available for userspace to call.
303
304 The implementation of this in Linux currently only exposes this to
305 other kernel space drivers.
306
307 This driver is a required dependency to build the firmware specific
308 drivers needed on many machines, including Acer and HP laptops.
309
310 It is safe to enable this driver even if your DSDT doesn't define
311 any ACPI-WMI devices.
312
313config ACPI_ASUS
314 tristate "ASUS/Medion Laptop Extras"
315 depends on ACPI
316 select BACKLIGHT_CLASS_DEVICE
317 ---help---
318 This driver provides support for extra features of ACPI-compatible
319 ASUS laptops. As some of Medion laptops are made by ASUS, it may also
320 support some Medion laptops (such as 9675 for example). It makes all
321 the extra buttons generate standard ACPI events that go through
322 /proc/acpi/events, and (on some models) adds support for changing the
323 display brightness and output, switching the LCD backlight on and off,
324 and most importantly, allows you to blink those fancy LEDs intended
325 for reporting mail and wireless status.
326
327 Note: display switching code is currently considered EXPERIMENTAL,
328 toying with these values may even lock your machine.
329
330 All settings are changed via /proc/acpi/asus directory entries. Owner
331 and group for these entries can be set with asus_uid and asus_gid
332 parameters.
333
334 More information and a userspace daemon for handling the extra buttons
335 at <http://sourceforge.net/projects/acpi4asus/>.
336
337 If you have an ACPI-compatible ASUS laptop, say Y or M here. This
338 driver is still under development, so if your laptop is unsupported or
339 something works not quite as expected, please use the mailing list
340 available on the above page (acpi4asus-user@lists.sourceforge.net).
341
342 NOTE: This driver is deprecated and will probably be removed soon,
343 use asus-laptop instead.
344
345config ACPI_TOSHIBA
346 tristate "Toshiba Laptop Extras"
347 depends on ACPI
348 depends on INPUT
349 select INPUT_POLLDEV
350 select NET
351 select RFKILL
352 select BACKLIGHT_CLASS_DEVICE
353 ---help---
354 This driver adds support for access to certain system settings
355 on "legacy free" Toshiba laptops. These laptops can be recognized by
356 their lack of a BIOS setup menu and APM support.
357
358 On these machines, all system configuration is handled through the
359 ACPI. This driver is required for access to controls not covered
360 by the general ACPI drivers, such as LCD brightness, video output,
361 etc.
362
363 This driver differs from the non-ACPI Toshiba laptop driver (located
364 under "Processor type and features") in several aspects.
365 Configuration is accessed by reading and writing text files in the
366 /proc tree instead of by program interface to /dev. Furthermore, no
367 power management functions are exposed, as those are handled by the
368 general ACPI drivers.
369
370 More information about this driver is available at
371 <http://memebeam.org/toys/ToshibaAcpiDriver>.
372
373 If you have a legacy free Toshiba laptop (such as the Libretto L1
374 series), say Y.
290endif # X86_PLATFORM_DEVICES 375endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 4d26b1bf22ae..1e9de2ae0de5 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -14,3 +14,6 @@ obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
14obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o 14obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
15obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o 15obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
16obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o 16obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
17obj-$(CONFIG_ACPI_WMI) += wmi.o
18obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
19obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
new file mode 100644
index 000000000000..1e74988c7b2d
--- /dev/null
+++ b/drivers/platform/x86/asus_acpi.c
@@ -0,0 +1,1460 @@
1/*
2 * asus_acpi.c - Asus Laptop ACPI Extras
3 *
4 *
5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 *
22 * The development page for this driver is located at
23 * http://sourceforge.net/projects/acpi4asus/
24 *
25 * Credits:
26 * Pontus Fuchs - Helper functions, cleanup
27 * Johann Wiesner - Small compile fixes
28 * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
29 * �ic Burghard - LED display support for W1N
30 *
31 */
32
33#include <linux/kernel.h>
34#include <linux/module.h>
35#include <linux/init.h>
36#include <linux/types.h>
37#include <linux/proc_fs.h>
38#include <linux/backlight.h>
39#include <acpi/acpi_drivers.h>
40#include <acpi/acpi_bus.h>
41#include <asm/uaccess.h>
42
43#define ASUS_ACPI_VERSION "0.30"
44
45#define PROC_ASUS "asus" /* The directory */
46#define PROC_MLED "mled"
47#define PROC_WLED "wled"
48#define PROC_TLED "tled"
49#define PROC_BT "bluetooth"
50#define PROC_LEDD "ledd"
51#define PROC_INFO "info"
52#define PROC_LCD "lcd"
53#define PROC_BRN "brn"
54#define PROC_DISP "disp"
55
56#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
57#define ACPI_HOTK_CLASS "hotkey"
58#define ACPI_HOTK_DEVICE_NAME "Hotkey"
59
60/*
61 * Some events we use, same for all Asus
62 */
63#define BR_UP 0x10
64#define BR_DOWN 0x20
65
66/*
67 * Flags for hotk status
68 */
69#define MLED_ON 0x01 /* Mail LED */
70#define WLED_ON 0x02 /* Wireless LED */
71#define TLED_ON 0x04 /* Touchpad LED */
72#define BT_ON 0x08 /* Internal Bluetooth */
73
74MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
75MODULE_DESCRIPTION(ACPI_HOTK_NAME);
76MODULE_LICENSE("GPL");
77
78static uid_t asus_uid;
79static gid_t asus_gid;
80module_param(asus_uid, uint, 0);
81MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus");
82module_param(asus_gid, uint, 0);
83MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus");
84
85/* For each model, all features implemented,
86 * those marked with R are relative to HOTK, A for absolute */
87struct model_data {
88 char *name; /* name of the laptop________________A */
89 char *mt_mled; /* method to handle mled_____________R */
90 char *mled_status; /* node to handle mled reading_______A */
91 char *mt_wled; /* method to handle wled_____________R */
92 char *wled_status; /* node to handle wled reading_______A */
93 char *mt_tled; /* method to handle tled_____________R */
94 char *tled_status; /* node to handle tled reading_______A */
95 char *mt_ledd; /* method to handle LED display______R */
96 char *mt_bt_switch; /* method to switch Bluetooth on/off_R */
97 char *bt_status; /* no model currently supports this__? */
98 char *mt_lcd_switch; /* method to turn LCD on/off_________A */
99 char *lcd_status; /* node to read LCD panel state______A */
100 char *brightness_up; /* method to set brightness up_______A */
101 char *brightness_down; /* method to set brightness down ____A */
102 char *brightness_set; /* method to set absolute brightness_R */
103 char *brightness_get; /* method to get absolute brightness_R */
104 char *brightness_status;/* node to get brightness____________A */
105 char *display_set; /* method to set video output________R */
106 char *display_get; /* method to get video output________R */
107};
108
109/*
110 * This is the main structure, we can use it to store anything interesting
111 * about the hotk device
112 */
113struct asus_hotk {
114 struct acpi_device *device; /* the device we are in */
115 acpi_handle handle; /* the handle of the hotk device */
116 char status; /* status of the hotk, for LEDs */
117 u32 ledd_status; /* status of the LED display */
118 struct model_data *methods; /* methods available on the laptop */
119 u8 brightness; /* brightness level */
120 enum {
121 A1x = 0, /* A1340D, A1300F */
122 A2x, /* A2500H */
123 A4G, /* A4700G */
124 D1x, /* D1 */
125 L2D, /* L2000D */
126 L3C, /* L3800C */
127 L3D, /* L3400D */
128 L3H, /* L3H, L2000E, L5D */
129 L4R, /* L4500R */
130 L5x, /* L5800C */
131 L8L, /* L8400L */
132 M1A, /* M1300A */
133 M2E, /* M2400E, L4400L */
134 M6N, /* M6800N, W3400N */
135 M6R, /* M6700R, A3000G */
136 P30, /* Samsung P30 */
137 S1x, /* S1300A, but also L1400B and M2400A (L84F) */
138 S2x, /* S200 (J1 reported), Victor MP-XP7210 */
139 W1N, /* W1000N */
140 W5A, /* W5A */
141 W3V, /* W3030V */
142 xxN, /* M2400N, M3700N, M5200N, M6800N,
143 S1300N, S5200N*/
144 A4S, /* Z81sp */
145 F3Sa, /* (Centrino) */
146 END_MODEL
147 } model; /* Models currently supported */
148 u16 event_count[128]; /* Count for each event TODO make this better */
149};
150
151/* Here we go */
152#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
153#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
154#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
155#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
156#define S1x_PREFIX "\\_SB.PCI0.PX40."
157#define S2x_PREFIX A1x_PREFIX
158#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
159
160static struct model_data model_conf[END_MODEL] = {
161 /*
162 * TODO I have seen a SWBX and AIBX method on some models, like L1400B,
163 * it seems to be a kind of switch, but what for ?
164 */
165
166 {
167 .name = "A1x",
168 .mt_mled = "MLED",
169 .mled_status = "\\MAIL",
170 .mt_lcd_switch = A1x_PREFIX "_Q10",
171 .lcd_status = "\\BKLI",
172 .brightness_up = A1x_PREFIX "_Q0E",
173 .brightness_down = A1x_PREFIX "_Q0F"},
174
175 {
176 .name = "A2x",
177 .mt_mled = "MLED",
178 .mt_wled = "WLED",
179 .wled_status = "\\SG66",
180 .mt_lcd_switch = "\\Q10",
181 .lcd_status = "\\BAOF",
182 .brightness_set = "SPLV",
183 .brightness_get = "GPLV",
184 .display_set = "SDSP",
185 .display_get = "\\INFB"},
186
187 {
188 .name = "A4G",
189 .mt_mled = "MLED",
190/* WLED present, but not controlled by ACPI */
191 .mt_lcd_switch = xxN_PREFIX "_Q10",
192 .brightness_set = "SPLV",
193 .brightness_get = "GPLV",
194 .display_set = "SDSP",
195 .display_get = "\\ADVG"},
196
197 {
198 .name = "D1x",
199 .mt_mled = "MLED",
200 .mt_lcd_switch = "\\Q0D",
201 .lcd_status = "\\GP11",
202 .brightness_up = "\\Q0C",
203 .brightness_down = "\\Q0B",
204 .brightness_status = "\\BLVL",
205 .display_set = "SDSP",
206 .display_get = "\\INFB"},
207
208 {
209 .name = "L2D",
210 .mt_mled = "MLED",
211 .mled_status = "\\SGP6",
212 .mt_wled = "WLED",
213 .wled_status = "\\RCP3",
214 .mt_lcd_switch = "\\Q10",
215 .lcd_status = "\\SGP0",
216 .brightness_up = "\\Q0E",
217 .brightness_down = "\\Q0F",
218 .display_set = "SDSP",
219 .display_get = "\\INFB"},
220
221 {
222 .name = "L3C",
223 .mt_mled = "MLED",
224 .mt_wled = "WLED",
225 .mt_lcd_switch = L3C_PREFIX "_Q10",
226 .lcd_status = "\\GL32",
227 .brightness_set = "SPLV",
228 .brightness_get = "GPLV",
229 .display_set = "SDSP",
230 .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"},
231
232 {
233 .name = "L3D",
234 .mt_mled = "MLED",
235 .mled_status = "\\MALD",
236 .mt_wled = "WLED",
237 .mt_lcd_switch = "\\Q10",
238 .lcd_status = "\\BKLG",
239 .brightness_set = "SPLV",
240 .brightness_get = "GPLV",
241 .display_set = "SDSP",
242 .display_get = "\\INFB"},
243
244 {
245 .name = "L3H",
246 .mt_mled = "MLED",
247 .mt_wled = "WLED",
248 .mt_lcd_switch = "EHK",
249 .lcd_status = "\\_SB.PCI0.PM.PBC",
250 .brightness_set = "SPLV",
251 .brightness_get = "GPLV",
252 .display_set = "SDSP",
253 .display_get = "\\INFB"},
254
255 {
256 .name = "L4R",
257 .mt_mled = "MLED",
258 .mt_wled = "WLED",
259 .wled_status = "\\_SB.PCI0.SBRG.SG13",
260 .mt_lcd_switch = xxN_PREFIX "_Q10",
261 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
262 .brightness_set = "SPLV",
263 .brightness_get = "GPLV",
264 .display_set = "SDSP",
265 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
266
267 {
268 .name = "L5x",
269 .mt_mled = "MLED",
270/* WLED present, but not controlled by ACPI */
271 .mt_tled = "TLED",
272 .mt_lcd_switch = "\\Q0D",
273 .lcd_status = "\\BAOF",
274 .brightness_set = "SPLV",
275 .brightness_get = "GPLV",
276 .display_set = "SDSP",
277 .display_get = "\\INFB"},
278
279 {
280 .name = "L8L"
281/* No features, but at least support the hotkeys */
282 },
283
284 {
285 .name = "M1A",
286 .mt_mled = "MLED",
287 .mt_lcd_switch = M1A_PREFIX "Q10",
288 .lcd_status = "\\PNOF",
289 .brightness_up = M1A_PREFIX "Q0E",
290 .brightness_down = M1A_PREFIX "Q0F",
291 .brightness_status = "\\BRIT",
292 .display_set = "SDSP",
293 .display_get = "\\INFB"},
294
295 {
296 .name = "M2E",
297 .mt_mled = "MLED",
298 .mt_wled = "WLED",
299 .mt_lcd_switch = "\\Q10",
300 .lcd_status = "\\GP06",
301 .brightness_set = "SPLV",
302 .brightness_get = "GPLV",
303 .display_set = "SDSP",
304 .display_get = "\\INFB"},
305
306 {
307 .name = "M6N",
308 .mt_mled = "MLED",
309 .mt_wled = "WLED",
310 .wled_status = "\\_SB.PCI0.SBRG.SG13",
311 .mt_lcd_switch = xxN_PREFIX "_Q10",
312 .lcd_status = "\\_SB.BKLT",
313 .brightness_set = "SPLV",
314 .brightness_get = "GPLV",
315 .display_set = "SDSP",
316 .display_get = "\\SSTE"},
317
318 {
319 .name = "M6R",
320 .mt_mled = "MLED",
321 .mt_wled = "WLED",
322 .mt_lcd_switch = xxN_PREFIX "_Q10",
323 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
324 .brightness_set = "SPLV",
325 .brightness_get = "GPLV",
326 .display_set = "SDSP",
327 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
328
329 {
330 .name = "P30",
331 .mt_wled = "WLED",
332 .mt_lcd_switch = P30_PREFIX "_Q0E",
333 .lcd_status = "\\BKLT",
334 .brightness_up = P30_PREFIX "_Q68",
335 .brightness_down = P30_PREFIX "_Q69",
336 .brightness_get = "GPLV",
337 .display_set = "SDSP",
338 .display_get = "\\DNXT"},
339
340 {
341 .name = "S1x",
342 .mt_mled = "MLED",
343 .mled_status = "\\EMLE",
344 .mt_wled = "WLED",
345 .mt_lcd_switch = S1x_PREFIX "Q10",
346 .lcd_status = "\\PNOF",
347 .brightness_set = "SPLV",
348 .brightness_get = "GPLV"},
349
350 {
351 .name = "S2x",
352 .mt_mled = "MLED",
353 .mled_status = "\\MAIL",
354 .mt_lcd_switch = S2x_PREFIX "_Q10",
355 .lcd_status = "\\BKLI",
356 .brightness_up = S2x_PREFIX "_Q0B",
357 .brightness_down = S2x_PREFIX "_Q0A"},
358
359 {
360 .name = "W1N",
361 .mt_mled = "MLED",
362 .mt_wled = "WLED",
363 .mt_ledd = "SLCM",
364 .mt_lcd_switch = xxN_PREFIX "_Q10",
365 .lcd_status = "\\BKLT",
366 .brightness_set = "SPLV",
367 .brightness_get = "GPLV",
368 .display_set = "SDSP",
369 .display_get = "\\ADVG"},
370
371 {
372 .name = "W5A",
373 .mt_bt_switch = "BLED",
374 .mt_wled = "WLED",
375 .mt_lcd_switch = xxN_PREFIX "_Q10",
376 .brightness_set = "SPLV",
377 .brightness_get = "GPLV",
378 .display_set = "SDSP",
379 .display_get = "\\ADVG"},
380
381 {
382 .name = "W3V",
383 .mt_mled = "MLED",
384 .mt_wled = "WLED",
385 .mt_lcd_switch = xxN_PREFIX "_Q10",
386 .lcd_status = "\\BKLT",
387 .brightness_set = "SPLV",
388 .brightness_get = "GPLV",
389 .display_set = "SDSP",
390 .display_get = "\\INFB"},
391
392 {
393 .name = "xxN",
394 .mt_mled = "MLED",
395/* WLED present, but not controlled by ACPI */
396 .mt_lcd_switch = xxN_PREFIX "_Q10",
397 .lcd_status = "\\BKLT",
398 .brightness_set = "SPLV",
399 .brightness_get = "GPLV",
400 .display_set = "SDSP",
401 .display_get = "\\ADVG"},
402
403 {
404 .name = "A4S",
405 .brightness_set = "SPLV",
406 .brightness_get = "GPLV",
407 .mt_bt_switch = "BLED",
408 .mt_wled = "WLED"
409 },
410
411 {
412 .name = "F3Sa",
413 .mt_bt_switch = "BLED",
414 .mt_wled = "WLED",
415 .mt_mled = "MLED",
416 .brightness_get = "GPLV",
417 .brightness_set = "SPLV",
418 .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10",
419 .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN",
420 .display_get = "\\ADVG",
421 .display_set = "SDSP",
422 },
423
424};
425
426/* procdir we use */
427static struct proc_dir_entry *asus_proc_dir;
428
429static struct backlight_device *asus_backlight_device;
430
431/*
432 * This header is made available to allow proper configuration given model,
433 * revision number , ... this info cannot go in struct asus_hotk because it is
434 * available before the hotk
435 */
436static struct acpi_table_header *asus_info;
437
438/* The actual device the driver binds to */
439static struct asus_hotk *hotk;
440
441/*
442 * The hotkey driver and autoloading declaration
443 */
444static int asus_hotk_add(struct acpi_device *device);
445static int asus_hotk_remove(struct acpi_device *device, int type);
446static const struct acpi_device_id asus_device_ids[] = {
447 {"ATK0100", 0},
448 {"", 0},
449};
450MODULE_DEVICE_TABLE(acpi, asus_device_ids);
451
452static struct acpi_driver asus_hotk_driver = {
453 .name = "asus_acpi",
454 .class = ACPI_HOTK_CLASS,
455 .ids = asus_device_ids,
456 .ops = {
457 .add = asus_hotk_add,
458 .remove = asus_hotk_remove,
459 },
460};
461
462/*
463 * This function evaluates an ACPI method, given an int as parameter, the
464 * method is searched within the scope of the handle, can be NULL. The output
465 * of the method is written is output, which can also be NULL
466 *
467 * returns 1 if write is successful, 0 else.
468 */
469static int write_acpi_int(acpi_handle handle, const char *method, int val,
470 struct acpi_buffer *output)
471{
472 struct acpi_object_list params; /* list of input parameters (int) */
473 union acpi_object in_obj; /* the only param we use */
474 acpi_status status;
475
476 params.count = 1;
477 params.pointer = &in_obj;
478 in_obj.type = ACPI_TYPE_INTEGER;
479 in_obj.integer.value = val;
480
481 status = acpi_evaluate_object(handle, (char *)method, &params, output);
482 return (status == AE_OK);
483}
484
485static int read_acpi_int(acpi_handle handle, const char *method, int *val)
486{
487 struct acpi_buffer output;
488 union acpi_object out_obj;
489 acpi_status status;
490
491 output.length = sizeof(out_obj);
492 output.pointer = &out_obj;
493
494 status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
495 *val = out_obj.integer.value;
496 return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
497}
498
499/*
500 * We write our info in page, we begin at offset off and cannot write more
501 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
502 * number of bytes written in page
503 */
504static int
505proc_read_info(char *page, char **start, off_t off, int count, int *eof,
506 void *data)
507{
508 int len = 0;
509 int temp;
510 char buf[16]; /* enough for all info */
511 /*
512 * We use the easy way, we don't care of off and count,
513 * so we don't set eof to 1
514 */
515
516 len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
517 len += sprintf(page + len, "Model reference : %s\n",
518 hotk->methods->name);
519 /*
520 * The SFUN method probably allows the original driver to get the list
521 * of features supported by a given model. For now, 0x0100 or 0x0800
522 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
523 * The significance of others is yet to be found.
524 */
525 if (read_acpi_int(hotk->handle, "SFUN", &temp))
526 len +=
527 sprintf(page + len, "SFUN value : 0x%04x\n", temp);
528 /*
529 * Another value for userspace: the ASYM method returns 0x02 for
530 * battery low and 0x04 for battery critical, its readings tend to be
531 * more accurate than those provided by _BST.
532 * Note: since not all the laptops provide this method, errors are
533 * silently ignored.
534 */
535 if (read_acpi_int(hotk->handle, "ASYM", &temp))
536 len +=
537 sprintf(page + len, "ASYM value : 0x%04x\n", temp);
538 if (asus_info) {
539 snprintf(buf, 16, "%d", asus_info->length);
540 len += sprintf(page + len, "DSDT length : %s\n", buf);
541 snprintf(buf, 16, "%d", asus_info->checksum);
542 len += sprintf(page + len, "DSDT checksum : %s\n", buf);
543 snprintf(buf, 16, "%d", asus_info->revision);
544 len += sprintf(page + len, "DSDT revision : %s\n", buf);
545 snprintf(buf, 7, "%s", asus_info->oem_id);
546 len += sprintf(page + len, "OEM id : %s\n", buf);
547 snprintf(buf, 9, "%s", asus_info->oem_table_id);
548 len += sprintf(page + len, "OEM table id : %s\n", buf);
549 snprintf(buf, 16, "%x", asus_info->oem_revision);
550 len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
551 snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
552 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
553 snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
554 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
555 }
556
557 return len;
558}
559
560/*
561 * /proc handlers
562 * We write our info in page, we begin at offset off and cannot write more
563 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
564 * number of bytes written in page
565 */
566
567/* Generic LED functions */
568static int read_led(const char *ledname, int ledmask)
569{
570 if (ledname) {
571 int led_status;
572
573 if (read_acpi_int(NULL, ledname, &led_status))
574 return led_status;
575 else
576 printk(KERN_WARNING "Asus ACPI: Error reading LED "
577 "status\n");
578 }
579 return (hotk->status & ledmask) ? 1 : 0;
580}
581
582static int parse_arg(const char __user *buf, unsigned long count, int *val)
583{
584 char s[32];
585 if (!count)
586 return 0;
587 if (count > 31)
588 return -EINVAL;
589 if (copy_from_user(s, buf, count))
590 return -EFAULT;
591 s[count] = 0;
592 if (sscanf(s, "%i", val) != 1)
593 return -EINVAL;
594 return count;
595}
596
597/* FIXME: kill extraneous args so it can be called independently */
598static int
599write_led(const char __user *buffer, unsigned long count,
600 char *ledname, int ledmask, int invert)
601{
602 int rv, value;
603 int led_out = 0;
604
605 rv = parse_arg(buffer, count, &value);
606 if (rv > 0)
607 led_out = value ? 1 : 0;
608
609 hotk->status =
610 (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
611
612 if (invert) /* invert target value */
613 led_out = !led_out;
614
615 if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
616 printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n",
617 ledname);
618
619 return rv;
620}
621
622/*
623 * Proc handlers for MLED
624 */
625static int
626proc_read_mled(char *page, char **start, off_t off, int count, int *eof,
627 void *data)
628{
629 return sprintf(page, "%d\n",
630 read_led(hotk->methods->mled_status, MLED_ON));
631}
632
633static int
634proc_write_mled(struct file *file, const char __user *buffer,
635 unsigned long count, void *data)
636{
637 return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
638}
639
640/*
641 * Proc handlers for LED display
642 */
643static int
644proc_read_ledd(char *page, char **start, off_t off, int count, int *eof,
645 void *data)
646{
647 return sprintf(page, "0x%08x\n", hotk->ledd_status);
648}
649
650static int
651proc_write_ledd(struct file *file, const char __user *buffer,
652 unsigned long count, void *data)
653{
654 int rv, value;
655
656 rv = parse_arg(buffer, count, &value);
657 if (rv > 0) {
658 if (!write_acpi_int
659 (hotk->handle, hotk->methods->mt_ledd, value, NULL))
660 printk(KERN_WARNING
661 "Asus ACPI: LED display write failed\n");
662 else
663 hotk->ledd_status = (u32) value;
664 }
665 return rv;
666}
667
668/*
669 * Proc handlers for WLED
670 */
671static int
672proc_read_wled(char *page, char **start, off_t off, int count, int *eof,
673 void *data)
674{
675 return sprintf(page, "%d\n",
676 read_led(hotk->methods->wled_status, WLED_ON));
677}
678
679static int
680proc_write_wled(struct file *file, const char __user *buffer,
681 unsigned long count, void *data)
682{
683 return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
684}
685
686/*
687 * Proc handlers for Bluetooth
688 */
689static int
690proc_read_bluetooth(char *page, char **start, off_t off, int count, int *eof,
691 void *data)
692{
693 return sprintf(page, "%d\n", read_led(hotk->methods->bt_status, BT_ON));
694}
695
696static int
697proc_write_bluetooth(struct file *file, const char __user *buffer,
698 unsigned long count, void *data)
699{
700 /* Note: mt_bt_switch controls both internal Bluetooth adapter's
701 presence and its LED */
702 return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0);
703}
704
705/*
706 * Proc handlers for TLED
707 */
708static int
709proc_read_tled(char *page, char **start, off_t off, int count, int *eof,
710 void *data)
711{
712 return sprintf(page, "%d\n",
713 read_led(hotk->methods->tled_status, TLED_ON));
714}
715
716static int
717proc_write_tled(struct file *file, const char __user *buffer,
718 unsigned long count, void *data)
719{
720 return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
721}
722
723static int get_lcd_state(void)
724{
725 int lcd = 0;
726
727 if (hotk->model == L3H) {
728 /* L3H and the like have to be handled differently */
729 acpi_status status = 0;
730 struct acpi_object_list input;
731 union acpi_object mt_params[2];
732 struct acpi_buffer output;
733 union acpi_object out_obj;
734
735 input.count = 2;
736 input.pointer = mt_params;
737 /* Note: the following values are partly guessed up, but
738 otherwise they seem to work */
739 mt_params[0].type = ACPI_TYPE_INTEGER;
740 mt_params[0].integer.value = 0x02;
741 mt_params[1].type = ACPI_TYPE_INTEGER;
742 mt_params[1].integer.value = 0x02;
743
744 output.length = sizeof(out_obj);
745 output.pointer = &out_obj;
746
747 status =
748 acpi_evaluate_object(NULL, hotk->methods->lcd_status,
749 &input, &output);
750 if (status != AE_OK)
751 return -1;
752 if (out_obj.type == ACPI_TYPE_INTEGER)
753 /* That's what the AML code does */
754 lcd = out_obj.integer.value >> 8;
755 } else if (hotk->model == F3Sa) {
756 unsigned long long tmp;
757 union acpi_object param;
758 struct acpi_object_list input;
759 acpi_status status;
760
761 /* Read pin 11 */
762 param.type = ACPI_TYPE_INTEGER;
763 param.integer.value = 0x11;
764 input.count = 1;
765 input.pointer = &param;
766
767 status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
768 &input, &tmp);
769 if (status != AE_OK)
770 return -1;
771
772 lcd = tmp;
773 } else {
774 /* We don't have to check anything if we are here */
775 if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
776 printk(KERN_WARNING
777 "Asus ACPI: Error reading LCD status\n");
778
779 if (hotk->model == L2D)
780 lcd = ~lcd;
781 }
782
783 return (lcd & 1);
784}
785
786static int set_lcd_state(int value)
787{
788 int lcd = 0;
789 acpi_status status = 0;
790
791 lcd = value ? 1 : 0;
792 if (lcd != get_lcd_state()) {
793 /* switch */
794 if (hotk->model != L3H) {
795 status =
796 acpi_evaluate_object(NULL,
797 hotk->methods->mt_lcd_switch,
798 NULL, NULL);
799 } else {
800 /* L3H and the like must be handled differently */
801 if (!write_acpi_int
802 (hotk->handle, hotk->methods->mt_lcd_switch, 0x07,
803 NULL))
804 status = AE_ERROR;
805 /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
806 the exact behaviour is simulated here */
807 }
808 if (ACPI_FAILURE(status))
809 printk(KERN_WARNING "Asus ACPI: Error switching LCD\n");
810 }
811 return 0;
812
813}
814
815static int
816proc_read_lcd(char *page, char **start, off_t off, int count, int *eof,
817 void *data)
818{
819 return sprintf(page, "%d\n", get_lcd_state());
820}
821
822static int
823proc_write_lcd(struct file *file, const char __user *buffer,
824 unsigned long count, void *data)
825{
826 int rv, value;
827
828 rv = parse_arg(buffer, count, &value);
829 if (rv > 0)
830 set_lcd_state(value);
831 return rv;
832}
833
834static int read_brightness(struct backlight_device *bd)
835{
836 int value;
837
838 if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */
839 if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
840 &value))
841 printk(KERN_WARNING
842 "Asus ACPI: Error reading brightness\n");
843 } else if (hotk->methods->brightness_status) { /* For D1 for example */
844 if (!read_acpi_int(NULL, hotk->methods->brightness_status,
845 &value))
846 printk(KERN_WARNING
847 "Asus ACPI: Error reading brightness\n");
848 } else /* No GPLV method */
849 value = hotk->brightness;
850 return value;
851}
852
853/*
854 * Change the brightness level
855 */
856static int set_brightness(int value)
857{
858 acpi_status status = 0;
859 int ret = 0;
860
861 /* SPLV laptop */
862 if (hotk->methods->brightness_set) {
863 if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
864 value, NULL))
865 printk(KERN_WARNING
866 "Asus ACPI: Error changing brightness\n");
867 ret = -EIO;
868 goto out;
869 }
870
871 /* No SPLV method if we are here, act as appropriate */
872 value -= read_brightness(NULL);
873 while (value != 0) {
874 status = acpi_evaluate_object(NULL, (value > 0) ?
875 hotk->methods->brightness_up :
876 hotk->methods->brightness_down,
877 NULL, NULL);
878 (value > 0) ? value-- : value++;
879 if (ACPI_FAILURE(status))
880 printk(KERN_WARNING
881 "Asus ACPI: Error changing brightness\n");
882 ret = -EIO;
883 }
884out:
885 return ret;
886}
887
888static int set_brightness_status(struct backlight_device *bd)
889{
890 return set_brightness(bd->props.brightness);
891}
892
893static int
894proc_read_brn(char *page, char **start, off_t off, int count, int *eof,
895 void *data)
896{
897 return sprintf(page, "%d\n", read_brightness(NULL));
898}
899
900static int
901proc_write_brn(struct file *file, const char __user *buffer,
902 unsigned long count, void *data)
903{
904 int rv, value;
905
906 rv = parse_arg(buffer, count, &value);
907 if (rv > 0) {
908 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
909 /* 0 <= value <= 15 */
910 set_brightness(value);
911 }
912 return rv;
913}
914
915static void set_display(int value)
916{
917 /* no sanity check needed for now */
918 if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
919 value, NULL))
920 printk(KERN_WARNING "Asus ACPI: Error setting display\n");
921 return;
922}
923
924/*
925 * Now, *this* one could be more user-friendly, but so far, no-one has
926 * complained. The significance of bits is the same as in proc_write_disp()
927 */
928static int
929proc_read_disp(char *page, char **start, off_t off, int count, int *eof,
930 void *data)
931{
932 int value = 0;
933
934 if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
935 printk(KERN_WARNING
936 "Asus ACPI: Error reading display status\n");
937 value &= 0x07; /* needed for some models, shouldn't hurt others */
938 return sprintf(page, "%d\n", value);
939}
940
941/*
942 * Experimental support for display switching. As of now: 1 should activate
943 * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
944 * (bitwise) of these will suffice. I never actually tested 3 displays hooked
945 * up simultaneously, so be warned. See the acpi4asus README for more info.
946 */
947static int
948proc_write_disp(struct file *file, const char __user *buffer,
949 unsigned long count, void *data)
950{
951 int rv, value;
952
953 rv = parse_arg(buffer, count, &value);
954 if (rv > 0)
955 set_display(value);
956 return rv;
957}
958
959typedef int (proc_readfunc) (char *page, char **start, off_t off, int count,
960 int *eof, void *data);
961typedef int (proc_writefunc) (struct file *file, const char __user *buffer,
962 unsigned long count, void *data);
963
964static int
965asus_proc_add(char *name, proc_writefunc *writefunc,
966 proc_readfunc *readfunc, mode_t mode,
967 struct acpi_device *device)
968{
969 struct proc_dir_entry *proc =
970 create_proc_entry(name, mode, acpi_device_dir(device));
971 if (!proc) {
972 printk(KERN_WARNING " Unable to create %s fs entry\n", name);
973 return -1;
974 }
975 proc->write_proc = writefunc;
976 proc->read_proc = readfunc;
977 proc->data = acpi_driver_data(device);
978 proc->owner = THIS_MODULE;
979 proc->uid = asus_uid;
980 proc->gid = asus_gid;
981 return 0;
982}
983
984static int asus_hotk_add_fs(struct acpi_device *device)
985{
986 struct proc_dir_entry *proc;
987 mode_t mode;
988
989 /*
990 * If parameter uid or gid is not changed, keep the default setting for
991 * our proc entries (-rw-rw-rw-) else, it means we care about security,
992 * and then set to -rw-rw----
993 */
994
995 if ((asus_uid == 0) && (asus_gid == 0)) {
996 mode = S_IFREG | S_IRUGO | S_IWUGO;
997 } else {
998 mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
999 printk(KERN_WARNING " asus_uid and asus_gid parameters are "
1000 "deprecated, use chown and chmod instead!\n");
1001 }
1002
1003 acpi_device_dir(device) = asus_proc_dir;
1004 if (!acpi_device_dir(device))
1005 return -ENODEV;
1006
1007 proc = create_proc_entry(PROC_INFO, mode, acpi_device_dir(device));
1008 if (proc) {
1009 proc->read_proc = proc_read_info;
1010 proc->data = acpi_driver_data(device);
1011 proc->owner = THIS_MODULE;
1012 proc->uid = asus_uid;
1013 proc->gid = asus_gid;
1014 } else {
1015 printk(KERN_WARNING " Unable to create " PROC_INFO
1016 " fs entry\n");
1017 }
1018
1019 if (hotk->methods->mt_wled) {
1020 asus_proc_add(PROC_WLED, &proc_write_wled, &proc_read_wled,
1021 mode, device);
1022 }
1023
1024 if (hotk->methods->mt_ledd) {
1025 asus_proc_add(PROC_LEDD, &proc_write_ledd, &proc_read_ledd,
1026 mode, device);
1027 }
1028
1029 if (hotk->methods->mt_mled) {
1030 asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled,
1031 mode, device);
1032 }
1033
1034 if (hotk->methods->mt_tled) {
1035 asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled,
1036 mode, device);
1037 }
1038
1039 if (hotk->methods->mt_bt_switch) {
1040 asus_proc_add(PROC_BT, &proc_write_bluetooth,
1041 &proc_read_bluetooth, mode, device);
1042 }
1043
1044 /*
1045 * We need both read node and write method as LCD switch is also
1046 * accessible from the keyboard
1047 */
1048 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
1049 asus_proc_add(PROC_LCD, &proc_write_lcd, &proc_read_lcd, mode,
1050 device);
1051 }
1052
1053 if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
1054 (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
1055 asus_proc_add(PROC_BRN, &proc_write_brn, &proc_read_brn, mode,
1056 device);
1057 }
1058
1059 if (hotk->methods->display_set) {
1060 asus_proc_add(PROC_DISP, &proc_write_disp, &proc_read_disp,
1061 mode, device);
1062 }
1063
1064 return 0;
1065}
1066
1067static int asus_hotk_remove_fs(struct acpi_device *device)
1068{
1069 if (acpi_device_dir(device)) {
1070 remove_proc_entry(PROC_INFO, acpi_device_dir(device));
1071 if (hotk->methods->mt_wled)
1072 remove_proc_entry(PROC_WLED, acpi_device_dir(device));
1073 if (hotk->methods->mt_mled)
1074 remove_proc_entry(PROC_MLED, acpi_device_dir(device));
1075 if (hotk->methods->mt_tled)
1076 remove_proc_entry(PROC_TLED, acpi_device_dir(device));
1077 if (hotk->methods->mt_ledd)
1078 remove_proc_entry(PROC_LEDD, acpi_device_dir(device));
1079 if (hotk->methods->mt_bt_switch)
1080 remove_proc_entry(PROC_BT, acpi_device_dir(device));
1081 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
1082 remove_proc_entry(PROC_LCD, acpi_device_dir(device));
1083 if ((hotk->methods->brightness_up
1084 && hotk->methods->brightness_down)
1085 || (hotk->methods->brightness_get
1086 && hotk->methods->brightness_set))
1087 remove_proc_entry(PROC_BRN, acpi_device_dir(device));
1088 if (hotk->methods->display_set)
1089 remove_proc_entry(PROC_DISP, acpi_device_dir(device));
1090 }
1091 return 0;
1092}
1093
1094static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
1095{
1096 /* TODO Find a better way to handle events count. */
1097 if (!hotk)
1098 return;
1099
1100 if ((event & ~((u32) BR_UP)) < 16)
1101 hotk->brightness = (event & ~((u32) BR_UP));
1102 else if ((event & ~((u32) BR_DOWN)) < 16)
1103 hotk->brightness = (event & ~((u32) BR_DOWN));
1104
1105 acpi_bus_generate_proc_event(hotk->device, event,
1106 hotk->event_count[event % 128]++);
1107
1108 return;
1109}
1110
1111/*
1112 * Match the model string to the list of supported models. Return END_MODEL if
1113 * no match or model is NULL.
1114 */
1115static int asus_model_match(char *model)
1116{
1117 if (model == NULL)
1118 return END_MODEL;
1119
1120 if (strncmp(model, "L3D", 3) == 0)
1121 return L3D;
1122 else if (strncmp(model, "L2E", 3) == 0 ||
1123 strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0)
1124 return L3H;
1125 else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0)
1126 return L3C;
1127 else if (strncmp(model, "L8L", 3) == 0)
1128 return L8L;
1129 else if (strncmp(model, "L4R", 3) == 0)
1130 return L4R;
1131 else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0)
1132 return M6N;
1133 else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0)
1134 return M6R;
1135 else if (strncmp(model, "M2N", 3) == 0 ||
1136 strncmp(model, "M3N", 3) == 0 ||
1137 strncmp(model, "M5N", 3) == 0 ||
1138 strncmp(model, "M6N", 3) == 0 ||
1139 strncmp(model, "S1N", 3) == 0 ||
1140 strncmp(model, "S5N", 3) == 0 || strncmp(model, "W1N", 3) == 0)
1141 return xxN;
1142 else if (strncmp(model, "M1", 2) == 0)
1143 return M1A;
1144 else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0)
1145 return M2E;
1146 else if (strncmp(model, "L2", 2) == 0)
1147 return L2D;
1148 else if (strncmp(model, "L8", 2) == 0)
1149 return S1x;
1150 else if (strncmp(model, "D1", 2) == 0)
1151 return D1x;
1152 else if (strncmp(model, "A1", 2) == 0)
1153 return A1x;
1154 else if (strncmp(model, "A2", 2) == 0)
1155 return A2x;
1156 else if (strncmp(model, "J1", 2) == 0)
1157 return S2x;
1158 else if (strncmp(model, "L5", 2) == 0)
1159 return L5x;
1160 else if (strncmp(model, "A4G", 3) == 0)
1161 return A4G;
1162 else if (strncmp(model, "W1N", 3) == 0)
1163 return W1N;
1164 else if (strncmp(model, "W3V", 3) == 0)
1165 return W3V;
1166 else if (strncmp(model, "W5A", 3) == 0)
1167 return W5A;
1168 else if (strncmp(model, "A4S", 3) == 0)
1169 return A4S;
1170 else if (strncmp(model, "F3Sa", 4) == 0)
1171 return F3Sa;
1172 else
1173 return END_MODEL;
1174}
1175
1176/*
1177 * This function is used to initialize the hotk with right values. In this
1178 * method, we can make all the detection we want, and modify the hotk struct
1179 */
1180static int asus_hotk_get_info(void)
1181{
1182 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1183 union acpi_object *model = NULL;
1184 int bsts_result;
1185 char *string = NULL;
1186 acpi_status status;
1187
1188 /*
1189 * Get DSDT headers early enough to allow for differentiating between
1190 * models, but late enough to allow acpi_bus_register_driver() to fail
1191 * before doing anything ACPI-specific. Should we encounter a machine,
1192 * which needs special handling (i.e. its hotkey device has a different
1193 * HID), this bit will be moved. A global variable asus_info contains
1194 * the DSDT header.
1195 */
1196 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
1197 if (ACPI_FAILURE(status))
1198 printk(KERN_WARNING " Couldn't get the DSDT table header\n");
1199
1200 /* We have to write 0 on init this far for all ASUS models */
1201 if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
1202 printk(KERN_ERR " Hotkey initialization failed\n");
1203 return -ENODEV;
1204 }
1205
1206 /* This needs to be called for some laptops to init properly */
1207 if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
1208 printk(KERN_WARNING " Error calling BSTS\n");
1209 else if (bsts_result)
1210 printk(KERN_NOTICE " BSTS called, 0x%02x returned\n",
1211 bsts_result);
1212
1213 /*
1214 * Try to match the object returned by INIT to the specific model.
1215 * Handle every possible object (or the lack of thereof) the DSDT
1216 * writers might throw at us. When in trouble, we pass NULL to
1217 * asus_model_match() and try something completely different.
1218 */
1219 if (buffer.pointer) {
1220 model = buffer.pointer;
1221 switch (model->type) {
1222 case ACPI_TYPE_STRING:
1223 string = model->string.pointer;
1224 break;
1225 case ACPI_TYPE_BUFFER:
1226 string = model->buffer.pointer;
1227 break;
1228 default:
1229 kfree(model);
1230 model = NULL;
1231 break;
1232 }
1233 }
1234 hotk->model = asus_model_match(string);
1235 if (hotk->model == END_MODEL) { /* match failed */
1236 if (asus_info &&
1237 strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
1238 hotk->model = P30;
1239 printk(KERN_NOTICE
1240 " Samsung P30 detected, supported\n");
1241 } else {
1242 hotk->model = M2E;
1243 printk(KERN_NOTICE " unsupported model %s, trying "
1244 "default values\n", string);
1245 printk(KERN_NOTICE
1246 " send /proc/acpi/dsdt to the developers\n");
1247 kfree(model);
1248 return -ENODEV;
1249 }
1250 hotk->methods = &model_conf[hotk->model];
1251 return AE_OK;
1252 }
1253 hotk->methods = &model_conf[hotk->model];
1254 printk(KERN_NOTICE " %s model detected, supported\n", string);
1255
1256 /* Sort of per-model blacklist */
1257 if (strncmp(string, "L2B", 3) == 0)
1258 hotk->methods->lcd_status = NULL;
1259 /* L2B is similar enough to L3C to use its settings, with this only
1260 exception */
1261 else if (strncmp(string, "A3G", 3) == 0)
1262 hotk->methods->lcd_status = "\\BLFG";
1263 /* A3G is like M6R */
1264 else if (strncmp(string, "S5N", 3) == 0 ||
1265 strncmp(string, "M5N", 3) == 0 ||
1266 strncmp(string, "W3N", 3) == 0)
1267 hotk->methods->mt_mled = NULL;
1268 /* S5N, M5N and W3N have no MLED */
1269 else if (strncmp(string, "L5D", 3) == 0)
1270 hotk->methods->mt_wled = NULL;
1271 /* L5D's WLED is not controlled by ACPI */
1272 else if (strncmp(string, "M2N", 3) == 0 ||
1273 strncmp(string, "W3V", 3) == 0 ||
1274 strncmp(string, "S1N", 3) == 0)
1275 hotk->methods->mt_wled = "WLED";
1276 /* M2N, S1N and W3V have a usable WLED */
1277 else if (asus_info) {
1278 if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
1279 hotk->methods->mled_status = NULL;
1280 /* S1300A reports L84F, but L1400B too, account for that */
1281 }
1282
1283 kfree(model);
1284
1285 return AE_OK;
1286}
1287
1288static int asus_hotk_check(void)
1289{
1290 int result = 0;
1291
1292 result = acpi_bus_get_status(hotk->device);
1293 if (result)
1294 return result;
1295
1296 if (hotk->device->status.present) {
1297 result = asus_hotk_get_info();
1298 } else {
1299 printk(KERN_ERR " Hotkey device not present, aborting\n");
1300 return -EINVAL;
1301 }
1302
1303 return result;
1304}
1305
1306static int asus_hotk_found;
1307
1308static int asus_hotk_add(struct acpi_device *device)
1309{
1310 acpi_status status = AE_OK;
1311 int result;
1312
1313 if (!device)
1314 return -EINVAL;
1315
1316 printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
1317 ASUS_ACPI_VERSION);
1318
1319 hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
1320 if (!hotk)
1321 return -ENOMEM;
1322
1323 hotk->handle = device->handle;
1324 strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
1325 strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
1326 device->driver_data = hotk;
1327 hotk->device = device;
1328
1329 result = asus_hotk_check();
1330 if (result)
1331 goto end;
1332
1333 result = asus_hotk_add_fs(device);
1334 if (result)
1335 goto end;
1336
1337 /*
1338 * We install the handler, it will receive the hotk in parameter, so, we
1339 * could add other data to the hotk struct
1340 */
1341 status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
1342 asus_hotk_notify, hotk);
1343 if (ACPI_FAILURE(status))
1344 printk(KERN_ERR " Error installing notify handler\n");
1345
1346 /* For laptops without GPLV: init the hotk->brightness value */
1347 if ((!hotk->methods->brightness_get)
1348 && (!hotk->methods->brightness_status)
1349 && (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
1350 status =
1351 acpi_evaluate_object(NULL, hotk->methods->brightness_down,
1352 NULL, NULL);
1353 if (ACPI_FAILURE(status))
1354 printk(KERN_WARNING " Error changing brightness\n");
1355 else {
1356 status =
1357 acpi_evaluate_object(NULL,
1358 hotk->methods->brightness_up,
1359 NULL, NULL);
1360 if (ACPI_FAILURE(status))
1361 printk(KERN_WARNING " Strange, error changing"
1362 " brightness\n");
1363 }
1364 }
1365
1366 asus_hotk_found = 1;
1367
1368 /* LED display is off by default */
1369 hotk->ledd_status = 0xFFF;
1370
1371end:
1372 if (result)
1373 kfree(hotk);
1374
1375 return result;
1376}
1377
1378static int asus_hotk_remove(struct acpi_device *device, int type)
1379{
1380 acpi_status status = 0;
1381
1382 if (!device || !acpi_driver_data(device))
1383 return -EINVAL;
1384
1385 status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
1386 asus_hotk_notify);
1387 if (ACPI_FAILURE(status))
1388 printk(KERN_ERR "Asus ACPI: Error removing notify handler\n");
1389
1390 asus_hotk_remove_fs(device);
1391
1392 kfree(hotk);
1393
1394 return 0;
1395}
1396
1397static struct backlight_ops asus_backlight_data = {
1398 .get_brightness = read_brightness,
1399 .update_status = set_brightness_status,
1400};
1401
1402static void asus_acpi_exit(void)
1403{
1404 if (asus_backlight_device)
1405 backlight_device_unregister(asus_backlight_device);
1406
1407 acpi_bus_unregister_driver(&asus_hotk_driver);
1408 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1409
1410 return;
1411}
1412
1413static int __init asus_acpi_init(void)
1414{
1415 int result;
1416
1417 if (acpi_disabled)
1418 return -ENODEV;
1419
1420 asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
1421 if (!asus_proc_dir) {
1422 printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
1423 return -ENODEV;
1424 }
1425 asus_proc_dir->owner = THIS_MODULE;
1426
1427 result = acpi_bus_register_driver(&asus_hotk_driver);
1428 if (result < 0) {
1429 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1430 return result;
1431 }
1432
1433 /*
1434 * This is a bit of a kludge. We only want this module loaded
1435 * for ASUS systems, but there's currently no way to probe the
1436 * ACPI namespace for ASUS HIDs. So we just return failure if
1437 * we didn't find one, which will cause the module to be
1438 * unloaded.
1439 */
1440 if (!asus_hotk_found) {
1441 acpi_bus_unregister_driver(&asus_hotk_driver);
1442 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1443 return -ENODEV;
1444 }
1445
1446 asus_backlight_device = backlight_device_register("asus", NULL, NULL,
1447 &asus_backlight_data);
1448 if (IS_ERR(asus_backlight_device)) {
1449 printk(KERN_ERR "Could not register asus backlight device\n");
1450 asus_backlight_device = NULL;
1451 asus_acpi_exit();
1452 return -ENODEV;
1453 }
1454 asus_backlight_device->props.max_brightness = 15;
1455
1456 return 0;
1457}
1458
1459module_init(asus_acpi_init);
1460module_exit(asus_acpi_exit);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
new file mode 100644
index 000000000000..40e60fc2e596
--- /dev/null
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -0,0 +1,863 @@
1/*
2 * toshiba_acpi.c - Toshiba Laptop ACPI Extras
3 *
4 *
5 * Copyright (C) 2002-2004 John Belmonte
6 * Copyright (C) 2008 Philip Langdale
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 * The devolpment page for this driver is located at
24 * http://memebeam.org/toys/ToshibaAcpiDriver.
25 *
26 * Credits:
27 * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
28 * engineering the Windows drivers
29 * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
30 * Rob Miller - TV out and hotkeys help
31 *
32 *
33 * TODO
34 *
35 */
36
37#define TOSHIBA_ACPI_VERSION "0.19"
38#define PROC_INTERFACE_VERSION 1
39
40#include <linux/kernel.h>
41#include <linux/module.h>
42#include <linux/init.h>
43#include <linux/types.h>
44#include <linux/proc_fs.h>
45#include <linux/backlight.h>
46#include <linux/platform_device.h>
47#include <linux/rfkill.h>
48#include <linux/input-polldev.h>
49
50#include <asm/uaccess.h>
51
52#include <acpi/acpi_drivers.h>
53
54MODULE_AUTHOR("John Belmonte");
55MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
56MODULE_LICENSE("GPL");
57
58#define MY_LOGPREFIX "toshiba_acpi: "
59#define MY_ERR KERN_ERR MY_LOGPREFIX
60#define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
61#define MY_INFO KERN_INFO MY_LOGPREFIX
62
63/* Toshiba ACPI method paths */
64#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
65#define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
66#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
67#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
68
69/* Toshiba HCI interface definitions
70 *
71 * HCI is Toshiba's "Hardware Control Interface" which is supposed to
72 * be uniform across all their models. Ideally we would just call
73 * dedicated ACPI methods instead of using this primitive interface.
74 * However the ACPI methods seem to be incomplete in some areas (for
75 * example they allow setting, but not reading, the LCD brightness value),
76 * so this is still useful.
77 */
78
79#define HCI_WORDS 6
80
81/* operations */
82#define HCI_SET 0xff00
83#define HCI_GET 0xfe00
84
85/* return codes */
86#define HCI_SUCCESS 0x0000
87#define HCI_FAILURE 0x1000
88#define HCI_NOT_SUPPORTED 0x8000
89#define HCI_EMPTY 0x8c00
90
91/* registers */
92#define HCI_FAN 0x0004
93#define HCI_SYSTEM_EVENT 0x0016
94#define HCI_VIDEO_OUT 0x001c
95#define HCI_HOTKEY_EVENT 0x001e
96#define HCI_LCD_BRIGHTNESS 0x002a
97#define HCI_WIRELESS 0x0056
98
99/* field definitions */
100#define HCI_LCD_BRIGHTNESS_BITS 3
101#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
102#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
103#define HCI_VIDEO_OUT_LCD 0x1
104#define HCI_VIDEO_OUT_CRT 0x2
105#define HCI_VIDEO_OUT_TV 0x4
106#define HCI_WIRELESS_KILL_SWITCH 0x01
107#define HCI_WIRELESS_BT_PRESENT 0x0f
108#define HCI_WIRELESS_BT_ATTACH 0x40
109#define HCI_WIRELESS_BT_POWER 0x80
110
111static const struct acpi_device_id toshiba_device_ids[] = {
112 {"TOS6200", 0},
113 {"TOS6208", 0},
114 {"TOS1900", 0},
115 {"", 0},
116};
117MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
118
119/* utility
120 */
121
122static __inline__ void _set_bit(u32 * word, u32 mask, int value)
123{
124 *word = (*word & ~mask) | (mask * value);
125}
126
127/* acpi interface wrappers
128 */
129
130static int is_valid_acpi_path(const char *methodName)
131{
132 acpi_handle handle;
133 acpi_status status;
134
135 status = acpi_get_handle(NULL, (char *)methodName, &handle);
136 return !ACPI_FAILURE(status);
137}
138
139static int write_acpi_int(const char *methodName, int val)
140{
141 struct acpi_object_list params;
142 union acpi_object in_objs[1];
143 acpi_status status;
144
145 params.count = ARRAY_SIZE(in_objs);
146 params.pointer = in_objs;
147 in_objs[0].type = ACPI_TYPE_INTEGER;
148 in_objs[0].integer.value = val;
149
150 status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
151 return (status == AE_OK);
152}
153
154#if 0
155static int read_acpi_int(const char *methodName, int *pVal)
156{
157 struct acpi_buffer results;
158 union acpi_object out_objs[1];
159 acpi_status status;
160
161 results.length = sizeof(out_objs);
162 results.pointer = out_objs;
163
164 status = acpi_evaluate_object(0, (char *)methodName, 0, &results);
165 *pVal = out_objs[0].integer.value;
166
167 return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER);
168}
169#endif
170
171static const char *method_hci /*= 0*/ ;
172
173/* Perform a raw HCI call. Here we don't care about input or output buffer
174 * format.
175 */
176static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
177{
178 struct acpi_object_list params;
179 union acpi_object in_objs[HCI_WORDS];
180 struct acpi_buffer results;
181 union acpi_object out_objs[HCI_WORDS + 1];
182 acpi_status status;
183 int i;
184
185 params.count = HCI_WORDS;
186 params.pointer = in_objs;
187 for (i = 0; i < HCI_WORDS; ++i) {
188 in_objs[i].type = ACPI_TYPE_INTEGER;
189 in_objs[i].integer.value = in[i];
190 }
191
192 results.length = sizeof(out_objs);
193 results.pointer = out_objs;
194
195 status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
196 &results);
197 if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
198 for (i = 0; i < out_objs->package.count; ++i) {
199 out[i] = out_objs->package.elements[i].integer.value;
200 }
201 }
202
203 return status;
204}
205
206/* common hci tasks (get or set one or two value)
207 *
208 * In addition to the ACPI status, the HCI system returns a result which
209 * may be useful (such as "not supported").
210 */
211
212static acpi_status hci_write1(u32 reg, u32 in1, u32 * result)
213{
214 u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
215 u32 out[HCI_WORDS];
216 acpi_status status = hci_raw(in, out);
217 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
218 return status;
219}
220
221static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
222{
223 u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
224 u32 out[HCI_WORDS];
225 acpi_status status = hci_raw(in, out);
226 *out1 = out[2];
227 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
228 return status;
229}
230
231static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result)
232{
233 u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
234 u32 out[HCI_WORDS];
235 acpi_status status = hci_raw(in, out);
236 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
237 return status;
238}
239
240static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
241{
242 u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
243 u32 out[HCI_WORDS];
244 acpi_status status = hci_raw(in, out);
245 *out1 = out[2];
246 *out2 = out[3];
247 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
248 return status;
249}
250
251struct toshiba_acpi_dev {
252 struct platform_device *p_dev;
253 struct rfkill *rfk_dev;
254 struct input_polled_dev *poll_dev;
255
256 const char *bt_name;
257 const char *rfk_name;
258
259 bool last_rfk_state;
260
261 struct mutex mutex;
262};
263
264static struct toshiba_acpi_dev toshiba_acpi = {
265 .bt_name = "Toshiba Bluetooth",
266 .rfk_name = "Toshiba RFKill Switch",
267 .last_rfk_state = false,
268};
269
270/* Bluetooth rfkill handlers */
271
272static u32 hci_get_bt_present(bool *present)
273{
274 u32 hci_result;
275 u32 value, value2;
276
277 value = 0;
278 value2 = 0;
279 hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
280 if (hci_result == HCI_SUCCESS)
281 *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
282
283 return hci_result;
284}
285
286static 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
301static u32 hci_get_radio_state(bool *radio_state)
302{
303 u32 hci_result;
304 u32 value, value2;
305
306 value = 0;
307 value2 = 0x0001;
308 hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
309
310 *radio_state = value & HCI_WIRELESS_KILL_SWITCH;
311 return hci_result;
312}
313
314static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state)
315{
316 u32 result1, result2;
317 u32 value;
318 bool radio_state;
319 struct toshiba_acpi_dev *dev = data;
320
321 value = (state == RFKILL_STATE_UNBLOCKED);
322
323 if (hci_get_radio_state(&radio_state) != HCI_SUCCESS)
324 return -EFAULT;
325
326 switch (state) {
327 case RFKILL_STATE_UNBLOCKED:
328 if (!radio_state)
329 return -EPERM;
330 break;
331 case RFKILL_STATE_SOFT_BLOCKED:
332 break;
333 default:
334 return -EINVAL;
335 }
336
337 mutex_lock(&dev->mutex);
338 hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
339 hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
340 mutex_unlock(&dev->mutex);
341
342 if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
343 return -EFAULT;
344
345 return 0;
346}
347
348static void bt_poll_rfkill(struct input_polled_dev *poll_dev)
349{
350 bool state_changed;
351 bool new_rfk_state;
352 bool value;
353 u32 hci_result;
354 struct toshiba_acpi_dev *dev = poll_dev->private;
355
356 hci_result = hci_get_radio_state(&value);
357 if (hci_result != HCI_SUCCESS)
358 return; /* Can't do anything useful */
359
360 new_rfk_state = value;
361
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);
366
367 if (unlikely(state_changed)) {
368 rfkill_force_state(dev->rfk_dev,
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}
377
378static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
379static struct backlight_device *toshiba_backlight_device;
380static int force_fan;
381static int last_key_event;
382static int key_event_valid;
383
384typedef struct _ProcItem {
385 const char *name;
386 char *(*read_func) (char *);
387 unsigned long (*write_func) (const char *, unsigned long);
388} ProcItem;
389
390/* proc file handlers
391 */
392
393static int
394dispatch_read(char *page, char **start, off_t off, int count, int *eof,
395 ProcItem * item)
396{
397 char *p = page;
398 int len;
399
400 if (off == 0)
401 p = item->read_func(p);
402
403 /* ISSUE: I don't understand this code */
404 len = (p - page);
405 if (len <= off + count)
406 *eof = 1;
407 *start = page + off;
408 len -= off;
409 if (len > count)
410 len = count;
411 if (len < 0)
412 len = 0;
413 return len;
414}
415
416static int
417dispatch_write(struct file *file, const char __user * buffer,
418 unsigned long count, ProcItem * item)
419{
420 int result;
421 char *tmp_buffer;
422
423 /* Arg buffer points to userspace memory, which can't be accessed
424 * directly. Since we're making a copy, zero-terminate the
425 * destination so that sscanf can be used on it safely.
426 */
427 tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
428 if (!tmp_buffer)
429 return -ENOMEM;
430
431 if (copy_from_user(tmp_buffer, buffer, count)) {
432 result = -EFAULT;
433 } else {
434 tmp_buffer[count] = 0;
435 result = item->write_func(tmp_buffer, count);
436 }
437 kfree(tmp_buffer);
438 return result;
439}
440
441static int get_lcd(struct backlight_device *bd)
442{
443 u32 hci_result;
444 u32 value;
445
446 hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
447 if (hci_result == HCI_SUCCESS) {
448 return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
449 } else
450 return -EFAULT;
451}
452
453static char *read_lcd(char *p)
454{
455 int value = get_lcd(NULL);
456
457 if (value >= 0) {
458 p += sprintf(p, "brightness: %d\n", value);
459 p += sprintf(p, "brightness_levels: %d\n",
460 HCI_LCD_BRIGHTNESS_LEVELS);
461 } else {
462 printk(MY_ERR "Error reading LCD brightness\n");
463 }
464
465 return p;
466}
467
468static int set_lcd(int value)
469{
470 u32 hci_result;
471
472 value = value << HCI_LCD_BRIGHTNESS_SHIFT;
473 hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
474 if (hci_result != HCI_SUCCESS)
475 return -EFAULT;
476
477 return 0;
478}
479
480static int set_lcd_status(struct backlight_device *bd)
481{
482 return set_lcd(bd->props.brightness);
483}
484
485static unsigned long write_lcd(const char *buffer, unsigned long count)
486{
487 int value;
488 int ret;
489
490 if (sscanf(buffer, " brightness : %i", &value) == 1 &&
491 value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
492 ret = set_lcd(value);
493 if (ret == 0)
494 ret = count;
495 } else {
496 ret = -EINVAL;
497 }
498 return ret;
499}
500
501static char *read_video(char *p)
502{
503 u32 hci_result;
504 u32 value;
505
506 hci_read1(HCI_VIDEO_OUT, &value, &hci_result);
507 if (hci_result == HCI_SUCCESS) {
508 int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
509 int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
510 int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
511 p += sprintf(p, "lcd_out: %d\n", is_lcd);
512 p += sprintf(p, "crt_out: %d\n", is_crt);
513 p += sprintf(p, "tv_out: %d\n", is_tv);
514 } else {
515 printk(MY_ERR "Error reading video out status\n");
516 }
517
518 return p;
519}
520
521static unsigned long write_video(const char *buffer, unsigned long count)
522{
523 int value;
524 int remain = count;
525 int lcd_out = -1;
526 int crt_out = -1;
527 int tv_out = -1;
528 u32 hci_result;
529 u32 video_out;
530
531 /* scan expression. Multiple expressions may be delimited with ;
532 *
533 * NOTE: to keep scanning simple, invalid fields are ignored
534 */
535 while (remain) {
536 if (sscanf(buffer, " lcd_out : %i", &value) == 1)
537 lcd_out = value & 1;
538 else if (sscanf(buffer, " crt_out : %i", &value) == 1)
539 crt_out = value & 1;
540 else if (sscanf(buffer, " tv_out : %i", &value) == 1)
541 tv_out = value & 1;
542 /* advance to one character past the next ; */
543 do {
544 ++buffer;
545 --remain;
546 }
547 while (remain && *(buffer - 1) != ';');
548 }
549
550 hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
551 if (hci_result == HCI_SUCCESS) {
552 unsigned int new_video_out = video_out;
553 if (lcd_out != -1)
554 _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
555 if (crt_out != -1)
556 _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
557 if (tv_out != -1)
558 _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
559 /* To avoid unnecessary video disruption, only write the new
560 * video setting if something changed. */
561 if (new_video_out != video_out)
562 write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
563 } else {
564 return -EFAULT;
565 }
566
567 return count;
568}
569
570static char *read_fan(char *p)
571{
572 u32 hci_result;
573 u32 value;
574
575 hci_read1(HCI_FAN, &value, &hci_result);
576 if (hci_result == HCI_SUCCESS) {
577 p += sprintf(p, "running: %d\n", (value > 0));
578 p += sprintf(p, "force_on: %d\n", force_fan);
579 } else {
580 printk(MY_ERR "Error reading fan status\n");
581 }
582
583 return p;
584}
585
586static unsigned long write_fan(const char *buffer, unsigned long count)
587{
588 int value;
589 u32 hci_result;
590
591 if (sscanf(buffer, " force_on : %i", &value) == 1 &&
592 value >= 0 && value <= 1) {
593 hci_write1(HCI_FAN, value, &hci_result);
594 if (hci_result != HCI_SUCCESS)
595 return -EFAULT;
596 else
597 force_fan = value;
598 } else {
599 return -EINVAL;
600 }
601
602 return count;
603}
604
605static char *read_keys(char *p)
606{
607 u32 hci_result;
608 u32 value;
609
610 if (!key_event_valid) {
611 hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
612 if (hci_result == HCI_SUCCESS) {
613 key_event_valid = 1;
614 last_key_event = value;
615 } else if (hci_result == HCI_EMPTY) {
616 /* better luck next time */
617 } else if (hci_result == HCI_NOT_SUPPORTED) {
618 /* This is a workaround for an unresolved issue on
619 * some machines where system events sporadically
620 * become disabled. */
621 hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
622 printk(MY_NOTICE "Re-enabled hotkeys\n");
623 } else {
624 printk(MY_ERR "Error reading hotkey status\n");
625 goto end;
626 }
627 }
628
629 p += sprintf(p, "hotkey_ready: %d\n", key_event_valid);
630 p += sprintf(p, "hotkey: 0x%04x\n", last_key_event);
631
632 end:
633 return p;
634}
635
636static unsigned long write_keys(const char *buffer, unsigned long count)
637{
638 int value;
639
640 if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && value == 0) {
641 key_event_valid = 0;
642 } else {
643 return -EINVAL;
644 }
645
646 return count;
647}
648
649static char *read_version(char *p)
650{
651 p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION);
652 p += sprintf(p, "proc_interface: %d\n",
653 PROC_INTERFACE_VERSION);
654 return p;
655}
656
657/* proc and module init
658 */
659
660#define PROC_TOSHIBA "toshiba"
661
662static ProcItem proc_items[] = {
663 {"lcd", read_lcd, write_lcd},
664 {"video", read_video, write_video},
665 {"fan", read_fan, write_fan},
666 {"keys", read_keys, write_keys},
667 {"version", read_version, NULL},
668 {NULL}
669};
670
671static acpi_status __init add_device(void)
672{
673 struct proc_dir_entry *proc;
674 ProcItem *item;
675
676 for (item = proc_items; item->name; ++item) {
677 proc = create_proc_read_entry(item->name,
678 S_IFREG | S_IRUGO | S_IWUSR,
679 toshiba_proc_dir,
680 (read_proc_t *) dispatch_read,
681 item);
682 if (proc)
683 proc->owner = THIS_MODULE;
684 if (proc && item->write_func)
685 proc->write_proc = (write_proc_t *) dispatch_write;
686 }
687
688 return AE_OK;
689}
690
691static acpi_status remove_device(void)
692{
693 ProcItem *item;
694
695 for (item = proc_items; item->name; ++item)
696 remove_proc_entry(item->name, toshiba_proc_dir);
697 return AE_OK;
698}
699
700static struct backlight_ops toshiba_backlight_data = {
701 .get_brightness = get_lcd,
702 .update_status = set_lcd_status,
703};
704
705static void toshiba_acpi_exit(void)
706{
707 if (toshiba_acpi.poll_dev) {
708 input_unregister_polled_device(toshiba_acpi.poll_dev);
709 input_free_polled_device(toshiba_acpi.poll_dev);
710 }
711
712 if (toshiba_acpi.rfk_dev)
713 rfkill_unregister(toshiba_acpi.rfk_dev);
714
715 if (toshiba_backlight_device)
716 backlight_device_unregister(toshiba_backlight_device);
717
718 remove_device();
719
720 if (toshiba_proc_dir)
721 remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
722
723 platform_device_unregister(toshiba_acpi.p_dev);
724
725 return;
726}
727
728static int __init toshiba_acpi_init(void)
729{
730 acpi_status status = AE_OK;
731 u32 hci_result;
732 bool bt_present;
733 bool bt_on;
734 bool radio_on;
735 int ret = 0;
736
737 if (acpi_disabled)
738 return -ENODEV;
739
740 /* simple device detection: look for HCI method */
741 if (is_valid_acpi_path(METHOD_HCI_1))
742 method_hci = METHOD_HCI_1;
743 else if (is_valid_acpi_path(METHOD_HCI_2))
744 method_hci = METHOD_HCI_2;
745 else
746 return -ENODEV;
747
748 printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
749 TOSHIBA_ACPI_VERSION);
750 printk(MY_INFO " HCI method: %s\n", method_hci);
751
752 mutex_init(&toshiba_acpi.mutex);
753
754 toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
755 -1, NULL, 0);
756 if (IS_ERR(toshiba_acpi.p_dev)) {
757 ret = PTR_ERR(toshiba_acpi.p_dev);
758 printk(MY_ERR "unable to register platform device\n");
759 toshiba_acpi.p_dev = NULL;
760 toshiba_acpi_exit();
761 return ret;
762 }
763
764 force_fan = 0;
765 key_event_valid = 0;
766
767 /* enable event fifo */
768 hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
769
770 toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
771 if (!toshiba_proc_dir) {
772 toshiba_acpi_exit();
773 return -ENODEV;
774 } else {
775 toshiba_proc_dir->owner = THIS_MODULE;
776 status = add_device();
777 if (ACPI_FAILURE(status)) {
778 toshiba_acpi_exit();
779 return -ENODEV;
780 }
781 }
782
783 toshiba_backlight_device = backlight_device_register("toshiba",
784 &toshiba_acpi.p_dev->dev,
785 NULL,
786 &toshiba_backlight_data);
787 if (IS_ERR(toshiba_backlight_device)) {
788 ret = PTR_ERR(toshiba_backlight_device);
789
790 printk(KERN_ERR "Could not register toshiba backlight device\n");
791 toshiba_backlight_device = NULL;
792 toshiba_acpi_exit();
793 return ret;
794 }
795 toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
796
797 /* Register rfkill switch for Bluetooth */
798 if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
799 toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev,
800 RFKILL_TYPE_BLUETOOTH);
801 if (!toshiba_acpi.rfk_dev) {
802 printk(MY_ERR "unable to allocate rfkill device\n");
803 toshiba_acpi_exit();
804 return -ENOMEM;
805 }
806
807 toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name;
808 toshiba_acpi.rfk_dev->toggle_radio = bt_rfkill_toggle_radio;
809 toshiba_acpi.rfk_dev->user_claim_unsupported = 1;
810 toshiba_acpi.rfk_dev->data = &toshiba_acpi;
811
812 if (hci_get_bt_on(&bt_on) == HCI_SUCCESS && bt_on) {
813 toshiba_acpi.rfk_dev->state = RFKILL_STATE_UNBLOCKED;
814 } else if (hci_get_radio_state(&radio_on) == HCI_SUCCESS &&
815 radio_on) {
816 toshiba_acpi.rfk_dev->state = RFKILL_STATE_SOFT_BLOCKED;
817 } else {
818 toshiba_acpi.rfk_dev->state = RFKILL_STATE_HARD_BLOCKED;
819 }
820
821 ret = rfkill_register(toshiba_acpi.rfk_dev);
822 if (ret) {
823 printk(MY_ERR "unable to register rfkill device\n");
824 toshiba_acpi_exit();
825 return -ENOMEM;
826 }
827
828 /* Register input device for kill switch */
829 toshiba_acpi.poll_dev = input_allocate_polled_device();
830 if (!toshiba_acpi.poll_dev) {
831 printk(MY_ERR
832 "unable to allocate kill-switch input device\n");
833 toshiba_acpi_exit();
834 return -ENOMEM;
835 }
836 toshiba_acpi.poll_dev->private = &toshiba_acpi;
837 toshiba_acpi.poll_dev->poll = bt_poll_rfkill;
838 toshiba_acpi.poll_dev->poll_interval = 1000; /* msecs */
839
840 toshiba_acpi.poll_dev->input->name = toshiba_acpi.rfk_name;
841 toshiba_acpi.poll_dev->input->id.bustype = BUS_HOST;
842 /* Toshiba USB ID */
843 toshiba_acpi.poll_dev->input->id.vendor = 0x0930;
844 set_bit(EV_SW, toshiba_acpi.poll_dev->input->evbit);
845 set_bit(SW_RFKILL_ALL, toshiba_acpi.poll_dev->input->swbit);
846 input_report_switch(toshiba_acpi.poll_dev->input,
847 SW_RFKILL_ALL, TRUE);
848 input_sync(toshiba_acpi.poll_dev->input);
849
850 ret = input_register_polled_device(toshiba_acpi.poll_dev);
851 if (ret) {
852 printk(MY_ERR
853 "unable to register kill-switch input device\n");
854 toshiba_acpi_exit();
855 return ret;
856 }
857 }
858
859 return 0;
860}
861
862module_init(toshiba_acpi_init);
863module_exit(toshiba_acpi_exit);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
new file mode 100644
index 000000000000..8a8b377712c9
--- /dev/null
+++ b/drivers/platform/x86/wmi.c
@@ -0,0 +1,747 @@
1/*
2 * ACPI-WMI mapping driver
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * GUID parsing code from ldm.c is:
7 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
8 * Copyright (c) 2001-2007 Anton Altaparmakov
9 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 */
29
30#include <linux/kernel.h>
31#include <linux/init.h>
32#include <linux/types.h>
33#include <linux/list.h>
34#include <linux/acpi.h>
35#include <acpi/acpi_bus.h>
36#include <acpi/acpi_drivers.h>
37
38ACPI_MODULE_NAME("wmi");
39MODULE_AUTHOR("Carlos Corbacho");
40MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
41MODULE_LICENSE("GPL");
42
43#define ACPI_WMI_CLASS "wmi"
44
45#undef PREFIX
46#define PREFIX "ACPI: WMI: "
47
48static DEFINE_MUTEX(wmi_data_lock);
49
50struct guid_block {
51 char guid[16];
52 union {
53 char object_id[2];
54 struct {
55 unsigned char notify_id;
56 unsigned char reserved;
57 };
58 };
59 u8 instance_count;
60 u8 flags;
61};
62
63struct wmi_block {
64 struct list_head list;
65 struct guid_block gblock;
66 acpi_handle handle;
67 wmi_notify_handler handler;
68 void *handler_data;
69};
70
71static struct wmi_block wmi_blocks;
72
73/*
74 * If the GUID data block is marked as expensive, we must enable and
75 * explicitily disable data collection.
76 */
77#define ACPI_WMI_EXPENSIVE 0x1
78#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
79#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
80#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
81
82static int acpi_wmi_remove(struct acpi_device *device, int type);
83static int acpi_wmi_add(struct acpi_device *device);
84
85static const struct acpi_device_id wmi_device_ids[] = {
86 {"PNP0C14", 0},
87 {"pnp0c14", 0},
88 {"", 0},
89};
90MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
91
92static struct acpi_driver acpi_wmi_driver = {
93 .name = "wmi",
94 .class = ACPI_WMI_CLASS,
95 .ids = wmi_device_ids,
96 .ops = {
97 .add = acpi_wmi_add,
98 .remove = acpi_wmi_remove,
99 },
100};
101
102/*
103 * GUID parsing functions
104 */
105
106/**
107 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
108 * @src: Pointer to at least 2 characters to convert.
109 *
110 * Convert a two character ASCII hex string to a number.
111 *
112 * Return: 0-255 Success, the byte was parsed correctly
113 * -1 Error, an invalid character was supplied
114 */
115static int wmi_parse_hexbyte(const u8 *src)
116{
117 unsigned int x; /* For correct wrapping */
118 int h;
119
120 /* high part */
121 x = src[0];
122 if (x - '0' <= '9' - '0') {
123 h = x - '0';
124 } else if (x - 'a' <= 'f' - 'a') {
125 h = x - 'a' + 10;
126 } else if (x - 'A' <= 'F' - 'A') {
127 h = x - 'A' + 10;
128 } else {
129 return -1;
130 }
131 h <<= 4;
132
133 /* low part */
134 x = src[1];
135 if (x - '0' <= '9' - '0')
136 return h | (x - '0');
137 if (x - 'a' <= 'f' - 'a')
138 return h | (x - 'a' + 10);
139 if (x - 'A' <= 'F' - 'A')
140 return h | (x - 'A' + 10);
141 return -1;
142}
143
144/**
145 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
146 * @src: Memory block holding binary GUID (16 bytes)
147 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
148 *
149 * Byte swap a binary GUID to match it's real GUID value
150 */
151static void wmi_swap_bytes(u8 *src, u8 *dest)
152{
153 int i;
154
155 for (i = 0; i <= 3; i++)
156 memcpy(dest + i, src + (3 - i), 1);
157
158 for (i = 0; i <= 1; i++)
159 memcpy(dest + 4 + i, src + (5 - i), 1);
160
161 for (i = 0; i <= 1; i++)
162 memcpy(dest + 6 + i, src + (7 - i), 1);
163
164 memcpy(dest + 8, src + 8, 8);
165}
166
167/**
168 * wmi_parse_guid - Convert GUID from ASCII to binary
169 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
170 * @dest: Memory block to hold binary GUID (16 bytes)
171 *
172 * N.B. The GUID need not be NULL terminated.
173 *
174 * Return: 'true' @dest contains binary GUID
175 * 'false' @dest contents are undefined
176 */
177static bool wmi_parse_guid(const u8 *src, u8 *dest)
178{
179 static const int size[] = { 4, 2, 2, 2, 6 };
180 int i, j, v;
181
182 if (src[8] != '-' || src[13] != '-' ||
183 src[18] != '-' || src[23] != '-')
184 return false;
185
186 for (j = 0; j < 5; j++, src++) {
187 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
188 v = wmi_parse_hexbyte(src);
189 if (v < 0)
190 return false;
191 }
192 }
193
194 return true;
195}
196
197static bool find_guid(const char *guid_string, struct wmi_block **out)
198{
199 char tmp[16], guid_input[16];
200 struct wmi_block *wblock;
201 struct guid_block *block;
202 struct list_head *p;
203
204 wmi_parse_guid(guid_string, tmp);
205 wmi_swap_bytes(tmp, guid_input);
206
207 list_for_each(p, &wmi_blocks.list) {
208 wblock = list_entry(p, struct wmi_block, list);
209 block = &wblock->gblock;
210
211 if (memcmp(block->guid, guid_input, 16) == 0) {
212 if (out)
213 *out = wblock;
214 return 1;
215 }
216 }
217 return 0;
218}
219
220static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
221{
222 struct guid_block *block = NULL;
223 char method[5];
224 struct acpi_object_list input;
225 union acpi_object params[1];
226 acpi_status status;
227 acpi_handle handle;
228
229 block = &wblock->gblock;
230 handle = wblock->handle;
231
232 if (!block)
233 return AE_NOT_EXIST;
234
235 input.count = 1;
236 input.pointer = params;
237 params[0].type = ACPI_TYPE_INTEGER;
238 params[0].integer.value = enable;
239
240 snprintf(method, 5, "WE%02X", block->notify_id);
241 status = acpi_evaluate_object(handle, method, &input, NULL);
242
243 if (status != AE_OK && status != AE_NOT_FOUND)
244 return status;
245 else
246 return AE_OK;
247}
248
249/*
250 * Exported WMI functions
251 */
252/**
253 * wmi_evaluate_method - Evaluate a WMI method
254 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
255 * @instance: Instance index
256 * @method_id: Method ID to call
257 * &in: Buffer containing input for the method call
258 * &out: Empty buffer to return the method results
259 *
260 * Call an ACPI-WMI method
261 */
262acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
263u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
264{
265 struct guid_block *block = NULL;
266 struct wmi_block *wblock = NULL;
267 acpi_handle handle;
268 acpi_status status;
269 struct acpi_object_list input;
270 union acpi_object params[3];
271 char method[4] = "WM";
272
273 if (!find_guid(guid_string, &wblock))
274 return AE_ERROR;
275
276 block = &wblock->gblock;
277 handle = wblock->handle;
278
279 if (!(block->flags & ACPI_WMI_METHOD))
280 return AE_BAD_DATA;
281
282 if (block->instance_count < instance)
283 return AE_BAD_PARAMETER;
284
285 input.count = 2;
286 input.pointer = params;
287 params[0].type = ACPI_TYPE_INTEGER;
288 params[0].integer.value = instance;
289 params[1].type = ACPI_TYPE_INTEGER;
290 params[1].integer.value = method_id;
291
292 if (in) {
293 input.count = 3;
294
295 if (block->flags & ACPI_WMI_STRING) {
296 params[2].type = ACPI_TYPE_STRING;
297 } else {
298 params[2].type = ACPI_TYPE_BUFFER;
299 }
300 params[2].buffer.length = in->length;
301 params[2].buffer.pointer = in->pointer;
302 }
303
304 strncat(method, block->object_id, 2);
305
306 status = acpi_evaluate_object(handle, method, &input, out);
307
308 return status;
309}
310EXPORT_SYMBOL_GPL(wmi_evaluate_method);
311
312/**
313 * wmi_query_block - Return contents of a WMI block
314 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
315 * @instance: Instance index
316 * &out: Empty buffer to return the contents of the data block to
317 *
318 * Return the contents of an ACPI-WMI data block to a buffer
319 */
320acpi_status wmi_query_block(const char *guid_string, u8 instance,
321struct acpi_buffer *out)
322{
323 struct guid_block *block = NULL;
324 struct wmi_block *wblock = NULL;
325 acpi_handle handle, wc_handle;
326 acpi_status status, wc_status = AE_ERROR;
327 struct acpi_object_list input, wc_input;
328 union acpi_object wc_params[1], wq_params[1];
329 char method[4];
330 char wc_method[4] = "WC";
331
332 if (!guid_string || !out)
333 return AE_BAD_PARAMETER;
334
335 if (!find_guid(guid_string, &wblock))
336 return AE_ERROR;
337
338 block = &wblock->gblock;
339 handle = wblock->handle;
340
341 if (block->instance_count < instance)
342 return AE_BAD_PARAMETER;
343
344 /* Check GUID is a data block */
345 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
346 return AE_ERROR;
347
348 input.count = 1;
349 input.pointer = wq_params;
350 wq_params[0].type = ACPI_TYPE_INTEGER;
351 wq_params[0].integer.value = instance;
352
353 /*
354 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
355 * enable collection.
356 */
357 if (block->flags & ACPI_WMI_EXPENSIVE) {
358 wc_input.count = 1;
359 wc_input.pointer = wc_params;
360 wc_params[0].type = ACPI_TYPE_INTEGER;
361 wc_params[0].integer.value = 1;
362
363 strncat(wc_method, block->object_id, 2);
364
365 /*
366 * Some GUIDs break the specification by declaring themselves
367 * expensive, but have no corresponding WCxx method. So we
368 * should not fail if this happens.
369 */
370 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
371 if (ACPI_SUCCESS(wc_status))
372 wc_status = acpi_evaluate_object(handle, wc_method,
373 &wc_input, NULL);
374 }
375
376 strcpy(method, "WQ");
377 strncat(method, block->object_id, 2);
378
379 status = acpi_evaluate_object(handle, method, &input, out);
380
381 /*
382 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
383 * the WQxx method failed - we should disable collection anyway.
384 */
385 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
386 wc_params[0].integer.value = 0;
387 status = acpi_evaluate_object(handle,
388 wc_method, &wc_input, NULL);
389 }
390
391 return status;
392}
393EXPORT_SYMBOL_GPL(wmi_query_block);
394
395/**
396 * wmi_set_block - Write to a WMI block
397 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
398 * @instance: Instance index
399 * &in: Buffer containing new values for the data block
400 *
401 * Write the contents of the input buffer to an ACPI-WMI data block
402 */
403acpi_status wmi_set_block(const char *guid_string, u8 instance,
404const struct acpi_buffer *in)
405{
406 struct guid_block *block = NULL;
407 struct wmi_block *wblock = NULL;
408 acpi_handle handle;
409 struct acpi_object_list input;
410 union acpi_object params[2];
411 char method[4] = "WS";
412
413 if (!guid_string || !in)
414 return AE_BAD_DATA;
415
416 if (!find_guid(guid_string, &wblock))
417 return AE_ERROR;
418
419 block = &wblock->gblock;
420 handle = wblock->handle;
421
422 if (block->instance_count < instance)
423 return AE_BAD_PARAMETER;
424
425 /* Check GUID is a data block */
426 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
427 return AE_ERROR;
428
429 input.count = 2;
430 input.pointer = params;
431 params[0].type = ACPI_TYPE_INTEGER;
432 params[0].integer.value = instance;
433
434 if (block->flags & ACPI_WMI_STRING) {
435 params[1].type = ACPI_TYPE_STRING;
436 } else {
437 params[1].type = ACPI_TYPE_BUFFER;
438 }
439 params[1].buffer.length = in->length;
440 params[1].buffer.pointer = in->pointer;
441
442 strncat(method, block->object_id, 2);
443
444 return acpi_evaluate_object(handle, method, &input, NULL);
445}
446EXPORT_SYMBOL_GPL(wmi_set_block);
447
448/**
449 * wmi_install_notify_handler - Register handler for WMI events
450 * @handler: Function to handle notifications
451 * @data: Data to be returned to handler when event is fired
452 *
453 * Register a handler for events sent to the ACPI-WMI mapper device.
454 */
455acpi_status wmi_install_notify_handler(const char *guid,
456wmi_notify_handler handler, void *data)
457{
458 struct wmi_block *block;
459 acpi_status status;
460
461 if (!guid || !handler)
462 return AE_BAD_PARAMETER;
463
464 find_guid(guid, &block);
465 if (!block)
466 return AE_NOT_EXIST;
467
468 if (block->handler)
469 return AE_ALREADY_ACQUIRED;
470
471 block->handler = handler;
472 block->handler_data = data;
473
474 status = wmi_method_enable(block, 1);
475
476 return status;
477}
478EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
479
480/**
481 * wmi_uninstall_notify_handler - Unregister handler for WMI events
482 *
483 * Unregister handler for events sent to the ACPI-WMI mapper device.
484 */
485acpi_status wmi_remove_notify_handler(const char *guid)
486{
487 struct wmi_block *block;
488 acpi_status status;
489
490 if (!guid)
491 return AE_BAD_PARAMETER;
492
493 find_guid(guid, &block);
494 if (!block)
495 return AE_NOT_EXIST;
496
497 if (!block->handler)
498 return AE_NULL_ENTRY;
499
500 status = wmi_method_enable(block, 0);
501
502 block->handler = NULL;
503 block->handler_data = NULL;
504
505 return status;
506}
507EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
508
509/**
510 * wmi_get_event_data - Get WMI data associated with an event
511 *
512 * @event - Event to find
513 * &out - Buffer to hold event data
514 *
515 * Returns extra data associated with an event in WMI.
516 */
517acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
518{
519 struct acpi_object_list input;
520 union acpi_object params[1];
521 struct guid_block *gblock;
522 struct wmi_block *wblock;
523 struct list_head *p;
524
525 input.count = 1;
526 input.pointer = params;
527 params[0].type = ACPI_TYPE_INTEGER;
528 params[0].integer.value = event;
529
530 list_for_each(p, &wmi_blocks.list) {
531 wblock = list_entry(p, struct wmi_block, list);
532 gblock = &wblock->gblock;
533
534 if ((gblock->flags & ACPI_WMI_EVENT) &&
535 (gblock->notify_id == event))
536 return acpi_evaluate_object(wblock->handle, "_WED",
537 &input, out);
538 }
539
540 return AE_NOT_FOUND;
541}
542EXPORT_SYMBOL_GPL(wmi_get_event_data);
543
544/**
545 * wmi_has_guid - Check if a GUID is available
546 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
547 *
548 * Check if a given GUID is defined by _WDG
549 */
550bool wmi_has_guid(const char *guid_string)
551{
552 return find_guid(guid_string, NULL);
553}
554EXPORT_SYMBOL_GPL(wmi_has_guid);
555
556/*
557 * Parse the _WDG method for the GUID data blocks
558 */
559static __init acpi_status parse_wdg(acpi_handle handle)
560{
561 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
562 union acpi_object *obj;
563 struct guid_block *gblock;
564 struct wmi_block *wblock;
565 acpi_status status;
566 u32 i, total;
567
568 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
569
570 if (ACPI_FAILURE(status))
571 return status;
572
573 obj = (union acpi_object *) out.pointer;
574
575 if (obj->type != ACPI_TYPE_BUFFER)
576 return AE_ERROR;
577
578 total = obj->buffer.length / sizeof(struct guid_block);
579
580 gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
581 if (!gblock)
582 return AE_NO_MEMORY;
583
584 memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
585
586 for (i = 0; i < total; i++) {
587 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
588 if (!wblock)
589 return AE_NO_MEMORY;
590
591 wblock->gblock = gblock[i];
592 wblock->handle = handle;
593 list_add_tail(&wblock->list, &wmi_blocks.list);
594 }
595
596 kfree(out.pointer);
597 kfree(gblock);
598
599 return status;
600}
601
602/*
603 * WMI can have EmbeddedControl access regions. In which case, we just want to
604 * hand these off to the EC driver.
605 */
606static acpi_status
607acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
608 u32 bits, acpi_integer * value,
609 void *handler_context, void *region_context)
610{
611 int result = 0, i = 0;
612 u8 temp = 0;
613
614 if ((address > 0xFF) || !value)
615 return AE_BAD_PARAMETER;
616
617 if (function != ACPI_READ && function != ACPI_WRITE)
618 return AE_BAD_PARAMETER;
619
620 if (bits != 8)
621 return AE_BAD_PARAMETER;
622
623 if (function == ACPI_READ) {
624 result = ec_read(address, &temp);
625 (*value) |= ((acpi_integer)temp) << i;
626 } else {
627 temp = 0xff & ((*value) >> i);
628 result = ec_write(address, temp);
629 }
630
631 switch (result) {
632 case -EINVAL:
633 return AE_BAD_PARAMETER;
634 break;
635 case -ENODEV:
636 return AE_NOT_FOUND;
637 break;
638 case -ETIME:
639 return AE_TIME;
640 break;
641 default:
642 return AE_OK;
643 }
644}
645
646static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data)
647{
648 struct guid_block *block;
649 struct wmi_block *wblock;
650 struct list_head *p;
651 struct acpi_device *device = data;
652
653 list_for_each(p, &wmi_blocks.list) {
654 wblock = list_entry(p, struct wmi_block, list);
655 block = &wblock->gblock;
656
657 if ((block->flags & ACPI_WMI_EVENT) &&
658 (block->notify_id == event)) {
659 if (wblock->handler)
660 wblock->handler(event, wblock->handler_data);
661
662 acpi_bus_generate_netlink_event(
663 device->pnp.device_class, dev_name(&device->dev),
664 event, 0);
665 break;
666 }
667 }
668}
669
670static int acpi_wmi_remove(struct acpi_device *device, int type)
671{
672 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
673 acpi_wmi_notify);
674
675 acpi_remove_address_space_handler(device->handle,
676 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
677
678 return 0;
679}
680
681static int __init acpi_wmi_add(struct acpi_device *device)
682{
683 acpi_status status;
684 int result = 0;
685
686 status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
687 acpi_wmi_notify, device);
688 if (ACPI_FAILURE(status)) {
689 printk(KERN_ERR PREFIX "Error installing notify handler\n");
690 return -ENODEV;
691 }
692
693 status = acpi_install_address_space_handler(device->handle,
694 ACPI_ADR_SPACE_EC,
695 &acpi_wmi_ec_space_handler,
696 NULL, NULL);
697 if (ACPI_FAILURE(status))
698 return -ENODEV;
699
700 status = parse_wdg(device->handle);
701 if (ACPI_FAILURE(status)) {
702 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
703 return -ENODEV;
704 }
705
706 return result;
707}
708
709static int __init acpi_wmi_init(void)
710{
711 acpi_status result;
712
713 INIT_LIST_HEAD(&wmi_blocks.list);
714
715 if (acpi_disabled)
716 return -ENODEV;
717
718 result = acpi_bus_register_driver(&acpi_wmi_driver);
719
720 if (result < 0) {
721 printk(KERN_INFO PREFIX "Error loading mapper\n");
722 } else {
723 printk(KERN_INFO PREFIX "Mapper loaded\n");
724 }
725
726 return result;
727}
728
729static void __exit acpi_wmi_exit(void)
730{
731 struct list_head *p, *tmp;
732 struct wmi_block *wblock;
733
734 acpi_bus_unregister_driver(&acpi_wmi_driver);
735
736 list_for_each_safe(p, tmp, &wmi_blocks.list) {
737 wblock = list_entry(p, struct wmi_block, list);
738
739 list_del(p);
740 kfree(wblock);
741 }
742
743 printk(KERN_INFO PREFIX "Mapper unloaded\n");
744}
745
746subsys_initcall(acpi_wmi_init);
747module_exit(acpi_wmi_exit);