aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS19
-rw-r--r--drivers/acpi/Kconfig10
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/wmi.c710
-rw-r--r--drivers/misc/Kconfig25
-rw-r--r--drivers/misc/Makefile2
-rw-r--r--drivers/misc/acer-wmi.c1109
-rw-r--r--drivers/misc/tc1100-wmi.c290
-rw-r--r--include/linux/acpi.h20
9 files changed, 2186 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 0885aa2b095a..0819a579005c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -202,6 +202,13 @@ L: linux-scsi@vger.kernel.org
202W: http://www.adaptec.com/ 202W: http://www.adaptec.com/
203S: Supported 203S: Supported
204 204
205ACER WMI LAPTOP EXTRAS
206P: Carlos Corbacho
207M: carlos@strangeworlds.co.uk
208L: aceracpi@googlegroups.com (subscribers-only)
209W: http://code.google.com/p/aceracpi
210S: Maintained
211
205ACPI 212ACPI
206P: Len Brown 213P: Len Brown
207M: len.brown@intel.com 214M: len.brown@intel.com
@@ -252,6 +259,13 @@ L: linux-acpi@vger.kernel.org
252W: http://acpi.sourceforge.net/ 259W: http://acpi.sourceforge.net/
253S: Supported 260S: Supported
254 261
262ACPI WMI DRIVER
263P: Carlos Corbacho
264M: carlos@strangeworlds.co.uk
265L: linux-acpi@vger.kernel.org
266W: http://www.lesswatts.org/projects/acpi/
267S: Maintained
268
255ADM1025 HARDWARE MONITOR DRIVER 269ADM1025 HARDWARE MONITOR DRIVER
256P: Jean Delvare 270P: Jean Delvare
257M: khali@linux-fr.org 271M: khali@linux-fr.org
@@ -1785,6 +1799,11 @@ P: Jaroslav Kysela
1785M: perex@perex.cz 1799M: perex@perex.cz
1786S: Maintained 1800S: Maintained
1787 1801
1802HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER
1803P: Carlos Corbacho
1804M: carlos@strangeworlds.co.uk
1805S: Odd Fixes
1806
1788HPET: High Precision Event Timers driver (hpet.c) 1807HPET: High Precision Event Timers driver (hpet.c)
1789P: Clemens Ladisch 1808P: Clemens Ladisch
1790M: clemens@ladisch.de 1809M: clemens@ladisch.de
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 558372957fd3..0065f3794576 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -200,6 +200,16 @@ config ACPI_NUMA
200 depends on (X86 || IA64) 200 depends on (X86 || IA64)
201 default y if IA64_GENERIC || IA64_SGI_SN2 201 default y if IA64_GENERIC || IA64_SGI_SN2
202 202
203config ACPI_WMI
204 tristate "WMI (EXPERIMENTAL)"
205 depends on EXPERIMENTAL
206 help
207 This driver adds support for the ACPI-WMI mapper device (PNP0C14)
208 found on some systems.
209
210 NOTE: You will need another driver or userspace application on top of
211 this to actually use anything defined in the ACPI-WMI mapper.
212
203config ACPI_ASUS 213config ACPI_ASUS
204 tristate "ASUS/Medion Laptop Extras" 214 tristate "ASUS/Medion Laptop Extras"
205 depends on X86 215 depends on X86
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 456446f90077..f29812a86533 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_ACPI_THERMAL) += thermal.o
55obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o 55obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o
56obj-$(CONFIG_ACPI_DEBUG) += debug.o 56obj-$(CONFIG_ACPI_DEBUG) += debug.o
57obj-$(CONFIG_ACPI_NUMA) += numa.o 57obj-$(CONFIG_ACPI_NUMA) += numa.o
58obj-$(CONFIG_ACPI_WMI) += wmi.o
58obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o 59obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
59obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 60obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
60obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o 61obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c
new file mode 100644
index 000000000000..36b84ab418dd
--- /dev/null
+++ b/drivers/acpi/wmi.c
@@ -0,0 +1,710 @@
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
220/*
221 * Exported WMI functions
222 */
223/**
224 * wmi_evaluate_method - Evaluate a WMI method
225 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
226 * @instance: Instance index
227 * @method_id: Method ID to call
228 * &in: Buffer containing input for the method call
229 * &out: Empty buffer to return the method results
230 *
231 * Call an ACPI-WMI method
232 */
233acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
234u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
235{
236 struct guid_block *block = NULL;
237 struct wmi_block *wblock = NULL;
238 acpi_handle handle;
239 acpi_status status;
240 struct acpi_object_list input;
241 union acpi_object params[3];
242 char method[4] = "WM";
243
244 if (!find_guid(guid_string, &wblock))
245 return AE_BAD_ADDRESS;
246
247 block = &wblock->gblock;
248 handle = wblock->handle;
249
250 if (!block->flags & ACPI_WMI_METHOD)
251 return AE_BAD_DATA;
252
253 if (block->instance_count < instance)
254 return AE_BAD_PARAMETER;
255
256 input.count = 2;
257 input.pointer = params;
258 params[0].type = ACPI_TYPE_INTEGER;
259 params[0].integer.value = instance;
260 params[1].type = ACPI_TYPE_INTEGER;
261 params[1].integer.value = method_id;
262
263 if (in) {
264 input.count = 3;
265
266 if (block->flags & ACPI_WMI_STRING) {
267 params[2].type = ACPI_TYPE_STRING;
268 } else {
269 params[2].type = ACPI_TYPE_BUFFER;
270 }
271 params[2].buffer.length = in->length;
272 params[2].buffer.pointer = in->pointer;
273 }
274
275 strncat(method, block->object_id, 2);
276
277 status = acpi_evaluate_object(handle, method, &input, out);
278
279 return status;
280}
281EXPORT_SYMBOL_GPL(wmi_evaluate_method);
282
283/**
284 * wmi_query_block - Return contents of a WMI block
285 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
286 * @instance: Instance index
287 * &out: Empty buffer to return the contents of the data block to
288 *
289 * Return the contents of an ACPI-WMI data block to a buffer
290 */
291acpi_status wmi_query_block(const char *guid_string, u8 instance,
292struct acpi_buffer *out)
293{
294 struct guid_block *block = NULL;
295 struct wmi_block *wblock = NULL;
296 acpi_handle handle;
297 acpi_status status, wc_status = AE_ERROR;
298 struct acpi_object_list input, wc_input;
299 union acpi_object wc_params[1], wq_params[1];
300 char method[4];
301 char wc_method[4] = "WC";
302
303 if (!guid_string || !out)
304 return AE_BAD_PARAMETER;
305
306 if (!find_guid(guid_string, &wblock))
307 return AE_BAD_ADDRESS;
308
309 block = &wblock->gblock;
310 handle = wblock->handle;
311
312 if (block->instance_count < instance)
313 return AE_BAD_PARAMETER;
314
315 /* Check GUID is a data block */
316 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
317 return AE_BAD_ADDRESS;
318
319 input.count = 1;
320 input.pointer = wq_params;
321 wq_params[0].type = ACPI_TYPE_INTEGER;
322 wq_params[0].integer.value = instance;
323
324 /*
325 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
326 * enable collection.
327 */
328 if (block->flags & ACPI_WMI_EXPENSIVE) {
329 wc_input.count = 1;
330 wc_input.pointer = wc_params;
331 wc_params[0].type = ACPI_TYPE_INTEGER;
332 wc_params[0].integer.value = 1;
333
334 strncat(wc_method, block->object_id, 2);
335
336 /*
337 * Some GUIDs break the specification by declaring themselves
338 * expensive, but have no corresponding WCxx method. So we
339 * should not fail if this happens.
340 */
341 wc_status = acpi_evaluate_object(handle, wc_method,
342 &wc_input, NULL);
343 }
344
345 strcpy(method, "WQ");
346 strncat(method, block->object_id, 2);
347
348 status = acpi_evaluate_object(handle, method, NULL, out);
349
350 /*
351 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
352 * the WQxx method failed - we should disable collection anyway.
353 */
354 if ((block->flags & ACPI_WMI_EXPENSIVE) && wc_status) {
355 wc_params[0].integer.value = 0;
356 status = acpi_evaluate_object(handle,
357 wc_method, &wc_input, NULL);
358 }
359
360 return status;
361}
362EXPORT_SYMBOL_GPL(wmi_query_block);
363
364/**
365 * wmi_set_block - Write to a WMI block
366 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
367 * @instance: Instance index
368 * &in: Buffer containing new values for the data block
369 *
370 * Write the contents of the input buffer to an ACPI-WMI data block
371 */
372acpi_status wmi_set_block(const char *guid_string, u8 instance,
373const struct acpi_buffer *in)
374{
375 struct guid_block *block = NULL;
376 struct wmi_block *wblock = NULL;
377 acpi_handle handle;
378 struct acpi_object_list input;
379 union acpi_object params[2];
380 char method[4] = "WS";
381
382 if (!guid_string || !in)
383 return AE_BAD_DATA;
384
385 if (!find_guid(guid_string, &wblock))
386 return AE_BAD_ADDRESS;
387
388 block = &wblock->gblock;
389 handle = wblock->handle;
390
391 if (block->instance_count < instance)
392 return AE_BAD_PARAMETER;
393
394 /* Check GUID is a data block */
395 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
396 return AE_BAD_ADDRESS;
397
398 input.count = 2;
399 input.pointer = params;
400 params[0].type = ACPI_TYPE_INTEGER;
401 params[0].integer.value = instance;
402
403 if (block->flags & ACPI_WMI_STRING) {
404 params[1].type = ACPI_TYPE_STRING;
405 } else {
406 params[1].type = ACPI_TYPE_BUFFER;
407 }
408 params[1].buffer.length = in->length;
409 params[1].buffer.pointer = in->pointer;
410
411 strncat(method, block->object_id, 2);
412
413 return acpi_evaluate_object(handle, method, &input, NULL);
414}
415EXPORT_SYMBOL_GPL(wmi_set_block);
416
417/**
418 * wmi_install_notify_handler - Register handler for WMI events
419 * @handler: Function to handle notifications
420 * @data: Data to be returned to handler when event is fired
421 *
422 * Register a handler for events sent to the ACPI-WMI mapper device.
423 */
424acpi_status wmi_install_notify_handler(const char *guid,
425wmi_notify_handler handler, void *data)
426{
427 struct wmi_block *block;
428
429 if (!guid || !handler)
430 return AE_BAD_PARAMETER;
431
432 find_guid(guid, &block);
433 if (!block)
434 return AE_NOT_EXIST;
435
436 if (block->handler)
437 return AE_ALREADY_ACQUIRED;
438
439 block->handler = handler;
440 block->handler_data = data;
441
442 return AE_OK;
443}
444EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
445
446/**
447 * wmi_uninstall_notify_handler - Unregister handler for WMI events
448 *
449 * Unregister handler for events sent to the ACPI-WMI mapper device.
450 */
451acpi_status wmi_remove_notify_handler(const char *guid)
452{
453 struct wmi_block *block;
454
455 if (!guid)
456 return AE_BAD_PARAMETER;
457
458 find_guid(guid, &block);
459 if (!block)
460 return AE_NOT_EXIST;
461
462 if (!block->handler)
463 return AE_NULL_ENTRY;
464
465 block->handler = NULL;
466 block->handler_data = NULL;
467
468 return AE_OK;
469}
470EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
471
472/**
473 * wmi_get_event_data - Get WMI data associated with an event
474 *
475 * @event - Event to find
476 * &out - Buffer to hold event data
477 *
478 * Returns extra data associated with an event in WMI.
479 */
480acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
481{
482 struct acpi_object_list input;
483 union acpi_object params[1];
484 struct guid_block *gblock;
485 struct wmi_block *wblock;
486 struct list_head *p;
487
488 input.count = 1;
489 input.pointer = params;
490 params[0].type = ACPI_TYPE_INTEGER;
491 params[0].integer.value = event;
492
493 list_for_each(p, &wmi_blocks.list) {
494 wblock = list_entry(p, struct wmi_block, list);
495 gblock = &wblock->gblock;
496
497 if ((gblock->flags & ACPI_WMI_EVENT) &&
498 (gblock->notify_id == event))
499 return acpi_evaluate_object(wblock->handle, "_WED",
500 &input, out);
501 }
502
503 return AE_NOT_FOUND;
504}
505EXPORT_SYMBOL_GPL(wmi_get_event_data);
506
507/**
508 * wmi_has_guid - Check if a GUID is available
509 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
510 *
511 * Check if a given GUID is defined by _WDG
512 */
513bool wmi_has_guid(const char *guid_string)
514{
515 return find_guid(guid_string, NULL);
516}
517EXPORT_SYMBOL_GPL(wmi_has_guid);
518
519/*
520 * Parse the _WDG method for the GUID data blocks
521 */
522static __init acpi_status parse_wdg(acpi_handle handle)
523{
524 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
525 union acpi_object *obj;
526 struct guid_block *gblock;
527 struct wmi_block *wblock;
528 acpi_status status;
529 u32 i, total;
530
531 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
532
533 if (ACPI_FAILURE(status))
534 return status;
535
536 obj = (union acpi_object *) out.pointer;
537
538 if (obj->type != ACPI_TYPE_BUFFER)
539 return AE_ERROR;
540
541 total = obj->buffer.length / sizeof(struct guid_block);
542
543 gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
544 if (!gblock)
545 return AE_NO_MEMORY;
546
547 memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
548
549 for (i = 0; i < total; i++) {
550 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
551 if (!wblock)
552 return AE_NO_MEMORY;
553
554 wblock->gblock = gblock[i];
555 wblock->handle = handle;
556 list_add_tail(&wblock->list, &wmi_blocks.list);
557 }
558
559 kfree(out.pointer);
560 kfree(gblock);
561
562 return status;
563}
564
565/*
566 * WMI can have EmbeddedControl access regions. In which case, we just want to
567 * hand these off to the EC driver.
568 */
569static acpi_status
570acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
571 u32 bits, acpi_integer * value,
572 void *handler_context, void *region_context)
573{
574 int result = 0, i = 0;
575 u8 temp = 0;
576
577 if ((address > 0xFF) || !value)
578 return AE_BAD_PARAMETER;
579
580 if (function != ACPI_READ && function != ACPI_WRITE)
581 return AE_BAD_PARAMETER;
582
583 if (bits != 8)
584 return AE_BAD_PARAMETER;
585
586 if (function == ACPI_READ) {
587 result = ec_read(address, &temp);
588 (*value) |= ((acpi_integer)temp) << i;
589 } else {
590 temp = 0xff & ((*value) >> i);
591 result = ec_write(address, temp);
592 }
593
594 switch (result) {
595 case -EINVAL:
596 return AE_BAD_PARAMETER;
597 break;
598 case -ENODEV:
599 return AE_NOT_FOUND;
600 break;
601 case -ETIME:
602 return AE_TIME;
603 break;
604 default:
605 return AE_OK;
606 }
607}
608
609static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data)
610{
611 struct guid_block *block;
612 struct wmi_block *wblock;
613 struct list_head *p;
614 struct acpi_device *device = data;
615
616 list_for_each(p, &wmi_blocks.list) {
617 wblock = list_entry(p, struct wmi_block, list);
618 block = &wblock->gblock;
619
620 if ((block->flags & ACPI_WMI_EVENT) &&
621 (block->notify_id == event)) {
622 if (wblock->handler)
623 wblock->handler(event, wblock->handler_data);
624
625 acpi_bus_generate_netlink_event(
626 device->pnp.device_class, device->dev.bus_id,
627 event, 0);
628 break;
629 }
630 }
631}
632
633static int acpi_wmi_remove(struct acpi_device *device, int type)
634{
635 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
636 acpi_wmi_notify);
637
638 acpi_remove_address_space_handler(device->handle,
639 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
640
641 return 0;
642}
643
644static int __init acpi_wmi_add(struct acpi_device *device)
645{
646 acpi_status status;
647 int result = 0;
648
649 status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
650 acpi_wmi_notify, device);
651 if (ACPI_FAILURE(status)) {
652 printk(KERN_ERR PREFIX "Error installing notify handler\n");
653 return -ENODEV;
654 }
655
656 status = acpi_install_address_space_handler(device->handle,
657 ACPI_ADR_SPACE_EC,
658 &acpi_wmi_ec_space_handler,
659 NULL, NULL);
660 if (ACPI_FAILURE(status))
661 return -ENODEV;
662
663 status = parse_wdg(device->handle);
664 if (ACPI_FAILURE(status)) {
665 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
666 return -ENODEV;
667 }
668
669 return result;
670}
671
672static int __init acpi_wmi_init(void)
673{
674 acpi_status result;
675
676 if (acpi_disabled)
677 return -ENODEV;
678
679 INIT_LIST_HEAD(&wmi_blocks.list);
680
681 result = acpi_bus_register_driver(&acpi_wmi_driver);
682
683 if (result < 0) {
684 printk(KERN_INFO PREFIX "Error loading mapper\n");
685 } else {
686 printk(KERN_INFO PREFIX "Mapper loaded\n");
687 }
688
689 return result;
690}
691
692static void __exit acpi_wmi_exit(void)
693{
694 struct list_head *p, *tmp;
695 struct wmi_block *wblock;
696
697 acpi_bus_unregister_driver(&acpi_wmi_driver);
698
699 list_for_each_safe(p, tmp, &wmi_blocks.list) {
700 wblock = list_entry(p, struct wmi_block, list);
701
702 list_del(p);
703 kfree(wblock);
704 }
705
706 printk(KERN_INFO PREFIX "Mapper unloaded\n");
707}
708
709subsys_initcall(acpi_wmi_init);
710module_exit(acpi_wmi_exit);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f20c30cf99e0..78cd33861766 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -92,6 +92,22 @@ config TIFM_7XX1
92 To compile this driver as a module, choose M here: the module will 92 To compile this driver as a module, choose M here: the module will
93 be called tifm_7xx1. 93 be called tifm_7xx1.
94 94
95config ACER_WMI
96 tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
97 depends on X86
98 depends on EXPERIMENTAL
99 depends on ACPI
100 depends on ACPI_WMI
101 depends on LEDS_CLASS
102 depends on BACKLIGHT_CLASS_DEVICE
103 ---help---
104 This is a driver for newer Acer (and Wistron) laptops. It adds
105 wireless radio and bluetooth control, and on some laptops,
106 exposes the mail LED and LCD backlight.
107
108 If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
109 here.
110
95config ASUS_LAPTOP 111config ASUS_LAPTOP
96 tristate "Asus Laptop Extras (EXPERIMENTAL)" 112 tristate "Asus Laptop Extras (EXPERIMENTAL)"
97 depends on X86 113 depends on X86
@@ -126,6 +142,15 @@ config FUJITSU_LAPTOP
126 142
127 If you have a Fujitsu laptop, say Y or M here. 143 If you have a Fujitsu laptop, say Y or M here.
128 144
145config TC1100_WMI
146 tristate "HP Compaq TC1100 Tablet WMI Extras"
147 depends on X86 && !X86_64
148 depends on ACPI
149 depends on ACPI_WMI
150 ---help---
151 This is a driver for the WMI extensions (wireless and bluetooth power
152 control) of the HP Compaq TC1100 tablet.
153
129config MSI_LAPTOP 154config MSI_LAPTOP
130 tristate "MSI Laptop Extras" 155 tristate "MSI Laptop Extras"
131 depends on X86 156 depends on X86
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a9e8faffc1b1..1f41654aae4d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -6,8 +6,10 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
6obj-$(CONFIG_IBM_ASM) += ibmasm/ 6obj-$(CONFIG_IBM_ASM) += ibmasm/
7obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ 7obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
8obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o 8obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
9obj-$(CONFIG_ACER_WMI) += acer-wmi.o
9obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o 10obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
10obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 11obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
12obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
11obj-$(CONFIG_LKDTM) += lkdtm.o 13obj-$(CONFIG_LKDTM) += lkdtm.o
12obj-$(CONFIG_TIFM_CORE) += tifm_core.o 14obj-$(CONFIG_TIFM_CORE) += tifm_core.o
13obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o 15obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
new file mode 100644
index 000000000000..a4d677504250
--- /dev/null
+++ b/drivers/misc/acer-wmi.c
@@ -0,0 +1,1109 @@
1/*
2 * Acer WMI Laptop Extras
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * Based on acer_acpi:
7 * Copyright (C) 2005-2007 E.M. Smith
8 * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#define ACER_WMI_VERSION "0.1"
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/dmi.h>
32#include <linux/backlight.h>
33#include <linux/leds.h>
34#include <linux/platform_device.h>
35#include <linux/acpi.h>
36#include <linux/i8042.h>
37
38#include <acpi/acpi_drivers.h>
39
40MODULE_AUTHOR("Carlos Corbacho");
41MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
42MODULE_LICENSE("GPL");
43
44#define ACER_LOGPREFIX "acer-wmi: "
45#define ACER_ERR KERN_ERR ACER_LOGPREFIX
46#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
47#define ACER_INFO KERN_INFO ACER_LOGPREFIX
48
49/*
50 * The following defines quirks to get some specific functions to work
51 * which are known to not be supported over ACPI-WMI (such as the mail LED
52 * on WMID based Acer's)
53 */
54struct acer_quirks {
55 const char *vendor;
56 const char *model;
57 u16 quirks;
58};
59
60/*
61 * Magic Number
62 * Meaning is unknown - this number is required for writing to ACPI for AMW0
63 * (it's also used in acerhk when directly accessing the BIOS)
64 */
65#define ACER_AMW0_WRITE 0x9610
66
67/*
68 * Bit masks for the AMW0 interface
69 */
70#define ACER_AMW0_WIRELESS_MASK 0x35
71#define ACER_AMW0_BLUETOOTH_MASK 0x34
72#define ACER_AMW0_MAILLED_MASK 0x31
73
74/*
75 * Method IDs for WMID interface
76 */
77#define ACER_WMID_GET_WIRELESS_METHODID 1
78#define ACER_WMID_GET_BLUETOOTH_METHODID 2
79#define ACER_WMID_GET_BRIGHTNESS_METHODID 3
80#define ACER_WMID_SET_WIRELESS_METHODID 4
81#define ACER_WMID_SET_BLUETOOTH_METHODID 5
82#define ACER_WMID_SET_BRIGHTNESS_METHODID 6
83#define ACER_WMID_GET_THREEG_METHODID 10
84#define ACER_WMID_SET_THREEG_METHODID 11
85
86/*
87 * Acer ACPI method GUIDs
88 */
89#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
90#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
91#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
92
93MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
94MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
95
96/* Temporary workaround until the WMI sysfs interface goes in */
97MODULE_ALIAS("dmi:*:*Acer*:*:");
98
99/*
100 * Interface capability flags
101 */
102#define ACER_CAP_MAILLED (1<<0)
103#define ACER_CAP_WIRELESS (1<<1)
104#define ACER_CAP_BLUETOOTH (1<<2)
105#define ACER_CAP_BRIGHTNESS (1<<3)
106#define ACER_CAP_THREEG (1<<4)
107#define ACER_CAP_ANY (0xFFFFFFFF)
108
109/*
110 * Interface type flags
111 */
112enum interface_flags {
113 ACER_AMW0,
114 ACER_AMW0_V2,
115 ACER_WMID,
116};
117
118#define ACER_DEFAULT_WIRELESS 0
119#define ACER_DEFAULT_BLUETOOTH 0
120#define ACER_DEFAULT_MAILLED 0
121#define ACER_DEFAULT_THREEG 0
122
123static int max_brightness = 0xF;
124
125static int wireless = -1;
126static int bluetooth = -1;
127static int mailled = -1;
128static int brightness = -1;
129static int threeg = -1;
130static int force_series;
131
132module_param(mailled, int, 0444);
133module_param(wireless, int, 0444);
134module_param(bluetooth, int, 0444);
135module_param(brightness, int, 0444);
136module_param(threeg, int, 0444);
137module_param(force_series, int, 0444);
138MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
139MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
140MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
141MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
142MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
143MODULE_PARM_DESC(force_series, "Force a different laptop series");
144
145struct acer_data {
146 int mailled;
147 int wireless;
148 int bluetooth;
149 int threeg;
150 int brightness;
151};
152
153/* Each low-level interface must define at least some of the following */
154struct wmi_interface {
155 /* The WMI device type */
156 u32 type;
157
158 /* The capabilities this interface provides */
159 u32 capability;
160
161 /* Private data for the current interface */
162 struct acer_data data;
163};
164
165/* The static interface pointer, points to the currently detected interface */
166static struct wmi_interface *interface;
167
168/*
169 * Embedded Controller quirks
170 * Some laptops require us to directly access the EC to either enable or query
171 * features that are not available through WMI.
172 */
173
174struct quirk_entry {
175 u8 wireless;
176 u8 mailled;
177 u8 brightness;
178 u8 bluetooth;
179};
180
181static struct quirk_entry *quirks;
182
183static void set_quirks(void)
184{
185 if (quirks->mailled)
186 interface->capability |= ACER_CAP_MAILLED;
187
188 if (quirks->brightness)
189 interface->capability |= ACER_CAP_BRIGHTNESS;
190}
191
192static int dmi_matched(const struct dmi_system_id *dmi)
193{
194 quirks = dmi->driver_data;
195 return 0;
196}
197
198static struct quirk_entry quirk_unknown = {
199};
200
201static struct quirk_entry quirk_acer_travelmate_2490 = {
202 .mailled = 1,
203};
204
205/* This AMW0 laptop has no bluetooth */
206static struct quirk_entry quirk_medion_md_98300 = {
207 .wireless = 1,
208};
209
210static struct dmi_system_id acer_quirks[] = {
211 {
212 .callback = dmi_matched,
213 .ident = "Acer Aspire 3100",
214 .matches = {
215 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
216 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
217 },
218 .driver_data = &quirk_acer_travelmate_2490,
219 },
220 {
221 .callback = dmi_matched,
222 .ident = "Acer Aspire 5100",
223 .matches = {
224 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
225 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
226 },
227 .driver_data = &quirk_acer_travelmate_2490,
228 },
229 {
230 .callback = dmi_matched,
231 .ident = "Acer Aspire 5630",
232 .matches = {
233 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
234 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
235 },
236 .driver_data = &quirk_acer_travelmate_2490,
237 },
238 {
239 .callback = dmi_matched,
240 .ident = "Acer Aspire 5650",
241 .matches = {
242 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
243 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
244 },
245 .driver_data = &quirk_acer_travelmate_2490,
246 },
247 {
248 .callback = dmi_matched,
249 .ident = "Acer Aspire 5680",
250 .matches = {
251 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
252 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
253 },
254 .driver_data = &quirk_acer_travelmate_2490,
255 },
256 {
257 .callback = dmi_matched,
258 .ident = "Acer Aspire 9110",
259 .matches = {
260 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
261 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
262 },
263 .driver_data = &quirk_acer_travelmate_2490,
264 },
265 {
266 .callback = dmi_matched,
267 .ident = "Acer TravelMate 2490",
268 .matches = {
269 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
270 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
271 },
272 .driver_data = &quirk_acer_travelmate_2490,
273 },
274 {
275 .callback = dmi_matched,
276 .ident = "Medion MD 98300",
277 .matches = {
278 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
279 DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
280 },
281 .driver_data = &quirk_medion_md_98300,
282 },
283 {}
284};
285
286/* Find which quirks are needed for a particular vendor/ model pair */
287static void find_quirks(void)
288{
289 if (!force_series) {
290 dmi_check_system(acer_quirks);
291 } else if (force_series == 2490) {
292 quirks = &quirk_acer_travelmate_2490;
293 }
294
295 if (quirks == NULL)
296 quirks = &quirk_unknown;
297
298 set_quirks();
299}
300
301/*
302 * General interface convenience methods
303 */
304
305static bool has_cap(u32 cap)
306{
307 if ((interface->capability & cap) != 0)
308 return 1;
309
310 return 0;
311}
312
313/*
314 * AMW0 (V1) interface
315 */
316struct wmab_args {
317 u32 eax;
318 u32 ebx;
319 u32 ecx;
320 u32 edx;
321};
322
323struct wmab_ret {
324 u32 eax;
325 u32 ebx;
326 u32 ecx;
327 u32 edx;
328 u32 eex;
329};
330
331static acpi_status wmab_execute(struct wmab_args *regbuf,
332struct acpi_buffer *result)
333{
334 struct acpi_buffer input;
335 acpi_status status;
336 input.length = sizeof(struct wmab_args);
337 input.pointer = (u8 *)regbuf;
338
339 status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
340
341 return status;
342}
343
344static acpi_status AMW0_get_u32(u32 *value, u32 cap,
345struct wmi_interface *iface)
346{
347 int err;
348 u8 result;
349
350 switch (cap) {
351 case ACER_CAP_MAILLED:
352 switch (quirks->mailled) {
353 default:
354 err = ec_read(0xA, &result);
355 if (err)
356 return AE_ERROR;
357 *value = (result >> 7) & 0x1;
358 return AE_OK;
359 }
360 break;
361 case ACER_CAP_WIRELESS:
362 switch (quirks->wireless) {
363 case 1:
364 err = ec_read(0x7B, &result);
365 if (err)
366 return AE_ERROR;
367 *value = result & 0x1;
368 return AE_OK;
369 default:
370 err = ec_read(0xA, &result);
371 if (err)
372 return AE_ERROR;
373 *value = (result >> 2) & 0x1;
374 return AE_OK;
375 }
376 break;
377 case ACER_CAP_BLUETOOTH:
378 switch (quirks->bluetooth) {
379 default:
380 err = ec_read(0xA, &result);
381 if (err)
382 return AE_ERROR;
383 *value = (result >> 4) & 0x1;
384 return AE_OK;
385 }
386 break;
387 case ACER_CAP_BRIGHTNESS:
388 switch (quirks->brightness) {
389 default:
390 err = ec_read(0x83, &result);
391 if (err)
392 return AE_ERROR;
393 *value = result;
394 return AE_OK;
395 }
396 break;
397 default:
398 return AE_BAD_ADDRESS;
399 }
400 return AE_OK;
401}
402
403static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
404{
405 struct wmab_args args;
406
407 args.eax = ACER_AMW0_WRITE;
408 args.ebx = value ? (1<<8) : 0;
409 args.ecx = args.edx = 0;
410
411 switch (cap) {
412 case ACER_CAP_MAILLED:
413 if (value > 1)
414 return AE_BAD_PARAMETER;
415 args.ebx |= ACER_AMW0_MAILLED_MASK;
416 break;
417 case ACER_CAP_WIRELESS:
418 if (value > 1)
419 return AE_BAD_PARAMETER;
420 args.ebx |= ACER_AMW0_WIRELESS_MASK;
421 break;
422 case ACER_CAP_BLUETOOTH:
423 if (value > 1)
424 return AE_BAD_PARAMETER;
425 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
426 break;
427 case ACER_CAP_BRIGHTNESS:
428 if (value > max_brightness)
429 return AE_BAD_PARAMETER;
430 switch (quirks->brightness) {
431 case 1:
432 return ec_write(0x83, value);
433 default:
434 return AE_BAD_ADDRESS;
435 break;
436 }
437 default:
438 return AE_BAD_ADDRESS;
439 }
440
441 /* Actually do the set */
442 return wmab_execute(&args, NULL);
443}
444
445static acpi_status AMW0_find_mailled(void)
446{
447 struct wmab_args args;
448 struct wmab_ret ret;
449 acpi_status status = AE_OK;
450 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
451 union acpi_object *obj;
452
453 args.eax = 0x86;
454 args.ebx = args.ecx = args.edx = 0;
455
456 status = wmab_execute(&args, &out);
457 if (ACPI_FAILURE(status))
458 return status;
459
460 obj = (union acpi_object *) out.pointer;
461 if (obj && obj->type == ACPI_TYPE_BUFFER &&
462 obj->buffer.length == sizeof(struct wmab_ret)) {
463 ret = *((struct wmab_ret *) obj->buffer.pointer);
464 } else {
465 return AE_ERROR;
466 }
467
468 if (ret.eex & 0x1)
469 interface->capability |= ACER_CAP_MAILLED;
470
471 kfree(out.pointer);
472
473 return AE_OK;
474}
475
476static acpi_status AMW0_set_capabilities(void)
477{
478 struct wmab_args args;
479 struct wmab_ret ret;
480 acpi_status status = AE_OK;
481 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
482 union acpi_object *obj;
483
484 args.eax = ACER_AMW0_WRITE;
485 args.ecx = args.edx = 0;
486
487 args.ebx = 0xa2 << 8;
488 args.ebx |= ACER_AMW0_WIRELESS_MASK;
489
490 status = wmab_execute(&args, &out);
491 if (ACPI_FAILURE(status))
492 return status;
493
494 obj = (union acpi_object *) out.pointer;
495 if (obj && obj->type == ACPI_TYPE_BUFFER &&
496 obj->buffer.length == sizeof(struct wmab_ret)) {
497 ret = *((struct wmab_ret *) obj->buffer.pointer);
498 } else {
499 return AE_ERROR;
500 }
501
502 if (ret.eax & 0x1)
503 interface->capability |= ACER_CAP_WIRELESS;
504
505 args.ebx = 2 << 8;
506 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
507
508 status = wmab_execute(&args, &out);
509 if (ACPI_FAILURE(status))
510 return status;
511
512 obj = (union acpi_object *) out.pointer;
513 if (obj && obj->type == ACPI_TYPE_BUFFER
514 && obj->buffer.length == sizeof(struct wmab_ret)) {
515 ret = *((struct wmab_ret *) obj->buffer.pointer);
516 } else {
517 return AE_ERROR;
518 }
519
520 if (ret.eax & 0x1)
521 interface->capability |= ACER_CAP_BLUETOOTH;
522
523 kfree(out.pointer);
524
525 /*
526 * This appears to be safe to enable, since all Wistron based laptops
527 * appear to use the same EC register for brightness, even if they
528 * differ for wireless, etc
529 */
530 interface->capability |= ACER_CAP_BRIGHTNESS;
531
532 return AE_OK;
533}
534
535static struct wmi_interface AMW0_interface = {
536 .type = ACER_AMW0,
537};
538
539static struct wmi_interface AMW0_V2_interface = {
540 .type = ACER_AMW0_V2,
541};
542
543/*
544 * New interface (The WMID interface)
545 */
546static acpi_status
547WMI_execute_u32(u32 method_id, u32 in, u32 *out)
548{
549 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
550 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
551 union acpi_object *obj;
552 u32 tmp;
553 acpi_status status;
554
555 status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
556
557 if (ACPI_FAILURE(status))
558 return status;
559
560 obj = (union acpi_object *) result.pointer;
561 if (obj && obj->type == ACPI_TYPE_BUFFER &&
562 obj->buffer.length == sizeof(u32)) {
563 tmp = *((u32 *) obj->buffer.pointer);
564 } else {
565 tmp = 0;
566 }
567
568 if (out)
569 *out = tmp;
570
571 kfree(result.pointer);
572
573 return status;
574}
575
576static acpi_status WMID_get_u32(u32 *value, u32 cap,
577struct wmi_interface *iface)
578{
579 acpi_status status;
580 u8 tmp;
581 u32 result, method_id = 0;
582
583 switch (cap) {
584 case ACER_CAP_WIRELESS:
585 method_id = ACER_WMID_GET_WIRELESS_METHODID;
586 break;
587 case ACER_CAP_BLUETOOTH:
588 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
589 break;
590 case ACER_CAP_BRIGHTNESS:
591 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
592 break;
593 case ACER_CAP_THREEG:
594 method_id = ACER_WMID_GET_THREEG_METHODID;
595 break;
596 case ACER_CAP_MAILLED:
597 if (quirks->mailled == 1) {
598 ec_read(0x9f, &tmp);
599 *value = tmp & 0x1;
600 return 0;
601 }
602 default:
603 return AE_BAD_ADDRESS;
604 }
605 status = WMI_execute_u32(method_id, 0, &result);
606
607 if (ACPI_SUCCESS(status))
608 *value = (u8)result;
609
610 return status;
611}
612
613static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
614{
615 u32 method_id = 0;
616 char param;
617
618 switch (cap) {
619 case ACER_CAP_BRIGHTNESS:
620 if (value > max_brightness)
621 return AE_BAD_PARAMETER;
622 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
623 break;
624 case ACER_CAP_WIRELESS:
625 if (value > 1)
626 return AE_BAD_PARAMETER;
627 method_id = ACER_WMID_SET_WIRELESS_METHODID;
628 break;
629 case ACER_CAP_BLUETOOTH:
630 if (value > 1)
631 return AE_BAD_PARAMETER;
632 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
633 break;
634 case ACER_CAP_THREEG:
635 if (value > 1)
636 return AE_BAD_PARAMETER;
637 method_id = ACER_WMID_SET_THREEG_METHODID;
638 break;
639 case ACER_CAP_MAILLED:
640 if (value > 1)
641 return AE_BAD_PARAMETER;
642 if (quirks->mailled == 1) {
643 param = value ? 0x92 : 0x93;
644 i8042_command(&param, 0x1059);
645 return 0;
646 }
647 break;
648 default:
649 return AE_BAD_ADDRESS;
650 }
651 return WMI_execute_u32(method_id, (u32)value, NULL);
652}
653
654static acpi_status WMID_set_capabilities(void)
655{
656 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
657 union acpi_object *obj;
658 acpi_status status;
659 u32 devices;
660
661 status = wmi_query_block(WMID_GUID2, 1, &out);
662 if (ACPI_FAILURE(status))
663 return status;
664
665 obj = (union acpi_object *) out.pointer;
666 if (obj && obj->type == ACPI_TYPE_BUFFER &&
667 obj->buffer.length == sizeof(u32)) {
668 devices = *((u32 *) obj->buffer.pointer);
669 } else {
670 return AE_ERROR;
671 }
672
673 /* Not sure on the meaning of the relevant bits yet to detect these */
674 interface->capability |= ACER_CAP_WIRELESS;
675 interface->capability |= ACER_CAP_THREEG;
676
677 /* WMID always provides brightness methods */
678 interface->capability |= ACER_CAP_BRIGHTNESS;
679
680 if (devices & 0x10)
681 interface->capability |= ACER_CAP_BLUETOOTH;
682
683 if (!(devices & 0x20))
684 max_brightness = 0x9;
685
686 return status;
687}
688
689static struct wmi_interface wmid_interface = {
690 .type = ACER_WMID,
691};
692
693/*
694 * Generic Device (interface-independent)
695 */
696
697static acpi_status get_u32(u32 *value, u32 cap)
698{
699 acpi_status status = AE_BAD_ADDRESS;
700
701 switch (interface->type) {
702 case ACER_AMW0:
703 status = AMW0_get_u32(value, cap, interface);
704 break;
705 case ACER_AMW0_V2:
706 if (cap == ACER_CAP_MAILLED) {
707 status = AMW0_get_u32(value, cap, interface);
708 break;
709 }
710 case ACER_WMID:
711 status = WMID_get_u32(value, cap, interface);
712 break;
713 }
714
715 return status;
716}
717
718static acpi_status set_u32(u32 value, u32 cap)
719{
720 if (interface->capability & cap) {
721 switch (interface->type) {
722 case ACER_AMW0:
723 return AMW0_set_u32(value, cap, interface);
724 case ACER_AMW0_V2:
725 case ACER_WMID:
726 return WMID_set_u32(value, cap, interface);
727 default:
728 return AE_BAD_PARAMETER;
729 }
730 }
731 return AE_BAD_PARAMETER;
732}
733
734static void __init acer_commandline_init(void)
735{
736 /*
737 * These will all fail silently if the value given is invalid, or the
738 * capability isn't available on the given interface
739 */
740 set_u32(mailled, ACER_CAP_MAILLED);
741 set_u32(wireless, ACER_CAP_WIRELESS);
742 set_u32(bluetooth, ACER_CAP_BLUETOOTH);
743 set_u32(threeg, ACER_CAP_THREEG);
744 set_u32(brightness, ACER_CAP_BRIGHTNESS);
745}
746
747/*
748 * LED device (Mail LED only, no other LEDs known yet)
749 */
750static void mail_led_set(struct led_classdev *led_cdev,
751enum led_brightness value)
752{
753 set_u32(value, ACER_CAP_MAILLED);
754}
755
756static struct led_classdev mail_led = {
757 .name = "acer-mail:green",
758 .brightness_set = mail_led_set,
759};
760
761static int __init acer_led_init(struct device *dev)
762{
763 return led_classdev_register(dev, &mail_led);
764}
765
766static void acer_led_exit(void)
767{
768 led_classdev_unregister(&mail_led);
769}
770
771/*
772 * Backlight device
773 */
774static struct backlight_device *acer_backlight_device;
775
776static int read_brightness(struct backlight_device *bd)
777{
778 u32 value;
779 get_u32(&value, ACER_CAP_BRIGHTNESS);
780 return value;
781}
782
783static int update_bl_status(struct backlight_device *bd)
784{
785 set_u32(bd->props.brightness, ACER_CAP_BRIGHTNESS);
786 return 0;
787}
788
789static struct backlight_ops acer_bl_ops = {
790 .get_brightness = read_brightness,
791 .update_status = update_bl_status,
792};
793
794static int __init acer_backlight_init(struct device *dev)
795{
796 struct backlight_device *bd;
797
798 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
799 if (IS_ERR(bd)) {
800 printk(ACER_ERR "Could not register Acer backlight device\n");
801 acer_backlight_device = NULL;
802 return PTR_ERR(bd);
803 }
804
805 acer_backlight_device = bd;
806
807 bd->props.max_brightness = max_brightness;
808 bd->props.brightness = read_brightness(NULL);
809 backlight_update_status(bd);
810 return 0;
811}
812
813static void __exit acer_backlight_exit(void)
814{
815 backlight_device_unregister(acer_backlight_device);
816}
817
818/*
819 * Read/ write bool sysfs macro
820 */
821#define show_set_bool(value, cap) \
822static ssize_t \
823show_bool_##value(struct device *dev, struct device_attribute *attr, \
824 char *buf) \
825{ \
826 u32 result; \
827 acpi_status status = get_u32(&result, cap); \
828 if (ACPI_SUCCESS(status)) \
829 return sprintf(buf, "%u\n", result); \
830 return sprintf(buf, "Read error\n"); \
831} \
832\
833static ssize_t \
834set_bool_##value(struct device *dev, struct device_attribute *attr, \
835 const char *buf, size_t count) \
836{ \
837 u32 tmp = simple_strtoul(buf, NULL, 10); \
838 acpi_status status = set_u32(tmp, cap); \
839 if (ACPI_FAILURE(status)) \
840 return -EINVAL; \
841 return count; \
842} \
843static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
844 show_bool_##value, set_bool_##value);
845
846show_set_bool(wireless, ACER_CAP_WIRELESS);
847show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
848show_set_bool(threeg, ACER_CAP_THREEG);
849
850/*
851 * Read interface sysfs macro
852 */
853static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
854 char *buf)
855{
856 switch (interface->type) {
857 case ACER_AMW0:
858 return sprintf(buf, "AMW0\n");
859 case ACER_AMW0_V2:
860 return sprintf(buf, "AMW0 v2\n");
861 case ACER_WMID:
862 return sprintf(buf, "WMID\n");
863 default:
864 return sprintf(buf, "Error!\n");
865 }
866}
867
868static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
869 show_interface, NULL);
870
871/*
872 * Platform device
873 */
874static int __devinit acer_platform_probe(struct platform_device *device)
875{
876 int err;
877
878 if (has_cap(ACER_CAP_MAILLED)) {
879 err = acer_led_init(&device->dev);
880 if (err)
881 goto error_mailled;
882 }
883
884 if (has_cap(ACER_CAP_BRIGHTNESS)) {
885 err = acer_backlight_init(&device->dev);
886 if (err)
887 goto error_brightness;
888 }
889
890 return 0;
891
892error_brightness:
893 acer_led_exit();
894error_mailled:
895 return err;
896}
897
898static int acer_platform_remove(struct platform_device *device)
899{
900 if (has_cap(ACER_CAP_MAILLED))
901 acer_led_exit();
902 if (has_cap(ACER_CAP_BRIGHTNESS))
903 acer_backlight_exit();
904 return 0;
905}
906
907static int acer_platform_suspend(struct platform_device *dev,
908pm_message_t state)
909{
910 u32 value;
911 struct acer_data *data = &interface->data;
912
913 if (!data)
914 return -ENOMEM;
915
916 if (has_cap(ACER_CAP_WIRELESS)) {
917 get_u32(&value, ACER_CAP_WIRELESS);
918 data->wireless = value;
919 }
920
921 if (has_cap(ACER_CAP_BLUETOOTH)) {
922 get_u32(&value, ACER_CAP_BLUETOOTH);
923 data->bluetooth = value;
924 }
925
926 if (has_cap(ACER_CAP_MAILLED)) {
927 get_u32(&value, ACER_CAP_MAILLED);
928 data->mailled = value;
929 }
930
931 if (has_cap(ACER_CAP_BRIGHTNESS)) {
932 get_u32(&value, ACER_CAP_BRIGHTNESS);
933 data->brightness = value;
934 }
935
936 return 0;
937}
938
939static int acer_platform_resume(struct platform_device *device)
940{
941 struct acer_data *data = &interface->data;
942
943 if (!data)
944 return -ENOMEM;
945
946 if (has_cap(ACER_CAP_WIRELESS))
947 set_u32(data->wireless, ACER_CAP_WIRELESS);
948
949 if (has_cap(ACER_CAP_BLUETOOTH))
950 set_u32(data->bluetooth, ACER_CAP_BLUETOOTH);
951
952 if (has_cap(ACER_CAP_THREEG))
953 set_u32(data->threeg, ACER_CAP_THREEG);
954
955 if (has_cap(ACER_CAP_MAILLED))
956 set_u32(data->mailled, ACER_CAP_MAILLED);
957
958 if (has_cap(ACER_CAP_BRIGHTNESS))
959 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
960
961 return 0;
962}
963
964static struct platform_driver acer_platform_driver = {
965 .driver = {
966 .name = "acer-wmi",
967 .owner = THIS_MODULE,
968 },
969 .probe = acer_platform_probe,
970 .remove = acer_platform_remove,
971 .suspend = acer_platform_suspend,
972 .resume = acer_platform_resume,
973};
974
975static struct platform_device *acer_platform_device;
976
977static int remove_sysfs(struct platform_device *device)
978{
979 if (has_cap(ACER_CAP_WIRELESS))
980 device_remove_file(&device->dev, &dev_attr_wireless);
981
982 if (has_cap(ACER_CAP_BLUETOOTH))
983 device_remove_file(&device->dev, &dev_attr_bluetooth);
984
985 if (has_cap(ACER_CAP_THREEG))
986 device_remove_file(&device->dev, &dev_attr_threeg);
987
988 device_remove_file(&device->dev, &dev_attr_interface);
989
990 return 0;
991}
992
993static int create_sysfs(void)
994{
995 int retval = -ENOMEM;
996
997 if (has_cap(ACER_CAP_WIRELESS)) {
998 retval = device_create_file(&acer_platform_device->dev,
999 &dev_attr_wireless);
1000 if (retval)
1001 goto error_sysfs;
1002 }
1003
1004 if (has_cap(ACER_CAP_BLUETOOTH)) {
1005 retval = device_create_file(&acer_platform_device->dev,
1006 &dev_attr_bluetooth);
1007 if (retval)
1008 goto error_sysfs;
1009 }
1010
1011 if (has_cap(ACER_CAP_THREEG)) {
1012 retval = device_create_file(&acer_platform_device->dev,
1013 &dev_attr_threeg);
1014 if (retval)
1015 goto error_sysfs;
1016 }
1017
1018 retval = device_create_file(&acer_platform_device->dev,
1019 &dev_attr_interface);
1020 if (retval)
1021 goto error_sysfs;
1022
1023 return 0;
1024
1025error_sysfs:
1026 remove_sysfs(acer_platform_device);
1027 return retval;
1028}
1029
1030static int __init acer_wmi_init(void)
1031{
1032 int err;
1033
1034 printk(ACER_INFO "Acer Laptop ACPI-WMI Extras version %s\n",
1035 ACER_WMI_VERSION);
1036
1037 /*
1038 * Detect which ACPI-WMI interface we're using.
1039 */
1040 if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1041 interface = &AMW0_V2_interface;
1042
1043 if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1044 interface = &wmid_interface;
1045
1046 if (wmi_has_guid(WMID_GUID2) && interface) {
1047 if (ACPI_FAILURE(WMID_set_capabilities())) {
1048 printk(ACER_ERR "Unable to detect available devices\n");
1049 return -ENODEV;
1050 }
1051 } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1052 printk(ACER_ERR "Unable to detect available devices\n");
1053 return -ENODEV;
1054 }
1055
1056 if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1057 interface = &AMW0_interface;
1058
1059 if (ACPI_FAILURE(AMW0_set_capabilities())) {
1060 printk(ACER_ERR "Unable to detect available devices\n");
1061 return -ENODEV;
1062 }
1063 }
1064
1065 if (wmi_has_guid(AMW0_GUID1)) {
1066 if (ACPI_FAILURE(AMW0_find_mailled()))
1067 printk(ACER_ERR "Unable to detect mail LED\n");
1068 }
1069
1070 find_quirks();
1071
1072 if (!interface) {
1073 printk(ACER_ERR "No or unsupported WMI interface, unable to ");
1074 printk(KERN_CONT "load.\n");
1075 return -ENODEV;
1076 }
1077
1078 if (platform_driver_register(&acer_platform_driver)) {
1079 printk(ACER_ERR "Unable to register platform driver.\n");
1080 goto error_platform_register;
1081 }
1082 acer_platform_device = platform_device_alloc("acer-wmi", -1);
1083 platform_device_add(acer_platform_device);
1084
1085 err = create_sysfs();
1086 if (err)
1087 return err;
1088
1089 /* Override any initial settings with values from the commandline */
1090 acer_commandline_init();
1091
1092 return 0;
1093
1094error_platform_register:
1095 return -ENODEV;
1096}
1097
1098static void __exit acer_wmi_exit(void)
1099{
1100 remove_sysfs(acer_platform_device);
1101 platform_device_del(acer_platform_device);
1102 platform_driver_unregister(&acer_platform_driver);
1103
1104 printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1105 return;
1106}
1107
1108module_init(acer_wmi_init);
1109module_exit(acer_wmi_exit);
diff --git a/drivers/misc/tc1100-wmi.c b/drivers/misc/tc1100-wmi.c
new file mode 100644
index 000000000000..f25e4c974dcf
--- /dev/null
+++ b/drivers/misc/tc1100-wmi.c
@@ -0,0 +1,290 @@
1/*
2 * HP Compaq TC1100 Tablet WMI Extras Driver
3 *
4 * Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 * Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com>
6 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
7 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
8 *
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 (at
14 * your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 *
25 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 */
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/types.h>
32#include <acpi/acpi.h>
33#include <acpi/actypes.h>
34#include <acpi/acpi_bus.h>
35#include <acpi/acpi_drivers.h>
36#include <linux/platform_device.h>
37
38#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
39
40#define TC1100_INSTANCE_WIRELESS 1
41#define TC1100_INSTANCE_JOGDIAL 2
42
43#define TC1100_LOGPREFIX "tc1100-wmi: "
44#define TC1100_INFO KERN_INFO TC1100_LOGPREFIX
45
46MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
47MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
48MODULE_LICENSE("GPL");
49MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
50
51static int tc1100_probe(struct platform_device *device);
52static int tc1100_remove(struct platform_device *device);
53static int tc1100_suspend(struct platform_device *device, pm_message_t state);
54static int tc1100_resume(struct platform_device *device);
55
56static struct platform_driver tc1100_driver = {
57 .driver = {
58 .name = "tc1100-wmi",
59 .owner = THIS_MODULE,
60 },
61 .probe = tc1100_probe,
62 .remove = tc1100_remove,
63 .suspend = tc1100_suspend,
64 .resume = tc1100_resume,
65};
66
67static struct platform_device *tc1100_device;
68
69struct tc1100_data {
70 u32 wireless;
71 u32 jogdial;
72};
73
74static struct tc1100_data suspend_data;
75
76/* --------------------------------------------------------------------------
77 Device Management
78 -------------------------------------------------------------------------- */
79
80static int get_state(u32 *out, u8 instance)
81{
82 u32 tmp;
83 acpi_status status;
84 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
85 union acpi_object *obj;
86
87 if (!out)
88 return -EINVAL;
89
90 if (instance > 2)
91 return -ENODEV;
92
93 status = wmi_query_block(GUID, instance, &result);
94 if (ACPI_FAILURE(status))
95 return -ENODEV;
96
97 obj = (union acpi_object *) result.pointer;
98 if (obj && obj->type == ACPI_TYPE_BUFFER &&
99 obj->buffer.length == sizeof(u32)) {
100 tmp = *((u32 *) obj->buffer.pointer);
101 } else {
102 tmp = 0;
103 }
104
105 if (result.length > 0 && result.pointer)
106 kfree(result.pointer);
107
108 switch (instance) {
109 case TC1100_INSTANCE_WIRELESS:
110 *out = (tmp == 3) ? 1 : 0;
111 return 0;
112 case TC1100_INSTANCE_JOGDIAL:
113 *out = (tmp == 1) ? 1 : 0;
114 return 0;
115 default:
116 return -ENODEV;
117 }
118}
119
120static int set_state(u32 *in, u8 instance)
121{
122 u32 value;
123 acpi_status status;
124 struct acpi_buffer input;
125
126 if (!in)
127 return -EINVAL;
128
129 if (instance > 2)
130 return -ENODEV;
131
132 switch (instance) {
133 case TC1100_INSTANCE_WIRELESS:
134 value = (*in) ? 1 : 2;
135 break;
136 case TC1100_INSTANCE_JOGDIAL:
137 value = (*in) ? 0 : 1;
138 break;
139 default:
140 return -ENODEV;
141 }
142
143 input.length = sizeof(u32);
144 input.pointer = &value;
145
146 status = wmi_set_block(GUID, instance, &input);
147 if (ACPI_FAILURE(status))
148 return -ENODEV;
149
150 return 0;
151}
152
153/* --------------------------------------------------------------------------
154 FS Interface (/sys)
155 -------------------------------------------------------------------------- */
156
157/*
158 * Read/ write bool sysfs macro
159 */
160#define show_set_bool(value, instance) \
161static ssize_t \
162show_bool_##value(struct device *dev, struct device_attribute *attr, \
163 char *buf) \
164{ \
165 u32 result; \
166 acpi_status status = get_state(&result, instance); \
167 if (ACPI_SUCCESS(status)) \
168 return sprintf(buf, "%d\n", result); \
169 return sprintf(buf, "Read error\n"); \
170} \
171\
172static ssize_t \
173set_bool_##value(struct device *dev, struct device_attribute *attr, \
174 const char *buf, size_t count) \
175{ \
176 u32 tmp = simple_strtoul(buf, NULL, 10); \
177 acpi_status status = set_state(&tmp, instance); \
178 if (ACPI_FAILURE(status)) \
179 return -EINVAL; \
180 return count; \
181} \
182static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
183 show_bool_##value, set_bool_##value);
184
185show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
186show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
187
188static void remove_fs(void)
189{
190 device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
191 device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
192}
193
194static int add_fs(void)
195{
196 int ret;
197
198 ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
199 if (ret)
200 goto add_sysfs_error;
201
202 ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
203 if (ret)
204 goto add_sysfs_error;
205
206 return ret;
207
208add_sysfs_error:
209 remove_fs();
210 return ret;
211}
212
213/* --------------------------------------------------------------------------
214 Driver Model
215 -------------------------------------------------------------------------- */
216
217static int tc1100_probe(struct platform_device *device)
218{
219 int result = 0;
220
221 result = add_fs();
222 return result;
223}
224
225
226static int tc1100_remove(struct platform_device *device)
227{
228 remove_fs();
229 return 0;
230}
231
232static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
233{
234 int ret;
235
236 ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
237 if (ret)
238 return ret;
239
240 ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
241 if (ret)
242 return ret;
243
244 return ret;
245}
246
247static int tc1100_resume(struct platform_device *dev)
248{
249 int ret;
250
251 ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
252 if (ret)
253 return ret;
254
255 ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
256 if (ret)
257 return ret;
258
259 return ret;
260}
261
262static int __init tc1100_init(void)
263{
264 int result = 0;
265
266 if (!wmi_has_guid(GUID))
267 return -ENODEV;
268
269 result = platform_driver_register(&tc1100_driver);
270 if (result)
271 return result;
272
273 tc1100_device = platform_device_alloc("tc1100-wmi", -1);
274 platform_device_add(tc1100_device);
275
276 printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
277
278 return result;
279}
280
281static void __exit tc1100_exit(void)
282{
283 platform_device_del(tc1100_device);
284 platform_driver_unregister(&tc1100_driver);
285
286 printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
287}
288
289module_init(tc1100_init);
290module_exit(tc1100_exit);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index cf13bec517b7..5a5a13b64d09 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -193,6 +193,26 @@ extern int ec_transaction(u8 command,
193 193
194#endif /*CONFIG_ACPI_EC*/ 194#endif /*CONFIG_ACPI_EC*/
195 195
196#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
197
198typedef void (*wmi_notify_handler) (u32 value, void *context);
199
200extern acpi_status wmi_evaluate_method(const char *guid, u8 instance,
201 u32 method_id,
202 const struct acpi_buffer *in,
203 struct acpi_buffer *out);
204extern acpi_status wmi_query_block(const char *guid, u8 instance,
205 struct acpi_buffer *out);
206extern acpi_status wmi_set_block(const char *guid, u8 instance,
207 const struct acpi_buffer *in);
208extern acpi_status wmi_install_notify_handler(const char *guid,
209 wmi_notify_handler handler, void *data);
210extern acpi_status wmi_remove_notify_handler(const char *guid);
211extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out);
212extern bool wmi_has_guid(const char *guid);
213
214#endif /* CONFIG_ACPI_WMI */
215
196extern int acpi_blacklisted(void); 216extern int acpi_blacklisted(void);
197#ifdef CONFIG_DMI 217#ifdef CONFIG_DMI
198extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); 218extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d);