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 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 cdevsw wmistat_cdevsw = { 161 .d_version = D_VERSION, 162 .d_open = acpi_wmi_wmistat_open, 163 .d_close = acpi_wmi_wmistat_close, 164 .d_read = acpi_wmi_wmistat_read, 165 .d_name = "wmistat", 166 }; 167 168 169 static device_method_t acpi_wmi_methods[] = { 170 /* Device interface */ 171 DEVMETHOD(device_probe, acpi_wmi_probe), 172 DEVMETHOD(device_attach, acpi_wmi_attach), 173 DEVMETHOD(device_detach, acpi_wmi_detach), 174 175 /* bus interface */ 176 DEVMETHOD(bus_add_child, bus_generic_add_child), 177 178 /* acpi_wmi interface */ 179 DEVMETHOD(acpi_wmi_provides_guid_string, 180 acpi_wmi_provides_guid_string_method), 181 DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method), 182 DEVMETHOD(acpi_wmi_install_event_handler, 183 acpi_wmi_install_event_handler_method), 184 DEVMETHOD(acpi_wmi_remove_event_handler, 185 acpi_wmi_remove_event_handler_method), 186 DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method), 187 DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method), 188 DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method), 189 190 DEVMETHOD_END 191 }; 192 193 static driver_t acpi_wmi_driver = { 194 "acpi_wmi", 195 acpi_wmi_methods, 196 sizeof(struct acpi_wmi_softc), 197 }; 198 199 static devclass_t acpi_wmi_devclass; 200 DRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, 0, 0); 201 MODULE_VERSION(acpi_wmi, 1); 202 MODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 203 static char *wmi_ids[] = {"PNP0C14", NULL}; 204 205 /* 206 * Probe for the PNP0C14 ACPI node 207 */ 208 static int 209 acpi_wmi_probe(device_t dev) 210 { 211 if (acpi_disabled("wmi") || 212 ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids) == NULL) 213 return (ENXIO); 214 device_set_desc(dev, "ACPI-WMI mapping"); 215 216 return (0); 217 } 218 219 /* 220 * Attach the device by: 221 * - Looking for the first ACPI EC device 222 * - Install the notify handler 223 * - Install the EC address space handler 224 * - Look for the _WDG node and read GUID information blocks 225 */ 226 static int 227 acpi_wmi_attach(device_t dev) 228 { 229 struct acpi_wmi_softc *sc; 230 int ret; 231 ACPI_STATUS status; 232 233 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 234 sc = device_get_softc(dev); 235 ret = ENXIO; 236 237 ACPI_SERIAL_BEGIN(acpi_wmi); 238 sc->wmi_dev = dev; 239 sc->wmi_handle = acpi_get_handle(dev); 240 TAILQ_INIT(&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_cdevsw, 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_SAFE(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 free(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 |= ((UINT64)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 = malloc(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 = malloc(sizeof(struct wmi_info), M_ACPIWMI, 737 M_NOWAIT | M_ZERO)) == NULL) { 738 AcpiOsFree(out.Pointer); 739 free(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 free(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 cdev* dev, int flags, int mode, struct thread *td) 878 { 879 struct acpi_wmi_softc *sc; 880 int ret; 881 882 if (dev == NULL || dev->si_drv1 == NULL) 883 return (EBADF); 884 sc = dev->si_drv1; 885 886 ACPI_SERIAL_BEGIN(acpi_wmi); 887 if (sc->wmistat_open_pid != 0) { 888 ret = EBUSY; 889 } 890 else { 891 if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 892 == NULL) { 893 ret = ENXIO; 894 } else { 895 sc->wmistat_open_pid = td->td_proc->p_pid; 896 sc->wmistat_bufptr = 0; 897 ret = 0; 898 } 899 } 900 ACPI_SERIAL_END(acpi_wmi); 901 902 return (ret); 903 } 904 905 /* 906 * close wmistat device 907 */ 908 static int 909 acpi_wmi_wmistat_close(struct cdev* dev, int flags, int mode, 910 struct thread *td) 911 { 912 struct acpi_wmi_softc *sc; 913 int ret; 914 915 if (dev == NULL || dev->si_drv1 == NULL) 916 return (EBADF); 917 sc = dev->si_drv1; 918 919 ACPI_SERIAL_BEGIN(acpi_wmi); 920 if (sc->wmistat_open_pid == 0) { 921 ret = EBADF; 922 } 923 else { 924 if (sc->wmistat_bufptr != -1) { 925 sbuf_delete(&sc->wmistat_sbuf); 926 sc->wmistat_bufptr = -1; 927 } 928 sc->wmistat_open_pid = 0; 929 ret = 0; 930 } 931 ACPI_SERIAL_END(acpi_wmi); 932 933 return (ret); 934 } 935 936 /* 937 * Read from wmistat guid information 938 */ 939 static int 940 acpi_wmi_wmistat_read(struct cdev *dev, struct uio *buf, int flag) 941 { 942 struct acpi_wmi_softc *sc; 943 struct wmi_info *winfo; 944 int l; 945 int ret; 946 UINT8* guid; 947 948 if (dev == NULL || dev->si_drv1 == NULL) 949 return (EBADF); 950 sc = dev->si_drv1; 951 952 ACPI_SERIAL_BEGIN(acpi_wmi); 953 if (sc->wmistat_open_pid != buf->uio_td->td_proc->p_pid || 954 sc->wmistat_bufptr == -1) { 955 ret = EBADF; 956 } 957 else { 958 if (!sbuf_done(&sc->wmistat_sbuf)) { 959 sbuf_printf(&sc->wmistat_sbuf, "GUID " 960 " INST EXPE METH STR " 961 "EVENT OID\n"); 962 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 963 guid = (UINT8*)winfo->ginfo.guid; 964 sbuf_printf(&sc->wmistat_sbuf, 965 "{%02X%02X%02X%02X-%02X%02X-" 966 "%02X%02X-%02X%02X-%02X%02X" 967 "%02X%02X%02X%02X} %3d %-5s", 968 guid[3], guid[2], guid[1], guid[0], 969 guid[5], guid[4], 970 guid[7], guid[6], 971 guid[8], guid[9], 972 guid[10], guid[11], guid[12], 973 guid[13], guid[14], guid[15], 974 winfo->ginfo.max_instance, 975 (winfo->ginfo.flags& 976 ACPI_WMI_REGFLAG_EXPENSIVE)? 977 "YES":"NO" 978 ); 979 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD) 980 sbuf_printf(&sc->wmistat_sbuf, 981 "WM%c%c ", 982 winfo->ginfo.oid[0], 983 winfo->ginfo.oid[1]); 984 else 985 sbuf_printf(&sc->wmistat_sbuf, "NO "); 986 sbuf_printf(&sc->wmistat_sbuf, "%-4s", 987 (winfo->ginfo.flags& 988 ACPI_WMI_REGFLAG_STRING)?"YES":"NO" 989 ); 990 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT) 991 sbuf_printf(&sc->wmistat_sbuf, 992 "0x%02X%s -\n", 993 (UINT8)winfo->ginfo.oid[0], 994 winfo->event_handler==NULL? 995 " ":"+"); 996 else 997 sbuf_printf(&sc->wmistat_sbuf, 998 "NO %c%c\n", 999 winfo->ginfo.oid[0], 1000 winfo->ginfo.oid[1]); 1001 } 1002 sbuf_finish(&sc->wmistat_sbuf); 1003 } 1004 if (sbuf_len(&sc->wmistat_sbuf) <= 0) { 1005 sbuf_delete(&sc->wmistat_sbuf); 1006 sc->wmistat_bufptr = -1; 1007 sc->wmistat_open_pid = 0; 1008 ret = ENOMEM; 1009 } else { 1010 l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) - 1011 sc->wmistat_bufptr); 1012 ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) + 1013 sc->wmistat_bufptr, l, buf) : 0; 1014 sc->wmistat_bufptr += l; 1015 } 1016 } 1017 ACPI_SERIAL_END(acpi_wmi); 1018 1019 return (ret); 1020 } 1021