1b4f9fe12SLen Brown /* 2b4f9fe12SLen Brown * ACPI-WMI mapping driver 3b4f9fe12SLen Brown * 4b4f9fe12SLen Brown * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> 5b4f9fe12SLen Brown * 6b4f9fe12SLen Brown * GUID parsing code from ldm.c is: 7b4f9fe12SLen Brown * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 8b4f9fe12SLen Brown * Copyright (c) 2001-2007 Anton Altaparmakov 9b4f9fe12SLen Brown * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 10b4f9fe12SLen Brown * 11b4f9fe12SLen Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12b4f9fe12SLen Brown * 13b4f9fe12SLen Brown * This program is free software; you can redistribute it and/or modify 14b4f9fe12SLen Brown * it under the terms of the GNU General Public License as published by 15b4f9fe12SLen Brown * the Free Software Foundation; either version 2 of the License, or (at 16b4f9fe12SLen Brown * your option) any later version. 17b4f9fe12SLen Brown * 18b4f9fe12SLen Brown * This program is distributed in the hope that it will be useful, but 19b4f9fe12SLen Brown * WITHOUT ANY WARRANTY; without even the implied warranty of 20b4f9fe12SLen Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21b4f9fe12SLen Brown * General Public License for more details. 22b4f9fe12SLen Brown * 23b4f9fe12SLen Brown * You should have received a copy of the GNU General Public License along 24b4f9fe12SLen Brown * with this program; if not, write to the Free Software Foundation, Inc., 25b4f9fe12SLen Brown * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 26b4f9fe12SLen Brown * 27b4f9fe12SLen Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28b4f9fe12SLen Brown */ 29b4f9fe12SLen Brown 30b4f9fe12SLen Brown #include <linux/kernel.h> 31b4f9fe12SLen Brown #include <linux/init.h> 32b4f9fe12SLen Brown #include <linux/types.h> 331caab3c1SMatthew Garrett #include <linux/device.h> 34b4f9fe12SLen Brown #include <linux/list.h> 35b4f9fe12SLen Brown #include <linux/acpi.h> 36b4f9fe12SLen Brown #include <acpi/acpi_bus.h> 37b4f9fe12SLen Brown #include <acpi/acpi_drivers.h> 38b4f9fe12SLen Brown 39b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi"); 40b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 41b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 42b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 43b4f9fe12SLen Brown 44b4f9fe12SLen Brown #define ACPI_WMI_CLASS "wmi" 45b4f9fe12SLen Brown 46b4f9fe12SLen Brown #define PREFIX "ACPI: WMI: " 47b4f9fe12SLen Brown 48b4f9fe12SLen Brown static DEFINE_MUTEX(wmi_data_lock); 49b4f9fe12SLen Brown 50b4f9fe12SLen Brown struct guid_block { 51b4f9fe12SLen Brown char guid[16]; 52b4f9fe12SLen Brown union { 53b4f9fe12SLen Brown char object_id[2]; 54b4f9fe12SLen Brown struct { 55b4f9fe12SLen Brown unsigned char notify_id; 56b4f9fe12SLen Brown unsigned char reserved; 57b4f9fe12SLen Brown }; 58b4f9fe12SLen Brown }; 59b4f9fe12SLen Brown u8 instance_count; 60b4f9fe12SLen Brown u8 flags; 61b4f9fe12SLen Brown }; 62b4f9fe12SLen Brown 63b4f9fe12SLen Brown struct wmi_block { 64b4f9fe12SLen Brown struct list_head list; 65b4f9fe12SLen Brown struct guid_block gblock; 66b4f9fe12SLen Brown acpi_handle handle; 67b4f9fe12SLen Brown wmi_notify_handler handler; 68b4f9fe12SLen Brown void *handler_data; 691caab3c1SMatthew Garrett struct device *dev; 70b4f9fe12SLen Brown }; 71b4f9fe12SLen Brown 72b4f9fe12SLen Brown static struct wmi_block wmi_blocks; 73b4f9fe12SLen Brown 74b4f9fe12SLen Brown /* 75b4f9fe12SLen Brown * If the GUID data block is marked as expensive, we must enable and 76b4f9fe12SLen Brown * explicitily disable data collection. 77b4f9fe12SLen Brown */ 78b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE 0x1 79b4f9fe12SLen Brown #define ACPI_WMI_METHOD 0x2 /* GUID is a method */ 80b4f9fe12SLen Brown #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ 81b4f9fe12SLen Brown #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ 82b4f9fe12SLen Brown 83b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type); 84b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device); 85f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event); 86b4f9fe12SLen Brown 87b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 88b4f9fe12SLen Brown {"PNP0C14", 0}, 89b4f9fe12SLen Brown {"pnp0c14", 0}, 90b4f9fe12SLen Brown {"", 0}, 91b4f9fe12SLen Brown }; 92b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 93b4f9fe12SLen Brown 94b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = { 95b4f9fe12SLen Brown .name = "wmi", 96b4f9fe12SLen Brown .class = ACPI_WMI_CLASS, 97b4f9fe12SLen Brown .ids = wmi_device_ids, 98b4f9fe12SLen Brown .ops = { 99b4f9fe12SLen Brown .add = acpi_wmi_add, 100b4f9fe12SLen Brown .remove = acpi_wmi_remove, 101f61bb939SBjorn Helgaas .notify = acpi_wmi_notify, 102b4f9fe12SLen Brown }, 103b4f9fe12SLen Brown }; 104b4f9fe12SLen Brown 105b4f9fe12SLen Brown /* 106b4f9fe12SLen Brown * GUID parsing functions 107b4f9fe12SLen Brown */ 108b4f9fe12SLen Brown 109b4f9fe12SLen Brown /** 110b4f9fe12SLen Brown * wmi_parse_hexbyte - Convert a ASCII hex number to a byte 111b4f9fe12SLen Brown * @src: Pointer to at least 2 characters to convert. 112b4f9fe12SLen Brown * 113b4f9fe12SLen Brown * Convert a two character ASCII hex string to a number. 114b4f9fe12SLen Brown * 115b4f9fe12SLen Brown * Return: 0-255 Success, the byte was parsed correctly 116b4f9fe12SLen Brown * -1 Error, an invalid character was supplied 117b4f9fe12SLen Brown */ 118b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src) 119b4f9fe12SLen Brown { 120b4f9fe12SLen Brown unsigned int x; /* For correct wrapping */ 121b4f9fe12SLen Brown int h; 122b4f9fe12SLen Brown 123b4f9fe12SLen Brown /* high part */ 124b4f9fe12SLen Brown x = src[0]; 125b4f9fe12SLen Brown if (x - '0' <= '9' - '0') { 126b4f9fe12SLen Brown h = x - '0'; 127b4f9fe12SLen Brown } else if (x - 'a' <= 'f' - 'a') { 128b4f9fe12SLen Brown h = x - 'a' + 10; 129b4f9fe12SLen Brown } else if (x - 'A' <= 'F' - 'A') { 130b4f9fe12SLen Brown h = x - 'A' + 10; 131b4f9fe12SLen Brown } else { 132b4f9fe12SLen Brown return -1; 133b4f9fe12SLen Brown } 134b4f9fe12SLen Brown h <<= 4; 135b4f9fe12SLen Brown 136b4f9fe12SLen Brown /* low part */ 137b4f9fe12SLen Brown x = src[1]; 138b4f9fe12SLen Brown if (x - '0' <= '9' - '0') 139b4f9fe12SLen Brown return h | (x - '0'); 140b4f9fe12SLen Brown if (x - 'a' <= 'f' - 'a') 141b4f9fe12SLen Brown return h | (x - 'a' + 10); 142b4f9fe12SLen Brown if (x - 'A' <= 'F' - 'A') 143b4f9fe12SLen Brown return h | (x - 'A' + 10); 144b4f9fe12SLen Brown return -1; 145b4f9fe12SLen Brown } 146b4f9fe12SLen Brown 147b4f9fe12SLen Brown /** 148b4f9fe12SLen Brown * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary 149b4f9fe12SLen Brown * @src: Memory block holding binary GUID (16 bytes) 150b4f9fe12SLen Brown * @dest: Memory block to hold byte swapped binary GUID (16 bytes) 151b4f9fe12SLen Brown * 152b4f9fe12SLen Brown * Byte swap a binary GUID to match it's real GUID value 153b4f9fe12SLen Brown */ 154b4f9fe12SLen Brown static void wmi_swap_bytes(u8 *src, u8 *dest) 155b4f9fe12SLen Brown { 156b4f9fe12SLen Brown int i; 157b4f9fe12SLen Brown 158b4f9fe12SLen Brown for (i = 0; i <= 3; i++) 159b4f9fe12SLen Brown memcpy(dest + i, src + (3 - i), 1); 160b4f9fe12SLen Brown 161b4f9fe12SLen Brown for (i = 0; i <= 1; i++) 162b4f9fe12SLen Brown memcpy(dest + 4 + i, src + (5 - i), 1); 163b4f9fe12SLen Brown 164b4f9fe12SLen Brown for (i = 0; i <= 1; i++) 165b4f9fe12SLen Brown memcpy(dest + 6 + i, src + (7 - i), 1); 166b4f9fe12SLen Brown 167b4f9fe12SLen Brown memcpy(dest + 8, src + 8, 8); 168b4f9fe12SLen Brown } 169b4f9fe12SLen Brown 170b4f9fe12SLen Brown /** 171b4f9fe12SLen Brown * wmi_parse_guid - Convert GUID from ASCII to binary 172b4f9fe12SLen Brown * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 173b4f9fe12SLen Brown * @dest: Memory block to hold binary GUID (16 bytes) 174b4f9fe12SLen Brown * 175b4f9fe12SLen Brown * N.B. The GUID need not be NULL terminated. 176b4f9fe12SLen Brown * 177b4f9fe12SLen Brown * Return: 'true' @dest contains binary GUID 178b4f9fe12SLen Brown * 'false' @dest contents are undefined 179b4f9fe12SLen Brown */ 180b4f9fe12SLen Brown static bool wmi_parse_guid(const u8 *src, u8 *dest) 181b4f9fe12SLen Brown { 182b4f9fe12SLen Brown static const int size[] = { 4, 2, 2, 2, 6 }; 183b4f9fe12SLen Brown int i, j, v; 184b4f9fe12SLen Brown 185b4f9fe12SLen Brown if (src[8] != '-' || src[13] != '-' || 186b4f9fe12SLen Brown src[18] != '-' || src[23] != '-') 187b4f9fe12SLen Brown return false; 188b4f9fe12SLen Brown 189b4f9fe12SLen Brown for (j = 0; j < 5; j++, src++) { 190b4f9fe12SLen Brown for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { 191b4f9fe12SLen Brown v = wmi_parse_hexbyte(src); 192b4f9fe12SLen Brown if (v < 0) 193b4f9fe12SLen Brown return false; 194b4f9fe12SLen Brown } 195b4f9fe12SLen Brown } 196b4f9fe12SLen Brown 197b4f9fe12SLen Brown return true; 198b4f9fe12SLen Brown } 199b4f9fe12SLen Brown 2001caab3c1SMatthew Garrett /* 2011caab3c1SMatthew Garrett * Convert a raw GUID to the ACII string representation 2021caab3c1SMatthew Garrett */ 2031caab3c1SMatthew Garrett static int wmi_gtoa(const char *in, char *out) 2041caab3c1SMatthew Garrett { 2051caab3c1SMatthew Garrett int i; 2061caab3c1SMatthew Garrett 2071caab3c1SMatthew Garrett for (i = 3; i >= 0; i--) 2081caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[i] & 0xFF); 2091caab3c1SMatthew Garrett 2101caab3c1SMatthew Garrett out += sprintf(out, "-"); 2111caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[5] & 0xFF); 2121caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[4] & 0xFF); 2131caab3c1SMatthew Garrett out += sprintf(out, "-"); 2141caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[7] & 0xFF); 2151caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[6] & 0xFF); 2161caab3c1SMatthew Garrett out += sprintf(out, "-"); 2171caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[8] & 0xFF); 2181caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[9] & 0xFF); 2191caab3c1SMatthew Garrett out += sprintf(out, "-"); 2201caab3c1SMatthew Garrett 2211caab3c1SMatthew Garrett for (i = 10; i <= 15; i++) 2221caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[i] & 0xFF); 2231caab3c1SMatthew Garrett 2241caab3c1SMatthew Garrett out = '\0'; 2251caab3c1SMatthew Garrett return 0; 2261caab3c1SMatthew Garrett } 2271caab3c1SMatthew Garrett 228b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out) 229b4f9fe12SLen Brown { 230b4f9fe12SLen Brown char tmp[16], guid_input[16]; 231b4f9fe12SLen Brown struct wmi_block *wblock; 232b4f9fe12SLen Brown struct guid_block *block; 233b4f9fe12SLen Brown struct list_head *p; 234b4f9fe12SLen Brown 235b4f9fe12SLen Brown wmi_parse_guid(guid_string, tmp); 236b4f9fe12SLen Brown wmi_swap_bytes(tmp, guid_input); 237b4f9fe12SLen Brown 238b4f9fe12SLen Brown list_for_each(p, &wmi_blocks.list) { 239b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 240b4f9fe12SLen Brown block = &wblock->gblock; 241b4f9fe12SLen Brown 242b4f9fe12SLen Brown if (memcmp(block->guid, guid_input, 16) == 0) { 243b4f9fe12SLen Brown if (out) 244b4f9fe12SLen Brown *out = wblock; 245b4f9fe12SLen Brown return 1; 246b4f9fe12SLen Brown } 247b4f9fe12SLen Brown } 248b4f9fe12SLen Brown return 0; 249b4f9fe12SLen Brown } 250b4f9fe12SLen Brown 251b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 252b4f9fe12SLen Brown { 253b4f9fe12SLen Brown struct guid_block *block = NULL; 254b4f9fe12SLen Brown char method[5]; 255b4f9fe12SLen Brown struct acpi_object_list input; 256b4f9fe12SLen Brown union acpi_object params[1]; 257b4f9fe12SLen Brown acpi_status status; 258b4f9fe12SLen Brown acpi_handle handle; 259b4f9fe12SLen Brown 260b4f9fe12SLen Brown block = &wblock->gblock; 261b4f9fe12SLen Brown handle = wblock->handle; 262b4f9fe12SLen Brown 263b4f9fe12SLen Brown if (!block) 264b4f9fe12SLen Brown return AE_NOT_EXIST; 265b4f9fe12SLen Brown 266b4f9fe12SLen Brown input.count = 1; 267b4f9fe12SLen Brown input.pointer = params; 268b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 269b4f9fe12SLen Brown params[0].integer.value = enable; 270b4f9fe12SLen Brown 271b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 272b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, NULL); 273b4f9fe12SLen Brown 274b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 275b4f9fe12SLen Brown return status; 276b4f9fe12SLen Brown else 277b4f9fe12SLen Brown return AE_OK; 278b4f9fe12SLen Brown } 279b4f9fe12SLen Brown 280b4f9fe12SLen Brown /* 281b4f9fe12SLen Brown * Exported WMI functions 282b4f9fe12SLen Brown */ 283b4f9fe12SLen Brown /** 284b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 285b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 286b4f9fe12SLen Brown * @instance: Instance index 287b4f9fe12SLen Brown * @method_id: Method ID to call 288b4f9fe12SLen Brown * &in: Buffer containing input for the method call 289b4f9fe12SLen Brown * &out: Empty buffer to return the method results 290b4f9fe12SLen Brown * 291b4f9fe12SLen Brown * Call an ACPI-WMI method 292b4f9fe12SLen Brown */ 293b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 294b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 295b4f9fe12SLen Brown { 296b4f9fe12SLen Brown struct guid_block *block = NULL; 297b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 298b4f9fe12SLen Brown acpi_handle handle; 299b4f9fe12SLen Brown acpi_status status; 300b4f9fe12SLen Brown struct acpi_object_list input; 301b4f9fe12SLen Brown union acpi_object params[3]; 302f3d83e24SCostantino Leandro char method[5] = "WM"; 303b4f9fe12SLen Brown 304b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 305b4f9fe12SLen Brown return AE_ERROR; 306b4f9fe12SLen Brown 307b4f9fe12SLen Brown block = &wblock->gblock; 308b4f9fe12SLen Brown handle = wblock->handle; 309b4f9fe12SLen Brown 310b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 311b4f9fe12SLen Brown return AE_BAD_DATA; 312b4f9fe12SLen Brown 313b4f9fe12SLen Brown if (block->instance_count < instance) 314b4f9fe12SLen Brown return AE_BAD_PARAMETER; 315b4f9fe12SLen Brown 316b4f9fe12SLen Brown input.count = 2; 317b4f9fe12SLen Brown input.pointer = params; 318b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 319b4f9fe12SLen Brown params[0].integer.value = instance; 320b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 321b4f9fe12SLen Brown params[1].integer.value = method_id; 322b4f9fe12SLen Brown 323b4f9fe12SLen Brown if (in) { 324b4f9fe12SLen Brown input.count = 3; 325b4f9fe12SLen Brown 326b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 327b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 328b4f9fe12SLen Brown } else { 329b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 330b4f9fe12SLen Brown } 331b4f9fe12SLen Brown params[2].buffer.length = in->length; 332b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 333b4f9fe12SLen Brown } 334b4f9fe12SLen Brown 335b4f9fe12SLen Brown strncat(method, block->object_id, 2); 336b4f9fe12SLen Brown 337b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 338b4f9fe12SLen Brown 339b4f9fe12SLen Brown return status; 340b4f9fe12SLen Brown } 341b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method); 342b4f9fe12SLen Brown 343b4f9fe12SLen Brown /** 344b4f9fe12SLen Brown * wmi_query_block - Return contents of a WMI block 345b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 346b4f9fe12SLen Brown * @instance: Instance index 347b4f9fe12SLen Brown * &out: Empty buffer to return the contents of the data block to 348b4f9fe12SLen Brown * 349b4f9fe12SLen Brown * Return the contents of an ACPI-WMI data block to a buffer 350b4f9fe12SLen Brown */ 351b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance, 352b4f9fe12SLen Brown struct acpi_buffer *out) 353b4f9fe12SLen Brown { 354b4f9fe12SLen Brown struct guid_block *block = NULL; 355b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 356b4f9fe12SLen Brown acpi_handle handle, wc_handle; 357b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 358b4f9fe12SLen Brown struct acpi_object_list input, wc_input; 359b4f9fe12SLen Brown union acpi_object wc_params[1], wq_params[1]; 360f3d83e24SCostantino Leandro char method[5]; 361f3d83e24SCostantino Leandro char wc_method[5] = "WC"; 362b4f9fe12SLen Brown 363b4f9fe12SLen Brown if (!guid_string || !out) 364b4f9fe12SLen Brown return AE_BAD_PARAMETER; 365b4f9fe12SLen Brown 366b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 367b4f9fe12SLen Brown return AE_ERROR; 368b4f9fe12SLen Brown 369b4f9fe12SLen Brown block = &wblock->gblock; 370b4f9fe12SLen Brown handle = wblock->handle; 371b4f9fe12SLen Brown 372b4f9fe12SLen Brown if (block->instance_count < instance) 373b4f9fe12SLen Brown return AE_BAD_PARAMETER; 374b4f9fe12SLen Brown 375b4f9fe12SLen Brown /* Check GUID is a data block */ 376b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 377b4f9fe12SLen Brown return AE_ERROR; 378b4f9fe12SLen Brown 379b4f9fe12SLen Brown input.count = 1; 380b4f9fe12SLen Brown input.pointer = wq_params; 381b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 382b4f9fe12SLen Brown wq_params[0].integer.value = instance; 383b4f9fe12SLen Brown 384b4f9fe12SLen Brown /* 385b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 386b4f9fe12SLen Brown * enable collection. 387b4f9fe12SLen Brown */ 388b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 389b4f9fe12SLen Brown wc_input.count = 1; 390b4f9fe12SLen Brown wc_input.pointer = wc_params; 391b4f9fe12SLen Brown wc_params[0].type = ACPI_TYPE_INTEGER; 392b4f9fe12SLen Brown wc_params[0].integer.value = 1; 393b4f9fe12SLen Brown 394b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 395b4f9fe12SLen Brown 396b4f9fe12SLen Brown /* 397b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 398b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 399b4f9fe12SLen Brown * should not fail if this happens. 400b4f9fe12SLen Brown */ 401b4f9fe12SLen Brown wc_status = acpi_get_handle(handle, wc_method, &wc_handle); 402b4f9fe12SLen Brown if (ACPI_SUCCESS(wc_status)) 403b4f9fe12SLen Brown wc_status = acpi_evaluate_object(handle, wc_method, 404b4f9fe12SLen Brown &wc_input, NULL); 405b4f9fe12SLen Brown } 406b4f9fe12SLen Brown 407b4f9fe12SLen Brown strcpy(method, "WQ"); 408b4f9fe12SLen Brown strncat(method, block->object_id, 2); 409b4f9fe12SLen Brown 410b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 411b4f9fe12SLen Brown 412b4f9fe12SLen Brown /* 413b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 414b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 415b4f9fe12SLen Brown */ 416b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 417b4f9fe12SLen Brown wc_params[0].integer.value = 0; 418b4f9fe12SLen Brown status = acpi_evaluate_object(handle, 419b4f9fe12SLen Brown wc_method, &wc_input, NULL); 420b4f9fe12SLen Brown } 421b4f9fe12SLen Brown 422b4f9fe12SLen Brown return status; 423b4f9fe12SLen Brown } 424b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 425b4f9fe12SLen Brown 426b4f9fe12SLen Brown /** 427b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 428b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 429b4f9fe12SLen Brown * @instance: Instance index 430b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 431b4f9fe12SLen Brown * 432b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 433b4f9fe12SLen Brown */ 434b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 435b4f9fe12SLen Brown const struct acpi_buffer *in) 436b4f9fe12SLen Brown { 437b4f9fe12SLen Brown struct guid_block *block = NULL; 438b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 439b4f9fe12SLen Brown acpi_handle handle; 440b4f9fe12SLen Brown struct acpi_object_list input; 441b4f9fe12SLen Brown union acpi_object params[2]; 442f3d83e24SCostantino Leandro char method[5] = "WS"; 443b4f9fe12SLen Brown 444b4f9fe12SLen Brown if (!guid_string || !in) 445b4f9fe12SLen Brown return AE_BAD_DATA; 446b4f9fe12SLen Brown 447b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 448b4f9fe12SLen Brown return AE_ERROR; 449b4f9fe12SLen Brown 450b4f9fe12SLen Brown block = &wblock->gblock; 451b4f9fe12SLen Brown handle = wblock->handle; 452b4f9fe12SLen Brown 453b4f9fe12SLen Brown if (block->instance_count < instance) 454b4f9fe12SLen Brown return AE_BAD_PARAMETER; 455b4f9fe12SLen Brown 456b4f9fe12SLen Brown /* Check GUID is a data block */ 457b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 458b4f9fe12SLen Brown return AE_ERROR; 459b4f9fe12SLen Brown 460b4f9fe12SLen Brown input.count = 2; 461b4f9fe12SLen Brown input.pointer = params; 462b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 463b4f9fe12SLen Brown params[0].integer.value = instance; 464b4f9fe12SLen Brown 465b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 466b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 467b4f9fe12SLen Brown } else { 468b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 469b4f9fe12SLen Brown } 470b4f9fe12SLen Brown params[1].buffer.length = in->length; 471b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 472b4f9fe12SLen Brown 473b4f9fe12SLen Brown strncat(method, block->object_id, 2); 474b4f9fe12SLen Brown 475b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 476b4f9fe12SLen Brown } 477b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 478b4f9fe12SLen Brown 479b4f9fe12SLen Brown /** 480b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 481b4f9fe12SLen Brown * @handler: Function to handle notifications 482b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 483b4f9fe12SLen Brown * 484b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 485b4f9fe12SLen Brown */ 486b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 487b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 488b4f9fe12SLen Brown { 489b4f9fe12SLen Brown struct wmi_block *block; 490b4f9fe12SLen Brown acpi_status status; 491b4f9fe12SLen Brown 492b4f9fe12SLen Brown if (!guid || !handler) 493b4f9fe12SLen Brown return AE_BAD_PARAMETER; 494b4f9fe12SLen Brown 495c03b26a5SPaul Rolland if (!find_guid(guid, &block)) 496b4f9fe12SLen Brown return AE_NOT_EXIST; 497b4f9fe12SLen Brown 498b4f9fe12SLen Brown if (block->handler) 499b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 500b4f9fe12SLen Brown 501b4f9fe12SLen Brown block->handler = handler; 502b4f9fe12SLen Brown block->handler_data = data; 503b4f9fe12SLen Brown 504b4f9fe12SLen Brown status = wmi_method_enable(block, 1); 505b4f9fe12SLen Brown 506b4f9fe12SLen Brown return status; 507b4f9fe12SLen Brown } 508b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 509b4f9fe12SLen Brown 510b4f9fe12SLen Brown /** 511b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 512b4f9fe12SLen Brown * 513b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 514b4f9fe12SLen Brown */ 515b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 516b4f9fe12SLen Brown { 517b4f9fe12SLen Brown struct wmi_block *block; 518b4f9fe12SLen Brown acpi_status status; 519b4f9fe12SLen Brown 520b4f9fe12SLen Brown if (!guid) 521b4f9fe12SLen Brown return AE_BAD_PARAMETER; 522b4f9fe12SLen Brown 523c03b26a5SPaul Rolland if (!find_guid(guid, &block)) 524b4f9fe12SLen Brown return AE_NOT_EXIST; 525b4f9fe12SLen Brown 526b4f9fe12SLen Brown if (!block->handler) 527b4f9fe12SLen Brown return AE_NULL_ENTRY; 528b4f9fe12SLen Brown 529b4f9fe12SLen Brown status = wmi_method_enable(block, 0); 530b4f9fe12SLen Brown 531b4f9fe12SLen Brown block->handler = NULL; 532b4f9fe12SLen Brown block->handler_data = NULL; 533b4f9fe12SLen Brown 534b4f9fe12SLen Brown return status; 535b4f9fe12SLen Brown } 536b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 537b4f9fe12SLen Brown 538b4f9fe12SLen Brown /** 539b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 540b4f9fe12SLen Brown * 5413e9b988eSAnisse Astier * @event: Event to find 5423e9b988eSAnisse Astier * @out: Buffer to hold event data. out->pointer should be freed with kfree() 543b4f9fe12SLen Brown * 544b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 545b4f9fe12SLen Brown */ 546b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 547b4f9fe12SLen Brown { 548b4f9fe12SLen Brown struct acpi_object_list input; 549b4f9fe12SLen Brown union acpi_object params[1]; 550b4f9fe12SLen Brown struct guid_block *gblock; 551b4f9fe12SLen Brown struct wmi_block *wblock; 552b4f9fe12SLen Brown struct list_head *p; 553b4f9fe12SLen Brown 554b4f9fe12SLen Brown input.count = 1; 555b4f9fe12SLen Brown input.pointer = params; 556b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 557b4f9fe12SLen Brown params[0].integer.value = event; 558b4f9fe12SLen Brown 559b4f9fe12SLen Brown list_for_each(p, &wmi_blocks.list) { 560b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 561b4f9fe12SLen Brown gblock = &wblock->gblock; 562b4f9fe12SLen Brown 563b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 564b4f9fe12SLen Brown (gblock->notify_id == event)) 565b4f9fe12SLen Brown return acpi_evaluate_object(wblock->handle, "_WED", 566b4f9fe12SLen Brown &input, out); 567b4f9fe12SLen Brown } 568b4f9fe12SLen Brown 569b4f9fe12SLen Brown return AE_NOT_FOUND; 570b4f9fe12SLen Brown } 571b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 572b4f9fe12SLen Brown 573b4f9fe12SLen Brown /** 574b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 575b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 576b4f9fe12SLen Brown * 577b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 578b4f9fe12SLen Brown */ 579b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 580b4f9fe12SLen Brown { 581b4f9fe12SLen Brown return find_guid(guid_string, NULL); 582b4f9fe12SLen Brown } 583b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 584b4f9fe12SLen Brown 585b4f9fe12SLen Brown /* 5861caab3c1SMatthew Garrett * sysfs interface 5871caab3c1SMatthew Garrett */ 5881caab3c1SMatthew Garrett static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, 5891caab3c1SMatthew Garrett char *buf) 5901caab3c1SMatthew Garrett { 5911caab3c1SMatthew Garrett char guid_string[37]; 5921caab3c1SMatthew Garrett struct wmi_block *wblock; 5931caab3c1SMatthew Garrett 5941caab3c1SMatthew Garrett wblock = dev_get_drvdata(dev); 5951caab3c1SMatthew Garrett if (!wblock) 5961caab3c1SMatthew Garrett return -ENOMEM; 5971caab3c1SMatthew Garrett 5981caab3c1SMatthew Garrett wmi_gtoa(wblock->gblock.guid, guid_string); 5991caab3c1SMatthew Garrett 6001caab3c1SMatthew Garrett return sprintf(buf, "wmi:%s\n", guid_string); 6011caab3c1SMatthew Garrett } 6021caab3c1SMatthew Garrett static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); 6031caab3c1SMatthew Garrett 6041caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 6051caab3c1SMatthew Garrett { 6061caab3c1SMatthew Garrett char guid_string[37]; 6071caab3c1SMatthew Garrett 6081caab3c1SMatthew Garrett struct wmi_block *wblock; 6091caab3c1SMatthew Garrett 6101caab3c1SMatthew Garrett if (add_uevent_var(env, "MODALIAS=")) 6111caab3c1SMatthew Garrett return -ENOMEM; 6121caab3c1SMatthew Garrett 6131caab3c1SMatthew Garrett wblock = dev_get_drvdata(dev); 6141caab3c1SMatthew Garrett if (!wblock) 6151caab3c1SMatthew Garrett return -ENOMEM; 6161caab3c1SMatthew Garrett 6171caab3c1SMatthew Garrett wmi_gtoa(wblock->gblock.guid, guid_string); 6181caab3c1SMatthew Garrett 6191caab3c1SMatthew Garrett strcpy(&env->buf[env->buflen - 1], "wmi:"); 6201caab3c1SMatthew Garrett memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); 6211caab3c1SMatthew Garrett env->buflen += 40; 6221caab3c1SMatthew Garrett 6231caab3c1SMatthew Garrett return 0; 6241caab3c1SMatthew Garrett } 6251caab3c1SMatthew Garrett 6261caab3c1SMatthew Garrett static void wmi_dev_free(struct device *dev) 6271caab3c1SMatthew Garrett { 6281caab3c1SMatthew Garrett kfree(dev); 6291caab3c1SMatthew Garrett } 6301caab3c1SMatthew Garrett 6311caab3c1SMatthew Garrett static struct class wmi_class = { 6321caab3c1SMatthew Garrett .name = "wmi", 6331caab3c1SMatthew Garrett .dev_release = wmi_dev_free, 6341caab3c1SMatthew Garrett .dev_uevent = wmi_dev_uevent, 6351caab3c1SMatthew Garrett }; 6361caab3c1SMatthew Garrett 6371caab3c1SMatthew Garrett static int wmi_create_devs(void) 6381caab3c1SMatthew Garrett { 6391caab3c1SMatthew Garrett int result; 6401caab3c1SMatthew Garrett char guid_string[37]; 6411caab3c1SMatthew Garrett struct guid_block *gblock; 6421caab3c1SMatthew Garrett struct wmi_block *wblock; 6431caab3c1SMatthew Garrett struct list_head *p; 6441caab3c1SMatthew Garrett struct device *guid_dev; 6451caab3c1SMatthew Garrett 6461caab3c1SMatthew Garrett /* Create devices for all the GUIDs */ 6471caab3c1SMatthew Garrett list_for_each(p, &wmi_blocks.list) { 6481caab3c1SMatthew Garrett wblock = list_entry(p, struct wmi_block, list); 6491caab3c1SMatthew Garrett 6501caab3c1SMatthew Garrett guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); 6511caab3c1SMatthew Garrett if (!guid_dev) 6521caab3c1SMatthew Garrett return -ENOMEM; 6531caab3c1SMatthew Garrett 6541caab3c1SMatthew Garrett wblock->dev = guid_dev; 6551caab3c1SMatthew Garrett 6561caab3c1SMatthew Garrett guid_dev->class = &wmi_class; 6571caab3c1SMatthew Garrett dev_set_drvdata(guid_dev, wblock); 6581caab3c1SMatthew Garrett 6591caab3c1SMatthew Garrett gblock = &wblock->gblock; 6601caab3c1SMatthew Garrett 6611caab3c1SMatthew Garrett wmi_gtoa(gblock->guid, guid_string); 6621caab3c1SMatthew Garrett dev_set_name(guid_dev, guid_string); 6631caab3c1SMatthew Garrett 6641caab3c1SMatthew Garrett result = device_register(guid_dev); 6651caab3c1SMatthew Garrett if (result) 6661caab3c1SMatthew Garrett return result; 6671caab3c1SMatthew Garrett 6681caab3c1SMatthew Garrett result = device_create_file(guid_dev, &dev_attr_modalias); 6691caab3c1SMatthew Garrett if (result) 6701caab3c1SMatthew Garrett return result; 6711caab3c1SMatthew Garrett } 6721caab3c1SMatthew Garrett 6731caab3c1SMatthew Garrett return 0; 6741caab3c1SMatthew Garrett } 6751caab3c1SMatthew Garrett 6761caab3c1SMatthew Garrett static void wmi_remove_devs(void) 6771caab3c1SMatthew Garrett { 6781caab3c1SMatthew Garrett struct guid_block *gblock; 6791caab3c1SMatthew Garrett struct wmi_block *wblock; 6801caab3c1SMatthew Garrett struct list_head *p; 6811caab3c1SMatthew Garrett struct device *guid_dev; 6821caab3c1SMatthew Garrett 6831caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 6841caab3c1SMatthew Garrett list_for_each(p, &wmi_blocks.list) { 6851caab3c1SMatthew Garrett wblock = list_entry(p, struct wmi_block, list); 6861caab3c1SMatthew Garrett 6871caab3c1SMatthew Garrett guid_dev = wblock->dev; 6881caab3c1SMatthew Garrett gblock = &wblock->gblock; 6891caab3c1SMatthew Garrett 6901caab3c1SMatthew Garrett device_remove_file(guid_dev, &dev_attr_modalias); 6911caab3c1SMatthew Garrett 6921caab3c1SMatthew Garrett device_unregister(guid_dev); 6931caab3c1SMatthew Garrett } 6941caab3c1SMatthew Garrett } 6951caab3c1SMatthew Garrett 6961caab3c1SMatthew Garrett static void wmi_class_exit(void) 6971caab3c1SMatthew Garrett { 6981caab3c1SMatthew Garrett wmi_remove_devs(); 6991caab3c1SMatthew Garrett class_unregister(&wmi_class); 7001caab3c1SMatthew Garrett } 7011caab3c1SMatthew Garrett 7021caab3c1SMatthew Garrett static int wmi_class_init(void) 7031caab3c1SMatthew Garrett { 7041caab3c1SMatthew Garrett int ret; 7051caab3c1SMatthew Garrett 7061caab3c1SMatthew Garrett ret = class_register(&wmi_class); 7071caab3c1SMatthew Garrett if (ret) 7081caab3c1SMatthew Garrett return ret; 7091caab3c1SMatthew Garrett 7101caab3c1SMatthew Garrett ret = wmi_create_devs(); 7111caab3c1SMatthew Garrett if (ret) 7121caab3c1SMatthew Garrett wmi_class_exit(); 7131caab3c1SMatthew Garrett 7141caab3c1SMatthew Garrett return ret; 7151caab3c1SMatthew Garrett } 7161caab3c1SMatthew Garrett 7171caab3c1SMatthew Garrett /* 718b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 719b4f9fe12SLen Brown */ 720b4f9fe12SLen Brown static __init acpi_status parse_wdg(acpi_handle handle) 721b4f9fe12SLen Brown { 722b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 723b4f9fe12SLen Brown union acpi_object *obj; 724b4f9fe12SLen Brown struct guid_block *gblock; 725b4f9fe12SLen Brown struct wmi_block *wblock; 726b4f9fe12SLen Brown acpi_status status; 727b4f9fe12SLen Brown u32 i, total; 728b4f9fe12SLen Brown 729b4f9fe12SLen Brown status = acpi_evaluate_object(handle, "_WDG", NULL, &out); 730b4f9fe12SLen Brown 731b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 732b4f9fe12SLen Brown return status; 733b4f9fe12SLen Brown 734b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 735b4f9fe12SLen Brown 736b4f9fe12SLen Brown if (obj->type != ACPI_TYPE_BUFFER) 737b4f9fe12SLen Brown return AE_ERROR; 738b4f9fe12SLen Brown 739b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 740b4f9fe12SLen Brown 741b4f9fe12SLen Brown gblock = kzalloc(obj->buffer.length, GFP_KERNEL); 742b4f9fe12SLen Brown if (!gblock) 743b4f9fe12SLen Brown return AE_NO_MEMORY; 744b4f9fe12SLen Brown 745b4f9fe12SLen Brown memcpy(gblock, obj->buffer.pointer, obj->buffer.length); 746b4f9fe12SLen Brown 747b4f9fe12SLen Brown for (i = 0; i < total; i++) { 748b4f9fe12SLen Brown wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 749b4f9fe12SLen Brown if (!wblock) 750b4f9fe12SLen Brown return AE_NO_MEMORY; 751b4f9fe12SLen Brown 752b4f9fe12SLen Brown wblock->gblock = gblock[i]; 753b4f9fe12SLen Brown wblock->handle = handle; 754b4f9fe12SLen Brown list_add_tail(&wblock->list, &wmi_blocks.list); 755b4f9fe12SLen Brown } 756b4f9fe12SLen Brown 757b4f9fe12SLen Brown kfree(out.pointer); 758b4f9fe12SLen Brown kfree(gblock); 759b4f9fe12SLen Brown 760b4f9fe12SLen Brown return status; 761b4f9fe12SLen Brown } 762b4f9fe12SLen Brown 763b4f9fe12SLen Brown /* 764b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 765b4f9fe12SLen Brown * hand these off to the EC driver. 766b4f9fe12SLen Brown */ 767b4f9fe12SLen Brown static acpi_status 768b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 769b4f9fe12SLen Brown u32 bits, acpi_integer * value, 770b4f9fe12SLen Brown void *handler_context, void *region_context) 771b4f9fe12SLen Brown { 772b4f9fe12SLen Brown int result = 0, i = 0; 773b4f9fe12SLen Brown u8 temp = 0; 774b4f9fe12SLen Brown 775b4f9fe12SLen Brown if ((address > 0xFF) || !value) 776b4f9fe12SLen Brown return AE_BAD_PARAMETER; 777b4f9fe12SLen Brown 778b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 779b4f9fe12SLen Brown return AE_BAD_PARAMETER; 780b4f9fe12SLen Brown 781b4f9fe12SLen Brown if (bits != 8) 782b4f9fe12SLen Brown return AE_BAD_PARAMETER; 783b4f9fe12SLen Brown 784b4f9fe12SLen Brown if (function == ACPI_READ) { 785b4f9fe12SLen Brown result = ec_read(address, &temp); 786b4f9fe12SLen Brown (*value) |= ((acpi_integer)temp) << i; 787b4f9fe12SLen Brown } else { 788b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 789b4f9fe12SLen Brown result = ec_write(address, temp); 790b4f9fe12SLen Brown } 791b4f9fe12SLen Brown 792b4f9fe12SLen Brown switch (result) { 793b4f9fe12SLen Brown case -EINVAL: 794b4f9fe12SLen Brown return AE_BAD_PARAMETER; 795b4f9fe12SLen Brown break; 796b4f9fe12SLen Brown case -ENODEV: 797b4f9fe12SLen Brown return AE_NOT_FOUND; 798b4f9fe12SLen Brown break; 799b4f9fe12SLen Brown case -ETIME: 800b4f9fe12SLen Brown return AE_TIME; 801b4f9fe12SLen Brown break; 802b4f9fe12SLen Brown default: 803b4f9fe12SLen Brown return AE_OK; 804b4f9fe12SLen Brown } 805b4f9fe12SLen Brown } 806b4f9fe12SLen Brown 807f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event) 808b4f9fe12SLen Brown { 809b4f9fe12SLen Brown struct guid_block *block; 810b4f9fe12SLen Brown struct wmi_block *wblock; 811b4f9fe12SLen Brown struct list_head *p; 812b4f9fe12SLen Brown 813b4f9fe12SLen Brown list_for_each(p, &wmi_blocks.list) { 814b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 815b4f9fe12SLen Brown block = &wblock->gblock; 816b4f9fe12SLen Brown 817b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EVENT) && 818b4f9fe12SLen Brown (block->notify_id == event)) { 819b4f9fe12SLen Brown if (wblock->handler) 820b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 821b4f9fe12SLen Brown 822b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 823b4f9fe12SLen Brown device->pnp.device_class, dev_name(&device->dev), 824b4f9fe12SLen Brown event, 0); 825b4f9fe12SLen Brown break; 826b4f9fe12SLen Brown } 827b4f9fe12SLen Brown } 828b4f9fe12SLen Brown } 829b4f9fe12SLen Brown 830b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type) 831b4f9fe12SLen Brown { 832b4f9fe12SLen Brown acpi_remove_address_space_handler(device->handle, 833b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 834b4f9fe12SLen Brown 835b4f9fe12SLen Brown return 0; 836b4f9fe12SLen Brown } 837b4f9fe12SLen Brown 838b4f9fe12SLen Brown static int __init acpi_wmi_add(struct acpi_device *device) 839b4f9fe12SLen Brown { 840b4f9fe12SLen Brown acpi_status status; 841b4f9fe12SLen Brown int result = 0; 842b4f9fe12SLen Brown 843b4f9fe12SLen Brown status = acpi_install_address_space_handler(device->handle, 844b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 845b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 846b4f9fe12SLen Brown NULL, NULL); 847b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 848b4f9fe12SLen Brown return -ENODEV; 849b4f9fe12SLen Brown 850b4f9fe12SLen Brown status = parse_wdg(device->handle); 851b4f9fe12SLen Brown if (ACPI_FAILURE(status)) { 852b4f9fe12SLen Brown printk(KERN_ERR PREFIX "Error installing EC region handler\n"); 853b4f9fe12SLen Brown return -ENODEV; 854b4f9fe12SLen Brown } 855b4f9fe12SLen Brown 856b4f9fe12SLen Brown return result; 857b4f9fe12SLen Brown } 858b4f9fe12SLen Brown 859b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 860b4f9fe12SLen Brown { 861da511997SRoel Kluin int result; 862b4f9fe12SLen Brown 863b4f9fe12SLen Brown INIT_LIST_HEAD(&wmi_blocks.list); 864b4f9fe12SLen Brown 865b4f9fe12SLen Brown if (acpi_disabled) 866b4f9fe12SLen Brown return -ENODEV; 867b4f9fe12SLen Brown 868b4f9fe12SLen Brown result = acpi_bus_register_driver(&acpi_wmi_driver); 869b4f9fe12SLen Brown 870b4f9fe12SLen Brown if (result < 0) { 871b4f9fe12SLen Brown printk(KERN_INFO PREFIX "Error loading mapper\n"); 8721caab3c1SMatthew Garrett return -ENODEV; 873b4f9fe12SLen Brown } 874b4f9fe12SLen Brown 8751caab3c1SMatthew Garrett result = wmi_class_init(); 8761caab3c1SMatthew Garrett if (result) { 8771caab3c1SMatthew Garrett acpi_bus_unregister_driver(&acpi_wmi_driver); 8781caab3c1SMatthew Garrett return result; 8791caab3c1SMatthew Garrett } 8801caab3c1SMatthew Garrett 8811caab3c1SMatthew Garrett printk(KERN_INFO PREFIX "Mapper loaded\n"); 8821caab3c1SMatthew Garrett 883b4f9fe12SLen Brown return result; 884b4f9fe12SLen Brown } 885b4f9fe12SLen Brown 886b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 887b4f9fe12SLen Brown { 888b4f9fe12SLen Brown struct list_head *p, *tmp; 889b4f9fe12SLen Brown struct wmi_block *wblock; 890b4f9fe12SLen Brown 8911caab3c1SMatthew Garrett wmi_class_exit(); 8921caab3c1SMatthew Garrett 893b4f9fe12SLen Brown acpi_bus_unregister_driver(&acpi_wmi_driver); 894b4f9fe12SLen Brown 895b4f9fe12SLen Brown list_for_each_safe(p, tmp, &wmi_blocks.list) { 896b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 897b4f9fe12SLen Brown 898b4f9fe12SLen Brown list_del(p); 899b4f9fe12SLen Brown kfree(wblock); 900b4f9fe12SLen Brown } 901b4f9fe12SLen Brown 902b4f9fe12SLen Brown printk(KERN_INFO PREFIX "Mapper unloaded\n"); 903b4f9fe12SLen Brown } 904b4f9fe12SLen Brown 905b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 906b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 907