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