acpi_wmi.c revision 212762
1/*- 2 * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_wmi.c 212762 2010-09-16 20:11:40Z jkim $"); 29 30/* 31 * Driver for acpi-wmi mapping, provides an interface for vendor specific 32 * implementations (e.g. HP and Acer laptops). 33 * Inspired by the ACPI-WMI mapping driver (c) 2008-2008 Carlos Corbacho which 34 * implements this functionality for Linux. 35 * 36 * WMI and ACPI: http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx 37 * acpi-wmi for Linux: http://www.kernel.org 38 */ 39 40#include "opt_acpi.h" 41#include <sys/param.h> 42#include <sys/conf.h> 43#include <sys/uio.h> 44#include <sys/proc.h> 45#include <sys/kernel.h> 46#include <sys/malloc.h> 47#include <sys/sbuf.h> 48#include <sys/module.h> 49#include <sys/bus.h> 50 51#include <contrib/dev/acpica/include/acpi.h> 52#include <contrib/dev/acpica/include/accommon.h> 53#include <dev/acpica/acpivar.h> 54#include "acpi_wmi_if.h" 55 56MALLOC_DEFINE(M_ACPIWMI, "acpiwmi", "ACPI-WMI mapping"); 57 58#define _COMPONENT ACPI_OEM 59ACPI_MODULE_NAME("ACPI_WMI"); 60 61#define ACPI_WMI_REGFLAG_EXPENSIVE 0x1 /* GUID flag: Expensive operation */ 62#define ACPI_WMI_REGFLAG_METHOD 0x2 /* GUID flag: Method call */ 63#define ACPI_WMI_REGFLAG_STRING 0x4 /* GUID flag: String */ 64#define ACPI_WMI_REGFLAG_EVENT 0x8 /* GUID flag: Event */ 65 66/* 67 * acpi_wmi driver private structure 68 */ 69struct acpi_wmi_softc { 70 device_t wmi_dev; /* wmi device id */ 71 ACPI_HANDLE wmi_handle; /* handle of the PNP0C14 node */ 72 device_t ec_dev; /* acpi_ec0 */ 73 struct cdev *wmistat_dev_t; /* wmistat device handle */ 74 struct sbuf wmistat_sbuf; /* sbuf for /dev/wmistat output */ 75 pid_t wmistat_open_pid; /* pid operating on /dev/wmistat */ 76 int wmistat_bufptr; /* /dev/wmistat ptr to buffer position */ 77}; 78 79/* 80 * Struct that holds information about 81 * about a single GUID entry in _WDG 82 */ 83struct guid_info { 84 char guid[16]; /* 16 byte non human readable GUID */ 85 char oid[2]; /* object id or event notify id (first byte) */ 86 UINT8 max_instance; /* highest instance known for this GUID */ 87 UINT8 flags; /* ACPI_WMI_REGFLAG_%s */ 88}; 89 90/* WExx event generation state (on/off) */ 91enum event_generation_state { 92 EVENT_GENERATION_ON = 1, 93 EVENT_GENERATION_OFF = 0 94}; 95 96 97/* 98 * Information about one entry in _WDG. 99 * List of those is used to lookup information by GUID. 100 */ 101struct wmi_info { 102 TAILQ_ENTRY(wmi_info) wmi_list; 103 struct guid_info ginfo; /* information on guid */ 104 ACPI_NOTIFY_HANDLER event_handler;/* client provided event handler */ 105 void *event_handler_user_data; /* ev handler cookie */ 106}; 107 108TAILQ_HEAD(wmi_info_list_head, wmi_info) 109 wmi_info_list = TAILQ_HEAD_INITIALIZER(wmi_info_list); 110 111ACPI_SERIAL_DECL(acpi_wmi, "ACPI-WMI Mapping"); 112 113/* public interface - declaration */ 114/* standard device interface*/ 115static int acpi_wmi_probe(device_t dev); 116static int acpi_wmi_attach(device_t dev); 117static int acpi_wmi_detach(device_t dev); 118/* see acpi_wmi_if.m */ 119static int acpi_wmi_provides_guid_string_method(device_t dev, 120 const char *guid_string); 121static ACPI_STATUS acpi_wmi_evaluate_call_method(device_t dev, 122 const char *guid_string, UINT8 instance, 123 UINT32 method_id, const ACPI_BUFFER *in, 124 ACPI_BUFFER *out); 125static ACPI_STATUS acpi_wmi_install_event_handler_method(device_t dev, 126 const char *guid_string, ACPI_NOTIFY_HANDLER handler, 127 void *data); 128static ACPI_STATUS acpi_wmi_remove_event_handler_method(device_t dev, 129 const char *guid_string); 130static ACPI_STATUS acpi_wmi_get_event_data_method(device_t dev, 131 UINT32 event_id, ACPI_BUFFER *out); 132static ACPI_STATUS acpi_wmi_get_block_method(device_t dev, 133 const char *guid_string, 134 UINT8 instance, ACPI_BUFFER *out); 135static ACPI_STATUS acpi_wmi_set_block_method(device_t dev, 136 const char *guid_string, 137 UINT8 instance, const ACPI_BUFFER *in); 138/* private interface - declaration */ 139/* callbacks */ 140static void acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, 141 void *context); 142static ACPI_STATUS acpi_wmi_ec_handler(UINT32 function, 143 ACPI_PHYSICAL_ADDRESS address, UINT32 width, 144 UINT64 *value, void *context, 145 void *region_context); 146/* helpers */ 147static ACPI_STATUS acpi_wmi_read_wdg_blocks(ACPI_HANDLE h); 148static ACPI_STATUS acpi_wmi_toggle_we_event_generation(device_t dev, 149 struct wmi_info *winfo, 150 enum event_generation_state state); 151static int acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, 152 UINT8 *guid); 153static struct wmi_info* acpi_wmi_lookup_wmi_info_by_guid_string( 154 const char *guid_string); 155 156static d_open_t acpi_wmi_wmistat_open; 157static d_close_t acpi_wmi_wmistat_close; 158static d_read_t acpi_wmi_wmistat_read; 159 160/* handler /dev/wmistat device */ 161static struct cdevsw wmistat_cdevsw = { 162 .d_version = D_VERSION, 163 .d_open = acpi_wmi_wmistat_open, 164 .d_close = acpi_wmi_wmistat_close, 165 .d_read = acpi_wmi_wmistat_read, 166 .d_name = "wmistat", 167}; 168 169 170static device_method_t acpi_wmi_methods[] = { 171 /* Device interface */ 172 DEVMETHOD(device_probe, acpi_wmi_probe), 173 DEVMETHOD(device_attach, acpi_wmi_attach), 174 DEVMETHOD(device_detach, acpi_wmi_detach), 175 176 /* bus interface */ 177 DEVMETHOD(bus_add_child, bus_generic_add_child), 178 DEVMETHOD(bus_print_child, bus_generic_print_child), 179 180 /* acpi_wmi interface */ 181 DEVMETHOD(acpi_wmi_provides_guid_string, 182 acpi_wmi_provides_guid_string_method), 183 DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method), 184 DEVMETHOD(acpi_wmi_install_event_handler, 185 acpi_wmi_install_event_handler_method), 186 DEVMETHOD(acpi_wmi_remove_event_handler, 187 acpi_wmi_remove_event_handler_method), 188 DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method), 189 DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method), 190 DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method), 191 192 {0, 0} 193}; 194 195static driver_t acpi_wmi_driver = { 196 "acpi_wmi", 197 acpi_wmi_methods, 198 sizeof(struct acpi_wmi_softc), 199}; 200 201static devclass_t acpi_wmi_devclass; 202DRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, 0, 0); 203MODULE_VERSION(acpi_wmi, 1); 204MODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 205static char *wmi_ids[] = {"PNP0C14", NULL}; 206 207/* 208 * Probe for the PNP0C14 ACPI node 209 */ 210static int 211acpi_wmi_probe(device_t dev) 212{ 213 if (acpi_disabled("wmi") || 214 ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids) == NULL) 215 return (ENXIO); 216 device_set_desc(dev, "ACPI-WMI mapping"); 217 218 return (0); 219} 220 221/* 222 * Attach the device by: 223 * - Looking for the first ACPI EC device 224 * - Install the notify handler 225 * - Install the EC address space handler 226 * - Look for the _WDG node and read GUID information blocks 227 */ 228static int 229acpi_wmi_attach(device_t dev) 230{ 231 struct acpi_wmi_softc *sc; 232 int ret; 233 ACPI_STATUS status; 234 235 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 236 sc = device_get_softc(dev); 237 ret = ENXIO; 238 239 ACPI_SERIAL_BEGIN(acpi_wmi); 240 sc->wmi_dev = dev; 241 sc->wmi_handle = acpi_get_handle(dev); 242 TAILQ_INIT(&wmi_info_list); 243 /* XXX Only works with one EC, but nearly all systems only have one. */ 244 if ((sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0)) 245 == NULL) 246 device_printf(dev, "cannot find EC device\n"); 247 else if (ACPI_FAILURE((status = AcpiInstallNotifyHandler(sc->wmi_handle, 248 ACPI_DEVICE_NOTIFY, acpi_wmi_notify_handler, sc)))) 249 device_printf(sc->wmi_dev, "couldn't install notify handler - %s\n", 250 AcpiFormatException(status)); 251 else if (ACPI_FAILURE((status = AcpiInstallAddressSpaceHandler( 252 sc->wmi_handle, ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler, 253 NULL, sc)))) { 254 device_printf(sc->wmi_dev, "couldn't install EC handler - %s\n", 255 AcpiFormatException(status)); 256 AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 257 acpi_wmi_notify_handler); 258 } else if (ACPI_FAILURE((status = acpi_wmi_read_wdg_blocks( 259 sc->wmi_handle)))) { 260 device_printf(sc->wmi_dev, "couldn't parse _WDG - %s\n", 261 AcpiFormatException(status)); 262 AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 263 acpi_wmi_notify_handler); 264 AcpiRemoveAddressSpaceHandler(sc->wmi_handle, ACPI_ADR_SPACE_EC, 265 acpi_wmi_ec_handler); 266 } else { 267 sc->wmistat_dev_t = make_dev(&wmistat_cdevsw, 0, UID_ROOT, 268 GID_WHEEL, 0644, "wmistat"); 269 sc->wmistat_dev_t->si_drv1 = sc; 270 sc->wmistat_open_pid = 0; 271 sc->wmistat_bufptr = -1; 272 ret = 0; 273 } 274 ACPI_SERIAL_END(acpi_wmi); 275 276 if (ret == 0) { 277 bus_generic_probe(dev); 278 ret = bus_generic_attach(dev); 279 } 280 281 return (ret); 282} 283 284/* 285 * Detach the driver by: 286 * - Removing notification handler 287 * - Removing address space handler 288 * - Turning off event generation for all WExx event activated by 289 * child drivers 290 */ 291static int 292acpi_wmi_detach(device_t dev) 293{ 294 struct wmi_info *winfo, *tmp; 295 struct acpi_wmi_softc *sc; 296 int ret; 297 298 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 299 sc = device_get_softc(dev); 300 ACPI_SERIAL_BEGIN(acpi_wmi); 301 302 if (sc->wmistat_open_pid != 0) { 303 ret = EBUSY; 304 } else { 305 AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 306 acpi_wmi_notify_handler); 307 AcpiRemoveAddressSpaceHandler(sc->wmi_handle, 308 ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler); 309 TAILQ_FOREACH_SAFE(winfo, &wmi_info_list, wmi_list, tmp) { 310 if (winfo->event_handler) 311 acpi_wmi_toggle_we_event_generation(dev, 312 winfo, EVENT_GENERATION_OFF); 313 TAILQ_REMOVE(&wmi_info_list, winfo, wmi_list); 314 free(winfo, M_ACPIWMI); 315 } 316 if (sc->wmistat_bufptr != -1) { 317 sbuf_delete(&sc->wmistat_sbuf); 318 sc->wmistat_bufptr = -1; 319 } 320 sc->wmistat_open_pid = 0; 321 destroy_dev(sc->wmistat_dev_t); 322 ret = 0; 323 } 324 ACPI_SERIAL_END(acpi_wmi); 325 326 return (ret); 327} 328 329 330/* 331 * Check if the given GUID string (human readable format 332 * AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP) 333 * exists within _WDG 334 */ 335static int 336acpi_wmi_provides_guid_string_method(device_t dev, const char *guid_string) 337{ 338 struct wmi_info *winfo; 339 int ret; 340 341 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 342 ACPI_SERIAL_BEGIN(acpi_wmi); 343 winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string); 344 ret = (winfo == NULL)?0:winfo->ginfo.max_instance+1; 345 ACPI_SERIAL_END(acpi_wmi); 346 347 return (ret); 348} 349 350/* 351 * Call a method "method_id" on the given GUID block 352 * write result into user provided output buffer 353 */ 354static ACPI_STATUS 355acpi_wmi_evaluate_call_method(device_t dev, const char *guid_string, 356 UINT8 instance, UINT32 method_id, const ACPI_BUFFER *in, ACPI_BUFFER *out) 357{ 358 ACPI_OBJECT params[3]; 359 ACPI_OBJECT_LIST input; 360 char method[5] = "WMxx"; 361 struct wmi_info *winfo; 362 struct acpi_wmi_softc *sc; 363 ACPI_STATUS status; 364 365 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 366 367 sc = device_get_softc(dev); 368 ACPI_SERIAL_BEGIN(acpi_wmi); 369 if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 370 == NULL) 371 status = AE_NOT_FOUND; 372 else if (!(winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 373 status = AE_BAD_DATA; 374 else if (instance > winfo->ginfo.max_instance) 375 status = AE_BAD_PARAMETER; 376 else { 377 params[0].Type = ACPI_TYPE_INTEGER; 378 params[0].Integer.Value = instance; 379 params[1].Type = ACPI_TYPE_INTEGER; 380 params[1].Integer.Value = method_id; 381 input.Pointer = params; 382 input.Count = 2; 383 if (in) { 384 params[2].Type = 385 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 386 ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 387 params[2].Buffer.Length = in->Length; 388 params[2].Buffer.Pointer = in->Pointer; 389 input.Count = 3; 390 } 391 method[2] = winfo->ginfo.oid[0]; 392 method[3] = winfo->ginfo.oid[1]; 393 status = AcpiEvaluateObject(sc->wmi_handle, method, 394 &input, out); 395 } 396 ACPI_SERIAL_END(acpi_wmi); 397 398 return (status); 399} 400 401/* 402 * Install a user provided event_handler on the given GUID 403 * provided *data will be passed on callback 404 * If there is already an existing event handler registered it will be silently 405 * discarded 406 */ 407static ACPI_STATUS 408acpi_wmi_install_event_handler_method(device_t dev, const char *guid_string, 409 ACPI_NOTIFY_HANDLER event_handler, void *data) 410{ 411 struct wmi_info *winfo; 412 ACPI_STATUS status; 413 414 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 415 416 status = AE_OK; 417 ACPI_SERIAL_BEGIN(acpi_wmi); 418 if (guid_string == NULL || event_handler == NULL) 419 status = AE_BAD_PARAMETER; 420 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 421 == NULL) 422 status = AE_NOT_EXIST; 423 else if (winfo->event_handler != NULL || 424 (status = acpi_wmi_toggle_we_event_generation(dev, winfo, 425 EVENT_GENERATION_ON)) == AE_OK) { 426 winfo->event_handler = event_handler; 427 winfo->event_handler_user_data = data; 428 } 429 ACPI_SERIAL_END(acpi_wmi); 430 431 return (status); 432} 433 434/* 435 * Remove a previously installed event handler from the given GUID 436 * If there was none installed, this call is silently discarded and 437 * reported as AE_OK 438 */ 439static ACPI_STATUS 440acpi_wmi_remove_event_handler_method(device_t dev, const char *guid_string) 441{ 442 struct wmi_info *winfo; 443 ACPI_STATUS status; 444 445 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 446 447 status = AE_OK; 448 ACPI_SERIAL_BEGIN(acpi_wmi); 449 if (guid_string && 450 (winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 451 != NULL && winfo->event_handler) { 452 status = acpi_wmi_toggle_we_event_generation(dev, winfo, 453 EVENT_GENERATION_OFF); 454 winfo->event_handler = NULL; 455 winfo->event_handler_user_data = NULL; 456 } 457 ACPI_SERIAL_END(acpi_wmi); 458 459 return (status); 460} 461 462/* 463 * Get details on an event received through a callback registered 464 * through ACPI_WMI_REMOVE_EVENT_HANDLER into a user provided output buffer. 465 * (event_id equals "notify" passed in the callback) 466 */ 467static ACPI_STATUS 468acpi_wmi_get_event_data_method(device_t dev, UINT32 event_id, ACPI_BUFFER *out) 469{ 470 ACPI_OBJECT_LIST input; 471 ACPI_OBJECT params[1]; 472 struct acpi_wmi_softc *sc; 473 struct wmi_info *winfo; 474 ACPI_STATUS status; 475 476 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 477 478 sc = device_get_softc(dev); 479 status = AE_NOT_FOUND; 480 ACPI_SERIAL_BEGIN(acpi_wmi); 481 params[0].Type = ACPI_TYPE_INTEGER; 482 params[0].Integer.Value = event_id; 483 input.Pointer = params; 484 input.Count = 1; 485 TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 486 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 487 ((UINT8) winfo->ginfo.oid[0] == event_id)) { 488 status = AcpiEvaluateObject(sc->wmi_handle, "_WED", 489 &input, out); 490 break; 491 } 492 } 493 ACPI_SERIAL_END(acpi_wmi); 494 495 return (status); 496} 497 498/* 499 * Read a block of data from the given GUID (using WQxx (query)) 500 * Will be returned in a user provided buffer (out). 501 * If the method is marked as expensive (ACPI_WMI_REGFLAG_EXPENSIVE) 502 * we will first call the WCxx control method to lock the node to 503 * lock the node for data collection and release it afterwards. 504 * (Failed WCxx calls are ignored to "support" broken implementations) 505 */ 506static ACPI_STATUS 507acpi_wmi_get_block_method(device_t dev, const char *guid_string, UINT8 instance, 508 ACPI_BUFFER *out) 509{ 510 char wc_method[5] = "WCxx"; 511 char wq_method[5] = "WQxx"; 512 ACPI_OBJECT_LIST wc_input; 513 ACPI_OBJECT_LIST wq_input; 514 ACPI_OBJECT wc_params[1]; 515 ACPI_OBJECT wq_params[1]; 516 ACPI_HANDLE wc_handle; 517 struct acpi_wmi_softc *sc; 518 struct wmi_info *winfo; 519 ACPI_STATUS status; 520 ACPI_STATUS wc_status; 521 522 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 523 524 sc = device_get_softc(dev); 525 wc_status = AE_ERROR; 526 ACPI_SERIAL_BEGIN(acpi_wmi); 527 if (guid_string == NULL || out == NULL) 528 status = AE_BAD_PARAMETER; 529 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 530 == NULL) 531 status = AE_ERROR; 532 else if (instance > winfo->ginfo.max_instance) 533 status = AE_BAD_PARAMETER; 534 else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 535 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 536 status = AE_ERROR; 537 else { 538 wq_params[0].Type = ACPI_TYPE_INTEGER; 539 wq_params[0].Integer.Value = instance; 540 wq_input.Pointer = wq_params; 541 wq_input.Count = 1; 542 if (winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) { 543 wc_params[0].Type = ACPI_TYPE_INTEGER; 544 wc_params[0].Integer.Value = 1; 545 wc_input.Pointer = wc_params; 546 wc_input.Count = 1; 547 wc_method[2] = winfo->ginfo.oid[0]; 548 wc_method[3] = winfo->ginfo.oid[1]; 549 wc_status = AcpiGetHandle(sc->wmi_handle, wc_method, 550 &wc_handle); 551 if (ACPI_SUCCESS(wc_status)) 552 wc_status = AcpiEvaluateObject(wc_handle, 553 wc_method, &wc_input, NULL); 554 } 555 wq_method[2] = winfo->ginfo.oid[0]; 556 wq_method[3] = winfo->ginfo.oid[1]; 557 status = AcpiEvaluateObject(sc->wmi_handle, wq_method, 558 &wq_input, out); 559 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) 560 && ACPI_SUCCESS(wc_status)) { 561 wc_params[0].Integer.Value = 0; 562 status = AcpiEvaluateObject(wc_handle, wc_method, 563 &wc_input, NULL); /* XXX this might be 564 the wrong status to 565 return? */ 566 } 567 } 568 ACPI_SERIAL_END(acpi_wmi); 569 570 return (status); 571} 572 573/* 574 * Write a block of data to the given GUID (using WSxx) 575 */ 576static ACPI_STATUS 577acpi_wmi_set_block_method(device_t dev, const char *guid_string, UINT8 instance, 578 const ACPI_BUFFER *in) 579{ 580 char method[5] = "WSxx"; 581 ACPI_OBJECT_LIST input; 582 ACPI_OBJECT params[2]; 583 struct wmi_info *winfo; 584 struct acpi_wmi_softc *sc; 585 ACPI_STATUS status; 586 587 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 588 589 sc = device_get_softc(dev); 590 ACPI_SERIAL_BEGIN(acpi_wmi); 591 if (guid_string == NULL || in == NULL) 592 status = AE_BAD_DATA; 593 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 594 == NULL) 595 status = AE_ERROR; 596 else if (instance > winfo->ginfo.max_instance) 597 status = AE_BAD_PARAMETER; 598 else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 599 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 600 status = AE_ERROR; 601 else { 602 params[0].Type = ACPI_TYPE_INTEGER; 603 params[0].Integer.Value = instance; 604 input.Pointer = params; 605 input.Count = 2; 606 params[1].Type = (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 607 ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 608 params[1].Buffer.Length = in->Length; 609 params[1].Buffer.Pointer = in->Pointer; 610 method[2] = winfo->ginfo.oid[0]; 611 method[3] = winfo->ginfo.oid[1]; 612 status = AcpiEvaluateObject(sc->wmi_handle, method, 613 &input, NULL); 614 } 615 ACPI_SERIAL_END(acpi_wmi); 616 617 return (status); 618} 619 620/* 621 * Handle events received and dispatch them to 622 * stakeholders that registered through ACPI_WMI_INSTALL_EVENT_HANDLER 623 */ 624static void 625acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 626{ 627 ACPI_NOTIFY_HANDLER handler; 628 void *handler_data; 629 struct wmi_info *winfo; 630 631 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 632 633 handler = NULL; 634 handler_data = NULL; 635 ACPI_SERIAL_BEGIN(acpi_wmi); 636 TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 637 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 638 ((UINT8) winfo->ginfo.oid[0] == notify)) { 639 if (winfo->event_handler) { 640 handler = winfo->event_handler; 641 handler_data = winfo->event_handler_user_data; 642 break; 643 } 644 } 645 } 646 ACPI_SERIAL_END(acpi_wmi); 647 if (handler) { 648 handler(h, notify, handler_data); 649 } 650} 651 652/* 653 * Handle EC address space notifications reveived on the WDG node 654 * (this mimics EcAddressSpaceHandler in acpi_ec.c) 655 */ 656static ACPI_STATUS 657acpi_wmi_ec_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 658 UINT32 width, UINT64 *value, void *context, 659 void *region_context) 660{ 661 struct acpi_wmi_softc *sc; 662 int i; 663 UINT64 ec_data; 664 UINT8 ec_addr; 665 ACPI_STATUS status; 666 667 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)address); 668 669 sc = (struct acpi_wmi_softc *)context; 670 if (width % 8 != 0 || value == NULL || context == NULL) 671 return (AE_BAD_PARAMETER); 672 if (address + (width / 8) - 1 > 0xFF) 673 return (AE_BAD_ADDRESS); 674 if (function == ACPI_READ) 675 *value = 0; 676 ec_addr = address; 677 status = AE_ERROR; 678 679 for (i = 0; i < width; i += 8, ++ec_addr) { 680 switch (function) { 681 case ACPI_READ: 682 status = ACPI_EC_READ(sc->ec_dev, ec_addr, &ec_data, 1); 683 if (ACPI_SUCCESS(status)) 684 *value |= ((UINT64)ec_data) << i; 685 break; 686 case ACPI_WRITE: 687 ec_data = (UINT8)((*value) >> i); 688 status = ACPI_EC_WRITE(sc->ec_dev, ec_addr, ec_data, 1); 689 break; 690 default: 691 device_printf(sc->wmi_dev, 692 "invalid acpi_wmi_ec_handler function %d\n", 693 function); 694 status = AE_BAD_PARAMETER; 695 break; 696 } 697 if (ACPI_FAILURE(status)) 698 break; 699 } 700 701 return (status); 702} 703 704/* 705 * Read GUID blocks from the _WDG node 706 * into wmi_info_list. 707 */ 708static ACPI_STATUS 709acpi_wmi_read_wdg_blocks(ACPI_HANDLE h) 710{ 711 ACPI_BUFFER out = {ACPI_ALLOCATE_BUFFER, NULL}; 712 struct guid_info *ginfo; 713 ACPI_OBJECT *obj; 714 struct wmi_info *winfo; 715 UINT32 i; 716 UINT32 wdg_block_count; 717 ACPI_STATUS status; 718 719 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 720 721 ACPI_SERIAL_ASSERT(acpi_wmi); 722 if (ACPI_FAILURE(status = AcpiEvaluateObject(h, "_WDG", NULL, &out))) 723 return (status); 724 obj = (ACPI_OBJECT*) out.Pointer; 725 wdg_block_count = obj->Buffer.Length / sizeof(struct guid_info); 726 if ((ginfo = malloc(obj->Buffer.Length, M_ACPIWMI, M_NOWAIT)) 727 == NULL) { 728 AcpiOsFree(out.Pointer); 729 return (AE_NO_MEMORY); 730 } 731 memcpy(ginfo, obj->Buffer.Pointer, obj->Buffer.Length); 732 for (i = 0; i < wdg_block_count; ++i) { 733 if ((winfo = malloc(sizeof(struct wmi_info), M_ACPIWMI, 734 M_NOWAIT | M_ZERO)) == NULL) { 735 AcpiOsFree(out.Pointer); 736 free(ginfo, M_ACPIWMI); 737 return (AE_NO_MEMORY); 738 } 739 winfo->ginfo = ginfo[i]; 740 TAILQ_INSERT_TAIL(&wmi_info_list, winfo, wmi_list); 741 } 742 AcpiOsFree(out.Pointer); 743 free(ginfo, M_ACPIWMI); 744 745 return (status); 746} 747 748/* 749 * Toggle event generation in for the given GUID (passed by winfo) 750 * Turn on to get notified (through acpi_wmi_notify_handler) if events happen 751 * on the given GUID. 752 */ 753static ACPI_STATUS 754acpi_wmi_toggle_we_event_generation(device_t dev, struct wmi_info *winfo, 755 enum event_generation_state state) 756{ 757 char method[5] = "WExx"; 758 ACPI_OBJECT_LIST input; 759 ACPI_OBJECT params[1]; 760 struct acpi_wmi_softc *sc; 761 ACPI_STATUS status; 762 763 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 764 765 sc = device_get_softc(dev); 766 ACPI_SERIAL_ASSERT(acpi_wmi); 767 params[0].Type = ACPI_TYPE_INTEGER; 768 params[0].Integer.Value = state==EVENT_GENERATION_ON?1:0; 769 input.Pointer = params; 770 input.Count = 1; 771 772 UINT8 hi = ((UINT8) winfo->ginfo.oid[0]) >> 4; 773 UINT8 lo = ((UINT8) winfo->ginfo.oid[0]) & 0xf; 774 method[2] = (hi > 9 ? hi + 55: hi + 48); 775 method[3] = (lo > 9 ? lo + 55: lo + 48); 776 status = AcpiEvaluateObject(sc->wmi_handle, method, &input, NULL); 777 if (status == AE_NOT_FOUND) status = AE_OK; 778 779 return (status); 780} 781 782/* 783 * Convert given two digit hex string (hexin) to an UINT8 referenced 784 * by byteout. 785 * Return != 0 if the was a problem (invalid input) 786 */ 787static __inline int acpi_wmi_hex_to_int(const UINT8 *hexin, UINT8 *byteout) 788{ 789 unsigned int hi; 790 unsigned int lo; 791 792 hi = hexin[0]; 793 lo = hexin[1]; 794 if ('0' <= hi && hi <= '9') 795 hi -= '0'; 796 else if ('A' <= hi && hi <= 'F') 797 hi -= ('A' - 10); 798 else if ('a' <= hi && hi <= 'f') 799 hi -= ('a' - 10); 800 else 801 return (1); 802 if ('0' <= lo && lo <= '9') 803 lo -= '0'; 804 else if ('A' <= lo && lo <= 'F') 805 lo -= ('A' - 10); 806 else if ('a' <= lo && lo <= 'f') 807 lo -= ('a' - 10); 808 else 809 return (1); 810 *byteout = (hi << 4) + lo; 811 812 return (0); 813} 814 815/* 816 * Convert a human readable 36 character GUID into a 16byte 817 * machine readable one. 818 * The basic algorithm looks as follows: 819 * Input: AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 820 * Output: DCBAFEHGIJKLMNOP 821 * (AA BB CC etc. represent two digit hex numbers == bytes) 822 * Return != 0 if passed guid string is invalid 823 */ 824static int 825acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, UINT8 *guid) 826{ 827 static const int mapping[20] = {3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 828 8, 9, -1, 10, 11, 12, 13, 14, 15}; 829 int i; 830 831 for (i = 0; i < 20; ++i, ++guid_string) { 832 if (mapping[i] >= 0) { 833 if (acpi_wmi_hex_to_int(guid_string, 834 &guid[mapping[i]])) 835 return (-1); 836 ++guid_string; 837 } else if (*guid_string != '-') 838 return (-1); 839 } 840 841 return (0); 842} 843 844/* 845 * Lookup a wmi_info structure in wmi_list based on a 846 * human readable GUID 847 * Return NULL if the GUID is unknown in the _WDG 848 */ 849static struct wmi_info* 850acpi_wmi_lookup_wmi_info_by_guid_string(const char *guid_string) 851{ 852 char guid[16]; 853 struct wmi_info *winfo; 854 855 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 856 857 ACPI_SERIAL_ASSERT(acpi_wmi); 858 859 if (!acpi_wmi_guid_string_to_guid(guid_string, guid)) { 860 TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 861 if (!memcmp(winfo->ginfo.guid, guid, 16)) { 862 return (winfo); 863 } 864 } 865 } 866 867 return (NULL); 868} 869 870/* 871 * open wmistat device 872 */ 873static int 874acpi_wmi_wmistat_open(struct cdev* dev, int flags, int mode, struct thread *td) 875{ 876 struct acpi_wmi_softc *sc; 877 int ret; 878 879 if (dev == NULL || dev->si_drv1 == NULL) 880 return (EBADF); 881 sc = dev->si_drv1; 882 883 ACPI_SERIAL_BEGIN(acpi_wmi); 884 if (sc->wmistat_open_pid != 0) { 885 ret = EBUSY; 886 } 887 else { 888 if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 889 == NULL) { 890 ret = ENXIO; 891 } else { 892 sc->wmistat_open_pid = td->td_proc->p_pid; 893 sc->wmistat_bufptr = 0; 894 ret = 0; 895 } 896 } 897 ACPI_SERIAL_END(acpi_wmi); 898 899 return (ret); 900} 901 902/* 903 * close wmistat device 904 */ 905static int 906acpi_wmi_wmistat_close(struct cdev* dev, int flags, int mode, 907 struct thread *td) 908{ 909 struct acpi_wmi_softc *sc; 910 int ret; 911 912 if (dev == NULL || dev->si_drv1 == NULL) 913 return (EBADF); 914 sc = dev->si_drv1; 915 916 ACPI_SERIAL_BEGIN(acpi_wmi); 917 if (sc->wmistat_open_pid == 0) { 918 ret = EBADF; 919 } 920 else { 921 if (sc->wmistat_bufptr != -1) { 922 sbuf_delete(&sc->wmistat_sbuf); 923 sc->wmistat_bufptr = -1; 924 } 925 sc->wmistat_open_pid = 0; 926 ret = 0; 927 } 928 ACPI_SERIAL_END(acpi_wmi); 929 930 return (ret); 931} 932 933/* 934 * Read from wmistat guid information 935 */ 936static int 937acpi_wmi_wmistat_read(struct cdev *dev, struct uio *buf, int flag) 938{ 939 struct acpi_wmi_softc *sc; 940 struct wmi_info *winfo; 941 int l; 942 int ret; 943 UINT8* guid; 944 945 if (dev == NULL || dev->si_drv1 == NULL) 946 return (EBADF); 947 sc = dev->si_drv1; 948 949 ACPI_SERIAL_BEGIN(acpi_wmi); 950 if (sc->wmistat_open_pid != buf->uio_td->td_proc->p_pid || 951 sc->wmistat_bufptr == -1) { 952 ret = EBADF; 953 } 954 else { 955 if (!sbuf_done(&sc->wmistat_sbuf)) { 956 sbuf_printf(&sc->wmistat_sbuf, "GUID " 957 " INST EXPE METH STR " 958 "EVENT OID\n"); 959 TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 960 guid = (UINT8*)winfo->ginfo.guid; 961 sbuf_printf(&sc->wmistat_sbuf, 962 "{%02X%02X%02X%02X-%02X%02X-" 963 "%02X%02X-%02X%02X-%02X%02X" 964 "%02X%02X%02X%02X} %3d %-5s", 965 guid[3], guid[2], guid[1], guid[0], 966 guid[5], guid[4], 967 guid[7], guid[6], 968 guid[8], guid[9], 969 guid[10], guid[11], guid[12], 970 guid[13], guid[14], guid[15], 971 winfo->ginfo.max_instance, 972 (winfo->ginfo.flags& 973 ACPI_WMI_REGFLAG_EXPENSIVE)? 974 "YES":"NO" 975 ); 976 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD) 977 sbuf_printf(&sc->wmistat_sbuf, 978 "WM%c%c ", 979 winfo->ginfo.oid[0], 980 winfo->ginfo.oid[1]); 981 else 982 sbuf_printf(&sc->wmistat_sbuf, "NO "); 983 sbuf_printf(&sc->wmistat_sbuf, "%-4s", 984 (winfo->ginfo.flags& 985 ACPI_WMI_REGFLAG_STRING)?"YES":"NO" 986 ); 987 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT) 988 sbuf_printf(&sc->wmistat_sbuf, 989 "0x%02X%s -\n", 990 (UINT8)winfo->ginfo.oid[0], 991 winfo->event_handler==NULL? 992 " ":"+"); 993 else 994 sbuf_printf(&sc->wmistat_sbuf, 995 "NO %c%c\n", 996 winfo->ginfo.oid[0], 997 winfo->ginfo.oid[1]); 998 } 999 sbuf_finish(&sc->wmistat_sbuf); 1000 } 1001 if (sbuf_len(&sc->wmistat_sbuf) <= 0) { 1002 sbuf_delete(&sc->wmistat_sbuf); 1003 sc->wmistat_bufptr = -1; 1004 sc->wmistat_open_pid = 0; 1005 ret = ENOMEM; 1006 } else { 1007 l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) - 1008 sc->wmistat_bufptr); 1009 ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) + 1010 sc->wmistat_bufptr, l, buf) : 0; 1011 sc->wmistat_bufptr += l; 1012 } 1013 } 1014 ACPI_SERIAL_END(acpi_wmi); 1015 1016 return (ret); 1017} 1018