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