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$"); 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 56 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 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 170 static 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 195 static driver_t acpi_wmi_driver = { 196 "acpi_wmi", 197 acpi_wmi_methods, 198 sizeof(struct acpi_wmi_softc), 199 }; 200 201 static devclass_t acpi_wmi_devclass; 202 DRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, 0, 0); 203 MODULE_VERSION(acpi_wmi, 1); 204 MODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 205 static char *wmi_ids[] = {"PNP0C14", NULL}; 206 207 /* 208 * Probe for the PNP0C14 ACPI node 209 */ 210 static int 211 acpi_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 */ 228 static int 229 acpi_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 */ 291 static int 292 acpi_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 */ 335 static int 336 acpi_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 */ 354 static ACPI_STATUS 355 acpi_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 */ 407 static ACPI_STATUS 408 acpi_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 */ 439 static ACPI_STATUS 440 acpi_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 */ 467 static ACPI_STATUS 468 acpi_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 */ 506 static ACPI_STATUS 507 acpi_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 */ 576 static ACPI_STATUS 577 acpi_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 */ 624 static void 625 acpi_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 */ 656 static ACPI_STATUS 657 acpi_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 */ 708 static ACPI_STATUS 709 acpi_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 */ 753 static ACPI_STATUS 754 acpi_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 */ 787 static __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 */ 824 static int 825 acpi_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 */ 849 static struct wmi_info* 850 acpi_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 */ 873 static int 874 acpi_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 */ 905 static int 906 acpi_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 */ 936 static int 937 acpi_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