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: head/sys/dev/acpi_support/acpi_wmi.c 241537 2012-10-14 09:31:11Z avg $ 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 TAILQ_HEAD(wmi_info_list_head, wmi_info) wmi_info_list; 78 }; 79 80 /* 81 * Struct that holds information about 82 * about a single GUID entry in _WDG 83 */ 84 struct guid_info { 85 char guid[16]; /* 16 byte non human readable GUID */ 86 char oid[2]; /* object id or event notify id (first byte) */ 87 UINT8 max_instance; /* highest instance known for this GUID */ 88 UINT8 flags; /* ACPI_WMI_REGFLAG_%s */ 89 }; 90 91 /* WExx event generation state (on/off) */ 92 enum event_generation_state { 93 EVENT_GENERATION_ON = 1, 94 EVENT_GENERATION_OFF = 0 95 }; 96 97 98 /* 99 * Information about one entry in _WDG. 100 * List of those is used to lookup information by GUID. 101 */ 102 struct wmi_info { 103 TAILQ_ENTRY(wmi_info) wmi_list; 104 struct guid_info ginfo; /* information on guid */ 105 ACPI_NOTIFY_HANDLER event_handler;/* client provided event handler */ 106 void *event_handler_user_data; /* ev handler cookie */ 107 }; 108 109 110 ACPI_SERIAL_DECL(acpi_wmi, "ACPI-WMI Mapping"); 111 112 /* public interface - declaration */ 113 /* standard device interface*/ 114 static int acpi_wmi_probe(device_t dev); 115 static int acpi_wmi_attach(device_t dev); 116 static int acpi_wmi_detach(device_t dev); 117 /* see acpi_wmi_if.m */ 118 static int acpi_wmi_provides_guid_string_method(device_t dev, 119 const char *guid_string); 120 static ACPI_STATUS acpi_wmi_evaluate_call_method(device_t dev, 121 const char *guid_string, UINT8 instance, 122 UINT32 method_id, const ACPI_BUFFER *in, 123 ACPI_BUFFER *out); 124 static ACPI_STATUS acpi_wmi_install_event_handler_method(device_t dev, 125 const char *guid_string, ACPI_NOTIFY_HANDLER handler, 126 void *data); 127 static ACPI_STATUS acpi_wmi_remove_event_handler_method(device_t dev, 128 const char *guid_string); 129 static ACPI_STATUS acpi_wmi_get_event_data_method(device_t dev, 130 UINT32 event_id, ACPI_BUFFER *out); 131 static ACPI_STATUS acpi_wmi_get_block_method(device_t dev, 132 const char *guid_string, 133 UINT8 instance, ACPI_BUFFER *out); 134 static ACPI_STATUS acpi_wmi_set_block_method(device_t dev, 135 const char *guid_string, 136 UINT8 instance, const ACPI_BUFFER *in); 137 /* private interface - declaration */ 138 /* callbacks */ 139 static void acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, 140 void *context); 141 static ACPI_STATUS acpi_wmi_ec_handler(UINT32 function, 142 ACPI_PHYSICAL_ADDRESS address, UINT32 width, 143 UINT64 *value, void *context, 144 void *region_context); 145 /* helpers */ 146 static ACPI_STATUS acpi_wmi_read_wdg_blocks(struct acpi_wmi_softc *sc, ACPI_HANDLE h); 147 static ACPI_STATUS acpi_wmi_toggle_we_event_generation(device_t dev, 148 struct wmi_info *winfo, 149 enum event_generation_state state); 150 static int acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, 151 UINT8 *guid); 152 static struct wmi_info* acpi_wmi_lookup_wmi_info_by_guid_string(struct acpi_wmi_softc *sc, 153 const char *guid_string); 154 155 static d_open_t acpi_wmi_wmistat_open; 156 static d_close_t acpi_wmi_wmistat_close; 157 static d_read_t acpi_wmi_wmistat_read; 158 159 /* handler /dev/wmistat device */ 160 static struct dev_ops wmistat_ops = { 161 { "wmistat", 0, D_MPSAFE }, 162 .d_open = acpi_wmi_wmistat_open, 163 .d_close = acpi_wmi_wmistat_close, 164 .d_read = acpi_wmi_wmistat_read, 165 }; 166 167 168 static device_method_t acpi_wmi_methods[] = { 169 /* Device interface */ 170 DEVMETHOD(device_probe, acpi_wmi_probe), 171 DEVMETHOD(device_attach, acpi_wmi_attach), 172 DEVMETHOD(device_detach, acpi_wmi_detach), 173 174 /* bus interface */ 175 DEVMETHOD(bus_add_child, bus_generic_add_child), 176 177 /* acpi_wmi interface */ 178 DEVMETHOD(acpi_wmi_provides_guid_string, 179 acpi_wmi_provides_guid_string_method), 180 DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method), 181 DEVMETHOD(acpi_wmi_install_event_handler, 182 acpi_wmi_install_event_handler_method), 183 DEVMETHOD(acpi_wmi_remove_event_handler, 184 acpi_wmi_remove_event_handler_method), 185 DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method), 186 DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method), 187 DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method), 188 189 DEVMETHOD_END 190 }; 191 192 static driver_t acpi_wmi_driver = { 193 "acpi_wmi", 194 acpi_wmi_methods, 195 sizeof(struct acpi_wmi_softc), 196 }; 197 198 static devclass_t acpi_wmi_devclass; 199 DRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, NULL, NULL); 200 MODULE_VERSION(acpi_wmi, 1); 201 MODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 202 static char *wmi_ids[] = {"PNP0C14", NULL}; 203 204 /* 205 * Probe for the PNP0C14 ACPI node 206 */ 207 static int 208 acpi_wmi_probe(device_t dev) 209 { 210 if (acpi_disabled("wmi") || 211 ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids) == NULL) 212 return (ENXIO); 213 device_set_desc(dev, "ACPI-WMI mapping"); 214 215 return (0); 216 } 217 218 /* 219 * Attach the device by: 220 * - Looking for the first ACPI EC device 221 * - Install the notify handler 222 * - Install the EC address space handler 223 * - Look for the _WDG node and read GUID information blocks 224 */ 225 static int 226 acpi_wmi_attach(device_t dev) 227 { 228 struct acpi_wmi_softc *sc; 229 int ret; 230 ACPI_STATUS status; 231 232 ACPI_SERIAL_INIT(acpi_wmi); 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(&sc->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(sc, 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, &sc->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(&sc->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 acpi_wmi_softc *sc; 337 struct wmi_info *winfo; 338 int ret; 339 340 sc = device_get_softc(dev); 341 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 342 ACPI_SERIAL_BEGIN(acpi_wmi); 343 winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, 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(sc, 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 acpi_wmi_softc *sc = device_get_softc(dev); 412 struct wmi_info *winfo; 413 ACPI_STATUS status; 414 415 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 416 417 status = AE_OK; 418 ACPI_SERIAL_BEGIN(acpi_wmi); 419 if (guid_string == NULL || event_handler == NULL) 420 status = AE_BAD_PARAMETER; 421 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 422 == NULL) 423 status = AE_NOT_EXIST; 424 else if (winfo->event_handler != NULL || 425 (status = acpi_wmi_toggle_we_event_generation(dev, winfo, 426 EVENT_GENERATION_ON)) == AE_OK) { 427 winfo->event_handler = event_handler; 428 winfo->event_handler_user_data = data; 429 } 430 ACPI_SERIAL_END(acpi_wmi); 431 432 return (status); 433 } 434 435 /* 436 * Remove a previously installed event handler from the given GUID 437 * If there was none installed, this call is silently discarded and 438 * reported as AE_OK 439 */ 440 static ACPI_STATUS 441 acpi_wmi_remove_event_handler_method(device_t dev, const char *guid_string) 442 { 443 struct acpi_wmi_softc *sc = device_get_softc(dev); 444 struct wmi_info *winfo; 445 ACPI_STATUS status; 446 447 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 448 449 status = AE_OK; 450 ACPI_SERIAL_BEGIN(acpi_wmi); 451 if (guid_string && 452 (winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 453 != NULL && winfo->event_handler) { 454 status = acpi_wmi_toggle_we_event_generation(dev, winfo, 455 EVENT_GENERATION_OFF); 456 winfo->event_handler = NULL; 457 winfo->event_handler_user_data = NULL; 458 } 459 ACPI_SERIAL_END(acpi_wmi); 460 461 return (status); 462 } 463 464 /* 465 * Get details on an event received through a callback registered 466 * through ACPI_WMI_REMOVE_EVENT_HANDLER into a user provided output buffer. 467 * (event_id equals "notify" passed in the callback) 468 */ 469 static ACPI_STATUS 470 acpi_wmi_get_event_data_method(device_t dev, UINT32 event_id, ACPI_BUFFER *out) 471 { 472 ACPI_OBJECT_LIST input; 473 ACPI_OBJECT params[1]; 474 struct acpi_wmi_softc *sc; 475 struct wmi_info *winfo; 476 ACPI_STATUS status; 477 478 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 479 480 sc = device_get_softc(dev); 481 status = AE_NOT_FOUND; 482 ACPI_SERIAL_BEGIN(acpi_wmi); 483 params[0].Type = ACPI_TYPE_INTEGER; 484 params[0].Integer.Value = event_id; 485 input.Pointer = params; 486 input.Count = 1; 487 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 488 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 489 ((UINT8) winfo->ginfo.oid[0] == event_id)) { 490 status = AcpiEvaluateObject(sc->wmi_handle, "_WED", 491 &input, out); 492 break; 493 } 494 } 495 ACPI_SERIAL_END(acpi_wmi); 496 497 return (status); 498 } 499 500 /* 501 * Read a block of data from the given GUID (using WQxx (query)) 502 * Will be returned in a user provided buffer (out). 503 * If the method is marked as expensive (ACPI_WMI_REGFLAG_EXPENSIVE) 504 * we will first call the WCxx control method to lock the node to 505 * lock the node for data collection and release it afterwards. 506 * (Failed WCxx calls are ignored to "support" broken implementations) 507 */ 508 static ACPI_STATUS 509 acpi_wmi_get_block_method(device_t dev, const char *guid_string, UINT8 instance, 510 ACPI_BUFFER *out) 511 { 512 char wc_method[5] = "WCxx"; 513 char wq_method[5] = "WQxx"; 514 ACPI_OBJECT_LIST wc_input; 515 ACPI_OBJECT_LIST wq_input; 516 ACPI_OBJECT wc_params[1]; 517 ACPI_OBJECT wq_params[1]; 518 ACPI_HANDLE wc_handle; 519 struct acpi_wmi_softc *sc; 520 struct wmi_info *winfo; 521 ACPI_STATUS status; 522 ACPI_STATUS wc_status; 523 524 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 525 526 sc = device_get_softc(dev); 527 wc_status = AE_ERROR; 528 ACPI_SERIAL_BEGIN(acpi_wmi); 529 if (guid_string == NULL || out == NULL) 530 status = AE_BAD_PARAMETER; 531 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 532 == NULL) 533 status = AE_ERROR; 534 else if (instance > winfo->ginfo.max_instance) 535 status = AE_BAD_PARAMETER; 536 else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 537 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 538 status = AE_ERROR; 539 else { 540 wq_params[0].Type = ACPI_TYPE_INTEGER; 541 wq_params[0].Integer.Value = instance; 542 wq_input.Pointer = wq_params; 543 wq_input.Count = 1; 544 if (winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) { 545 wc_params[0].Type = ACPI_TYPE_INTEGER; 546 wc_params[0].Integer.Value = 1; 547 wc_input.Pointer = wc_params; 548 wc_input.Count = 1; 549 wc_method[2] = winfo->ginfo.oid[0]; 550 wc_method[3] = winfo->ginfo.oid[1]; 551 wc_status = AcpiGetHandle(sc->wmi_handle, wc_method, 552 &wc_handle); 553 if (ACPI_SUCCESS(wc_status)) 554 wc_status = AcpiEvaluateObject(wc_handle, 555 wc_method, &wc_input, NULL); 556 } 557 wq_method[2] = winfo->ginfo.oid[0]; 558 wq_method[3] = winfo->ginfo.oid[1]; 559 status = AcpiEvaluateObject(sc->wmi_handle, wq_method, 560 &wq_input, out); 561 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) 562 && ACPI_SUCCESS(wc_status)) { 563 wc_params[0].Integer.Value = 0; 564 status = AcpiEvaluateObject(wc_handle, wc_method, 565 &wc_input, NULL); /* XXX this might be 566 the wrong status to 567 return? */ 568 } 569 } 570 ACPI_SERIAL_END(acpi_wmi); 571 572 return (status); 573 } 574 575 /* 576 * Write a block of data to the given GUID (using WSxx) 577 */ 578 static ACPI_STATUS 579 acpi_wmi_set_block_method(device_t dev, const char *guid_string, UINT8 instance, 580 const ACPI_BUFFER *in) 581 { 582 char method[5] = "WSxx"; 583 ACPI_OBJECT_LIST input; 584 ACPI_OBJECT params[2]; 585 struct wmi_info *winfo; 586 struct acpi_wmi_softc *sc; 587 ACPI_STATUS status; 588 589 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 590 591 sc = device_get_softc(dev); 592 ACPI_SERIAL_BEGIN(acpi_wmi); 593 if (guid_string == NULL || in == NULL) 594 status = AE_BAD_DATA; 595 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 596 == NULL) 597 status = AE_ERROR; 598 else if (instance > winfo->ginfo.max_instance) 599 status = AE_BAD_PARAMETER; 600 else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 601 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 602 status = AE_ERROR; 603 else { 604 params[0].Type = ACPI_TYPE_INTEGER; 605 params[0].Integer.Value = instance; 606 input.Pointer = params; 607 input.Count = 2; 608 params[1].Type = (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 609 ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 610 params[1].Buffer.Length = in->Length; 611 params[1].Buffer.Pointer = in->Pointer; 612 method[2] = winfo->ginfo.oid[0]; 613 method[3] = winfo->ginfo.oid[1]; 614 status = AcpiEvaluateObject(sc->wmi_handle, method, 615 &input, NULL); 616 } 617 ACPI_SERIAL_END(acpi_wmi); 618 619 return (status); 620 } 621 622 /* 623 * Handle events received and dispatch them to 624 * stakeholders that registered through ACPI_WMI_INSTALL_EVENT_HANDLER 625 */ 626 static void 627 acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 628 { 629 struct acpi_wmi_softc *sc = context; 630 ACPI_NOTIFY_HANDLER handler; 631 void *handler_data; 632 struct wmi_info *winfo; 633 634 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 635 636 handler = NULL; 637 handler_data = NULL; 638 ACPI_SERIAL_BEGIN(acpi_wmi); 639 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 640 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 641 ((UINT8) winfo->ginfo.oid[0] == notify)) { 642 if (winfo->event_handler) { 643 handler = winfo->event_handler; 644 handler_data = winfo->event_handler_user_data; 645 break; 646 } 647 } 648 } 649 ACPI_SERIAL_END(acpi_wmi); 650 if (handler) { 651 handler(h, notify, handler_data); 652 } 653 } 654 655 /* 656 * Handle EC address space notifications reveived on the WDG node 657 * (this mimics EcAddressSpaceHandler in acpi_ec.c) 658 */ 659 static ACPI_STATUS 660 acpi_wmi_ec_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 661 UINT32 width, UINT64 *value, void *context, 662 void *region_context) 663 { 664 struct acpi_wmi_softc *sc; 665 int i; 666 UINT64 ec_data; 667 UINT8 ec_addr; 668 ACPI_STATUS status; 669 670 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)address); 671 672 sc = (struct acpi_wmi_softc *)context; 673 if (width % 8 != 0 || value == NULL || context == NULL) 674 return (AE_BAD_PARAMETER); 675 if (address + (width / 8) - 1 > 0xFF) 676 return (AE_BAD_ADDRESS); 677 if (function == ACPI_READ) 678 *value = 0; 679 ec_addr = address; 680 status = AE_ERROR; 681 682 for (i = 0; i < width; i += 8, ++ec_addr) { 683 switch (function) { 684 case ACPI_READ: 685 status = ACPI_EC_READ(sc->ec_dev, ec_addr, &ec_data, 1); 686 if (ACPI_SUCCESS(status)) 687 *value |= ec_data << i; 688 break; 689 case ACPI_WRITE: 690 ec_data = (UINT8)((*value) >> i); 691 status = ACPI_EC_WRITE(sc->ec_dev, ec_addr, ec_data, 1); 692 break; 693 default: 694 device_printf(sc->wmi_dev, 695 "invalid acpi_wmi_ec_handler function %d\n", 696 function); 697 status = AE_BAD_PARAMETER; 698 break; 699 } 700 if (ACPI_FAILURE(status)) 701 break; 702 } 703 704 return (status); 705 } 706 707 /* 708 * Read GUID blocks from the _WDG node 709 * into wmi_info_list. 710 */ 711 static ACPI_STATUS 712 acpi_wmi_read_wdg_blocks(struct acpi_wmi_softc *sc, ACPI_HANDLE h) 713 { 714 ACPI_BUFFER out = {ACPI_ALLOCATE_BUFFER, NULL}; 715 struct guid_info *ginfo; 716 ACPI_OBJECT *obj; 717 struct wmi_info *winfo; 718 UINT32 i; 719 UINT32 wdg_block_count; 720 ACPI_STATUS status; 721 722 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 723 724 ACPI_SERIAL_ASSERT(acpi_wmi); 725 if (ACPI_FAILURE(status = AcpiEvaluateObject(h, "_WDG", NULL, &out))) 726 return (status); 727 obj = (ACPI_OBJECT*) out.Pointer; 728 wdg_block_count = obj->Buffer.Length / sizeof(struct guid_info); 729 if ((ginfo = kmalloc(obj->Buffer.Length, M_ACPIWMI, M_NOWAIT)) 730 == NULL) { 731 AcpiOsFree(out.Pointer); 732 return (AE_NO_MEMORY); 733 } 734 memcpy(ginfo, obj->Buffer.Pointer, obj->Buffer.Length); 735 for (i = 0; i < wdg_block_count; ++i) { 736 if ((winfo = kmalloc(sizeof(struct wmi_info), M_ACPIWMI, 737 M_NOWAIT | M_ZERO)) == NULL) { 738 AcpiOsFree(out.Pointer); 739 kfree(ginfo, M_ACPIWMI); 740 return (AE_NO_MEMORY); 741 } 742 winfo->ginfo = ginfo[i]; 743 TAILQ_INSERT_TAIL(&sc->wmi_info_list, winfo, wmi_list); 744 } 745 AcpiOsFree(out.Pointer); 746 kfree(ginfo, M_ACPIWMI); 747 748 return (status); 749 } 750 751 /* 752 * Toggle event generation in for the given GUID (passed by winfo) 753 * Turn on to get notified (through acpi_wmi_notify_handler) if events happen 754 * on the given GUID. 755 */ 756 static ACPI_STATUS 757 acpi_wmi_toggle_we_event_generation(device_t dev, struct wmi_info *winfo, 758 enum event_generation_state state) 759 { 760 char method[5] = "WExx"; 761 ACPI_OBJECT_LIST input; 762 ACPI_OBJECT params[1]; 763 struct acpi_wmi_softc *sc; 764 ACPI_STATUS status; 765 766 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 767 768 sc = device_get_softc(dev); 769 ACPI_SERIAL_ASSERT(acpi_wmi); 770 params[0].Type = ACPI_TYPE_INTEGER; 771 params[0].Integer.Value = state==EVENT_GENERATION_ON?1:0; 772 input.Pointer = params; 773 input.Count = 1; 774 775 UINT8 hi = ((UINT8) winfo->ginfo.oid[0]) >> 4; 776 UINT8 lo = ((UINT8) winfo->ginfo.oid[0]) & 0xf; 777 method[2] = (hi > 9 ? hi + 55: hi + 48); 778 method[3] = (lo > 9 ? lo + 55: lo + 48); 779 status = AcpiEvaluateObject(sc->wmi_handle, method, &input, NULL); 780 if (status == AE_NOT_FOUND) status = AE_OK; 781 782 return (status); 783 } 784 785 /* 786 * Convert given two digit hex string (hexin) to an UINT8 referenced 787 * by byteout. 788 * Return != 0 if the was a problem (invalid input) 789 */ 790 static __inline int acpi_wmi_hex_to_int(const UINT8 *hexin, UINT8 *byteout) 791 { 792 unsigned int hi; 793 unsigned int lo; 794 795 hi = hexin[0]; 796 lo = hexin[1]; 797 if ('0' <= hi && hi <= '9') 798 hi -= '0'; 799 else if ('A' <= hi && hi <= 'F') 800 hi -= ('A' - 10); 801 else if ('a' <= hi && hi <= 'f') 802 hi -= ('a' - 10); 803 else 804 return (1); 805 if ('0' <= lo && lo <= '9') 806 lo -= '0'; 807 else if ('A' <= lo && lo <= 'F') 808 lo -= ('A' - 10); 809 else if ('a' <= lo && lo <= 'f') 810 lo -= ('a' - 10); 811 else 812 return (1); 813 *byteout = (hi << 4) + lo; 814 815 return (0); 816 } 817 818 /* 819 * Convert a human readable 36 character GUID into a 16byte 820 * machine readable one. 821 * The basic algorithm looks as follows: 822 * Input: AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 823 * Output: DCBAFEHGIJKLMNOP 824 * (AA BB CC etc. represent two digit hex numbers == bytes) 825 * Return != 0 if passed guid string is invalid 826 */ 827 static int 828 acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, UINT8 *guid) 829 { 830 static const int mapping[20] = {3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 831 8, 9, -1, 10, 11, 12, 13, 14, 15}; 832 int i; 833 834 for (i = 0; i < 20; ++i, ++guid_string) { 835 if (mapping[i] >= 0) { 836 if (acpi_wmi_hex_to_int(guid_string, 837 &guid[mapping[i]])) 838 return (-1); 839 ++guid_string; 840 } else if (*guid_string != '-') 841 return (-1); 842 } 843 844 return (0); 845 } 846 847 /* 848 * Lookup a wmi_info structure in wmi_list based on a 849 * human readable GUID 850 * Return NULL if the GUID is unknown in the _WDG 851 */ 852 static struct wmi_info* 853 acpi_wmi_lookup_wmi_info_by_guid_string(struct acpi_wmi_softc *sc, const char *guid_string) 854 { 855 char guid[16]; 856 struct wmi_info *winfo; 857 858 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 859 860 ACPI_SERIAL_ASSERT(acpi_wmi); 861 862 if (!acpi_wmi_guid_string_to_guid(guid_string, guid)) { 863 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 864 if (!memcmp(winfo->ginfo.guid, guid, 16)) { 865 return (winfo); 866 } 867 } 868 } 869 870 return (NULL); 871 } 872 873 /* 874 * open wmistat device 875 */ 876 static int 877 acpi_wmi_wmistat_open(struct dev_open_args *ap) 878 { 879 struct cdev *dev = ap->a_head.a_dev; 880 struct thread *td = curthread; 881 struct acpi_wmi_softc *sc; 882 int ret; 883 884 if (dev == NULL || dev->si_drv1 == NULL) 885 return (EBADF); 886 sc = dev->si_drv1; 887 888 ACPI_SERIAL_BEGIN(acpi_wmi); 889 if (sc->wmistat_open_pid != 0) { 890 ret = EBUSY; 891 } 892 else { 893 if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 894 == NULL) { 895 ret = ENXIO; 896 } else { 897 sc->wmistat_open_pid = td->td_proc->p_pid; 898 sc->wmistat_bufptr = 0; 899 ret = 0; 900 } 901 } 902 ACPI_SERIAL_END(acpi_wmi); 903 904 return (ret); 905 } 906 907 /* 908 * close wmistat device 909 */ 910 static int 911 acpi_wmi_wmistat_close(struct dev_close_args *ap) 912 { 913 struct cdev *dev = ap->a_head.a_dev; 914 struct acpi_wmi_softc *sc; 915 int ret; 916 917 if (dev == NULL || dev->si_drv1 == NULL) 918 return (EBADF); 919 sc = dev->si_drv1; 920 921 ACPI_SERIAL_BEGIN(acpi_wmi); 922 if (sc->wmistat_open_pid == 0) { 923 ret = EBADF; 924 } 925 else { 926 if (sc->wmistat_bufptr != -1) { 927 sbuf_delete(&sc->wmistat_sbuf); 928 sc->wmistat_bufptr = -1; 929 } 930 sc->wmistat_open_pid = 0; 931 ret = 0; 932 } 933 ACPI_SERIAL_END(acpi_wmi); 934 935 return (ret); 936 } 937 938 /* 939 * Read from wmistat guid information 940 */ 941 static int 942 acpi_wmi_wmistat_read(struct dev_read_args *ap) 943 { 944 struct cdev *dev = ap->a_head.a_dev; 945 struct uio *buf = ap->a_uio; 946 struct acpi_wmi_softc *sc; 947 struct wmi_info *winfo; 948 int l; 949 int ret; 950 UINT8* guid; 951 952 if (dev == NULL || dev->si_drv1 == NULL) 953 return (EBADF); 954 sc = dev->si_drv1; 955 956 ACPI_SERIAL_BEGIN(acpi_wmi); 957 if (sc->wmistat_open_pid != buf->uio_td->td_proc->p_pid || 958 sc->wmistat_bufptr == -1) { 959 ret = EBADF; 960 } 961 else { 962 if (!sbuf_done(&sc->wmistat_sbuf)) { 963 sbuf_printf(&sc->wmistat_sbuf, "GUID " 964 " INST EXPE METH STR " 965 "EVENT OID\n"); 966 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 967 guid = (UINT8*)winfo->ginfo.guid; 968 sbuf_printf(&sc->wmistat_sbuf, 969 "{%02X%02X%02X%02X-%02X%02X-" 970 "%02X%02X-%02X%02X-%02X%02X" 971 "%02X%02X%02X%02X} %3d %-5s", 972 guid[3], guid[2], guid[1], guid[0], 973 guid[5], guid[4], 974 guid[7], guid[6], 975 guid[8], guid[9], 976 guid[10], guid[11], guid[12], 977 guid[13], guid[14], guid[15], 978 winfo->ginfo.max_instance, 979 (winfo->ginfo.flags& 980 ACPI_WMI_REGFLAG_EXPENSIVE)? 981 "YES":"NO" 982 ); 983 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD) 984 sbuf_printf(&sc->wmistat_sbuf, 985 "WM%c%c ", 986 winfo->ginfo.oid[0], 987 winfo->ginfo.oid[1]); 988 else 989 sbuf_printf(&sc->wmistat_sbuf, "NO "); 990 sbuf_printf(&sc->wmistat_sbuf, "%-4s", 991 (winfo->ginfo.flags& 992 ACPI_WMI_REGFLAG_STRING)?"YES":"NO" 993 ); 994 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT) 995 sbuf_printf(&sc->wmistat_sbuf, 996 "0x%02X%s -\n", 997 (UINT8)winfo->ginfo.oid[0], 998 winfo->event_handler==NULL? 999 " ":"+"); 1000 else 1001 sbuf_printf(&sc->wmistat_sbuf, 1002 "NO %c%c\n", 1003 winfo->ginfo.oid[0], 1004 winfo->ginfo.oid[1]); 1005 } 1006 sbuf_finish(&sc->wmistat_sbuf); 1007 } 1008 if (sbuf_len(&sc->wmistat_sbuf) <= 0) { 1009 sbuf_delete(&sc->wmistat_sbuf); 1010 sc->wmistat_bufptr = -1; 1011 sc->wmistat_open_pid = 0; 1012 ret = ENOMEM; 1013 } else { 1014 l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) - 1015 sc->wmistat_bufptr); 1016 ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) + 1017 sc->wmistat_bufptr, l, buf) : 0; 1018 sc->wmistat_bufptr += l; 1019 } 1020 } 1021 ACPI_SERIAL_END(acpi_wmi); 1022 1023 return (ret); 1024 } 1025