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 /* 29 * Driver for acpi-wmi mapping, provides an interface for vendor specific 30 * implementations (e.g. HP and Acer laptops). 31 * Inspired by the ACPI-WMI mapping driver (c) 2008-2008 Carlos Corbacho which 32 * implements this functionality for Linux. 33 * 34 * WMI and ACPI: http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx 35 * acpi-wmi for Linux: http://www.kernel.org 36 */ 37 38 #include "opt_acpi.h" 39 #include <sys/param.h> 40 #include <sys/conf.h> 41 #include <sys/uio.h> 42 #include <sys/proc.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/sbuf.h> 46 #include <sys/module.h> 47 #include <sys/bus.h> 48 49 #include <contrib/dev/acpica/include/acpi.h> 50 #include <contrib/dev/acpica/include/accommon.h> 51 #include <dev/acpica/acpivar.h> 52 #include "acpi_wmi_if.h" 53 54 static MALLOC_DEFINE(M_ACPIWMI, "acpiwmi", "ACPI-WMI mapping"); 55 56 #define _COMPONENT ACPI_OEM 57 ACPI_MODULE_NAME("ACPI_WMI"); 58 59 #define ACPI_WMI_REGFLAG_EXPENSIVE 0x1 /* GUID flag: Expensive operation */ 60 #define ACPI_WMI_REGFLAG_METHOD 0x2 /* GUID flag: Method call */ 61 #define ACPI_WMI_REGFLAG_STRING 0x4 /* GUID flag: String */ 62 #define ACPI_WMI_REGFLAG_EVENT 0x8 /* GUID flag: Event */ 63 #define ACPI_WMI_BMOF_UUID "05901221-D566-11D1-B2F0-00A0C9062910" 64 65 /* 66 * acpi_wmi driver private structure 67 */ 68 struct acpi_wmi_softc { 69 device_t wmi_dev; /* wmi device id */ 70 ACPI_HANDLE wmi_handle; /* handle of the PNP0C14 node */ 71 device_t ec_dev; /* acpi_ec0 */ 72 struct cdev *wmistat_dev_t; /* wmistat device handle */ 73 struct sbuf wmistat_sbuf; /* sbuf for /dev/wmistat output */ 74 pid_t wmistat_open_pid; /* pid operating on /dev/wmistat */ 75 int wmistat_bufptr; /* /dev/wmistat ptr to buffer position */ 76 char *mofbuf; 77 78 TAILQ_HEAD(wmi_info_list_head, wmi_info) wmi_info_list; 79 }; 80 81 /* 82 * Struct that holds information about 83 * about a single GUID entry in _WDG 84 */ 85 struct guid_info { 86 char guid[16]; /* 16 byte non human readable GUID */ 87 char oid[2]; /* object id or event notify id (first byte) */ 88 UINT8 max_instance; /* highest instance known for this GUID */ 89 UINT8 flags; /* ACPI_WMI_REGFLAG_%s */ 90 }; 91 92 /* WExx event generation state (on/off) */ 93 enum event_generation_state { 94 EVENT_GENERATION_ON = 1, 95 EVENT_GENERATION_OFF = 0 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 ACPI_SERIAL_DECL(acpi_wmi, "ACPI-WMI Mapping"); 110 111 /* public interface - declaration */ 112 /* standard device interface*/ 113 static int acpi_wmi_probe(device_t dev); 114 static int acpi_wmi_attach(device_t dev); 115 static int acpi_wmi_detach(device_t dev); 116 /* see acpi_wmi_if.m */ 117 static int acpi_wmi_provides_guid_string_method(device_t dev, 118 const char *guid_string); 119 static ACPI_STATUS acpi_wmi_evaluate_call_method(device_t dev, 120 const char *guid_string, UINT8 instance, 121 UINT32 method_id, const ACPI_BUFFER *in, 122 ACPI_BUFFER *out); 123 static ACPI_STATUS acpi_wmi_install_event_handler_method(device_t dev, 124 const char *guid_string, ACPI_NOTIFY_HANDLER handler, 125 void *data); 126 static ACPI_STATUS acpi_wmi_remove_event_handler_method(device_t dev, 127 const char *guid_string); 128 static ACPI_STATUS acpi_wmi_get_event_data_method(device_t dev, 129 UINT32 event_id, ACPI_BUFFER *out); 130 static ACPI_STATUS acpi_wmi_get_block_method(device_t dev, 131 const char *guid_string, 132 UINT8 instance, ACPI_BUFFER *out); 133 static ACPI_STATUS acpi_wmi_set_block_method(device_t dev, 134 const char *guid_string, 135 UINT8 instance, const ACPI_BUFFER *in); 136 /* private interface - declaration */ 137 /* callbacks */ 138 static void acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, 139 void *context); 140 static ACPI_STATUS acpi_wmi_ec_handler(UINT32 function, 141 ACPI_PHYSICAL_ADDRESS address, UINT32 width, 142 UINT64 *value, void *context, 143 void *region_context); 144 /* helpers */ 145 static ACPI_STATUS acpi_wmi_read_wdg_blocks(struct acpi_wmi_softc *sc, ACPI_HANDLE h); 146 static ACPI_STATUS acpi_wmi_toggle_we_event_generation(device_t dev, 147 struct wmi_info *winfo, 148 enum event_generation_state state); 149 static int acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, 150 UINT8 *guid); 151 static struct wmi_info* acpi_wmi_lookup_wmi_info_by_guid_string(struct acpi_wmi_softc *sc, 152 const char *guid_string); 153 154 static d_open_t acpi_wmi_wmistat_open; 155 static d_close_t acpi_wmi_wmistat_close; 156 static d_read_t acpi_wmi_wmistat_read; 157 158 /* handler /dev/wmistat device */ 159 static struct cdevsw wmistat_cdevsw = { 160 .d_version = D_VERSION, 161 .d_open = acpi_wmi_wmistat_open, 162 .d_close = acpi_wmi_wmistat_close, 163 .d_read = acpi_wmi_wmistat_read, 164 .d_name = "wmistat", 165 }; 166 167 static device_method_t acpi_wmi_methods[] = { 168 /* Device interface */ 169 DEVMETHOD(device_probe, acpi_wmi_probe), 170 DEVMETHOD(device_attach, acpi_wmi_attach), 171 DEVMETHOD(device_detach, acpi_wmi_detach), 172 173 /* bus interface */ 174 DEVMETHOD(bus_add_child, bus_generic_add_child), 175 176 /* acpi_wmi interface */ 177 DEVMETHOD(acpi_wmi_provides_guid_string, 178 acpi_wmi_provides_guid_string_method), 179 DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method), 180 DEVMETHOD(acpi_wmi_install_event_handler, 181 acpi_wmi_install_event_handler_method), 182 DEVMETHOD(acpi_wmi_remove_event_handler, 183 acpi_wmi_remove_event_handler_method), 184 DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method), 185 DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method), 186 DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method), 187 188 DEVMETHOD_END 189 }; 190 191 static driver_t acpi_wmi_driver = { 192 "acpi_wmi", 193 acpi_wmi_methods, 194 sizeof(struct acpi_wmi_softc), 195 }; 196 197 DRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, 0, 0); 198 MODULE_VERSION(acpi_wmi, 1); 199 MODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 200 static char *wmi_ids[] = {"PNP0C14", NULL}; 201 ACPI_PNP_INFO(wmi_ids); 202 203 /* 204 * Probe for the PNP0C14 ACPI node 205 */ 206 static int 207 acpi_wmi_probe(device_t dev) 208 { 209 int rv; 210 211 if (acpi_disabled("wmi")) 212 return (ENXIO); 213 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids, NULL); 214 if (rv <= 0) 215 device_set_desc(dev, "ACPI-WMI mapping"); 216 217 return (rv); 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(&sc->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 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(sc, 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 (acpi_wmi_provides_guid_string_method(dev, ACPI_WMI_BMOF_UUID)) { 276 ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; 277 ACPI_OBJECT *obj; 278 279 device_printf(dev, "Embedded MOF found\n"); 280 status = acpi_wmi_get_block_method(dev, ACPI_WMI_BMOF_UUID, 281 0, &out); 282 if (ACPI_SUCCESS(status)) { 283 obj = out.Pointer; 284 if (obj && obj->Type == ACPI_TYPE_BUFFER) { 285 SYSCTL_ADD_OPAQUE(device_get_sysctl_ctx(dev), 286 SYSCTL_CHILDREN( 287 device_get_sysctl_tree(dev)), 288 OID_AUTO, "bmof", 289 CTLFLAG_RD | CTLFLAG_MPSAFE, 290 obj->Buffer.Pointer, 291 obj->Buffer.Length, 292 "A", "MOF Blob"); 293 } 294 } 295 sc->mofbuf = out.Pointer; 296 } 297 298 if (ret == 0) { 299 bus_identify_children(dev); 300 bus_attach_children(dev); 301 } 302 303 return (ret); 304 } 305 306 /* 307 * Detach the driver by: 308 * - Removing notification handler 309 * - Removing address space handler 310 * - Turning off event generation for all WExx event activated by 311 * child drivers 312 */ 313 static int 314 acpi_wmi_detach(device_t dev) 315 { 316 struct wmi_info *winfo, *tmp; 317 struct acpi_wmi_softc *sc; 318 int ret; 319 320 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 321 sc = device_get_softc(dev); 322 ACPI_SERIAL_BEGIN(acpi_wmi); 323 324 if (sc->wmistat_open_pid != 0) { 325 ret = EBUSY; 326 } else { 327 AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 328 acpi_wmi_notify_handler); 329 AcpiRemoveAddressSpaceHandler(sc->wmi_handle, 330 ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler); 331 TAILQ_FOREACH_SAFE(winfo, &sc->wmi_info_list, wmi_list, tmp) { 332 if (winfo->event_handler) 333 acpi_wmi_toggle_we_event_generation(dev, 334 winfo, EVENT_GENERATION_OFF); 335 TAILQ_REMOVE(&sc->wmi_info_list, winfo, wmi_list); 336 free(winfo, M_ACPIWMI); 337 } 338 if (sc->wmistat_bufptr != -1) { 339 sbuf_delete(&sc->wmistat_sbuf); 340 sc->wmistat_bufptr = -1; 341 } 342 sc->wmistat_open_pid = 0; 343 destroy_dev(sc->wmistat_dev_t); 344 ret = 0; 345 AcpiOsFree(sc->mofbuf); 346 } 347 ACPI_SERIAL_END(acpi_wmi); 348 349 return (ret); 350 } 351 352 /* 353 * Check if the given GUID string (human readable format 354 * AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP) 355 * exists within _WDG 356 */ 357 static int 358 acpi_wmi_provides_guid_string_method(device_t dev, const char *guid_string) 359 { 360 struct acpi_wmi_softc *sc; 361 struct wmi_info *winfo; 362 int ret; 363 364 sc = device_get_softc(dev); 365 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 366 ACPI_SERIAL_BEGIN(acpi_wmi); 367 winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string); 368 ret = (winfo == NULL)?0:winfo->ginfo.max_instance+1; 369 ACPI_SERIAL_END(acpi_wmi); 370 371 return (ret); 372 } 373 374 /* 375 * Call a method "method_id" on the given GUID block 376 * write result into user provided output buffer 377 */ 378 static ACPI_STATUS 379 acpi_wmi_evaluate_call_method(device_t dev, const char *guid_string, 380 UINT8 instance, UINT32 method_id, const ACPI_BUFFER *in, ACPI_BUFFER *out) 381 { 382 ACPI_OBJECT params[3]; 383 ACPI_OBJECT_LIST input; 384 char method[5] = "WMxx"; 385 struct wmi_info *winfo; 386 struct acpi_wmi_softc *sc; 387 ACPI_STATUS status; 388 389 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 390 391 sc = device_get_softc(dev); 392 ACPI_SERIAL_BEGIN(acpi_wmi); 393 if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 394 == NULL) 395 status = AE_NOT_FOUND; 396 else if (!(winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 397 status = AE_BAD_DATA; 398 else if (instance > winfo->ginfo.max_instance) 399 status = AE_BAD_PARAMETER; 400 else { 401 params[0].Type = ACPI_TYPE_INTEGER; 402 params[0].Integer.Value = instance; 403 params[1].Type = ACPI_TYPE_INTEGER; 404 params[1].Integer.Value = method_id; 405 input.Pointer = params; 406 input.Count = 2; 407 if (in) { 408 params[2].Type = 409 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 410 ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 411 params[2].Buffer.Length = in->Length; 412 params[2].Buffer.Pointer = in->Pointer; 413 input.Count = 3; 414 } 415 method[2] = winfo->ginfo.oid[0]; 416 method[3] = winfo->ginfo.oid[1]; 417 status = AcpiEvaluateObject(sc->wmi_handle, method, 418 &input, out); 419 } 420 ACPI_SERIAL_END(acpi_wmi); 421 422 return (status); 423 } 424 425 /* 426 * Install a user provided event_handler on the given GUID 427 * provided *data will be passed on callback 428 * If there is already an existing event handler registered it will be silently 429 * discarded 430 */ 431 static ACPI_STATUS 432 acpi_wmi_install_event_handler_method(device_t dev, const char *guid_string, 433 ACPI_NOTIFY_HANDLER event_handler, void *data) 434 { 435 struct acpi_wmi_softc *sc = device_get_softc(dev); 436 struct wmi_info *winfo; 437 ACPI_STATUS status; 438 439 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 440 441 status = AE_OK; 442 ACPI_SERIAL_BEGIN(acpi_wmi); 443 if (guid_string == NULL || event_handler == NULL) 444 status = AE_BAD_PARAMETER; 445 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 446 == NULL) 447 status = AE_NOT_EXIST; 448 else if (winfo->event_handler != NULL || 449 (status = acpi_wmi_toggle_we_event_generation(dev, winfo, 450 EVENT_GENERATION_ON)) == AE_OK) { 451 winfo->event_handler = event_handler; 452 winfo->event_handler_user_data = data; 453 } 454 ACPI_SERIAL_END(acpi_wmi); 455 456 return (status); 457 } 458 459 /* 460 * Remove a previously installed event handler from the given GUID 461 * If there was none installed, this call is silently discarded and 462 * reported as AE_OK 463 */ 464 static ACPI_STATUS 465 acpi_wmi_remove_event_handler_method(device_t dev, const char *guid_string) 466 { 467 struct acpi_wmi_softc *sc = device_get_softc(dev); 468 struct wmi_info *winfo; 469 ACPI_STATUS status; 470 471 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 472 473 status = AE_OK; 474 ACPI_SERIAL_BEGIN(acpi_wmi); 475 if (guid_string && 476 (winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 477 != NULL && winfo->event_handler) { 478 status = acpi_wmi_toggle_we_event_generation(dev, winfo, 479 EVENT_GENERATION_OFF); 480 winfo->event_handler = NULL; 481 winfo->event_handler_user_data = NULL; 482 } 483 ACPI_SERIAL_END(acpi_wmi); 484 485 return (status); 486 } 487 488 /* 489 * Get details on an event received through a callback registered 490 * through ACPI_WMI_REMOVE_EVENT_HANDLER into a user provided output buffer. 491 * (event_id equals "notify" passed in the callback) 492 */ 493 static ACPI_STATUS 494 acpi_wmi_get_event_data_method(device_t dev, UINT32 event_id, ACPI_BUFFER *out) 495 { 496 ACPI_OBJECT_LIST input; 497 ACPI_OBJECT params[1]; 498 struct acpi_wmi_softc *sc; 499 struct wmi_info *winfo; 500 ACPI_STATUS status; 501 502 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 503 504 sc = device_get_softc(dev); 505 status = AE_NOT_FOUND; 506 ACPI_SERIAL_BEGIN(acpi_wmi); 507 params[0].Type = ACPI_TYPE_INTEGER; 508 params[0].Integer.Value = event_id; 509 input.Pointer = params; 510 input.Count = 1; 511 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 512 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 513 ((UINT8) winfo->ginfo.oid[0] == event_id)) { 514 status = AcpiEvaluateObject(sc->wmi_handle, "_WED", 515 &input, out); 516 break; 517 } 518 } 519 ACPI_SERIAL_END(acpi_wmi); 520 521 return (status); 522 } 523 524 /* 525 * Read a block of data from the given GUID (using WQxx (query)) 526 * Will be returned in a user provided buffer (out). 527 * If the method is marked as expensive (ACPI_WMI_REGFLAG_EXPENSIVE) 528 * we will first call the WCxx control method to lock the node to 529 * lock the node for data collection and release it afterwards. 530 * (Failed WCxx calls are ignored to "support" broken implementations) 531 */ 532 static ACPI_STATUS 533 acpi_wmi_get_block_method(device_t dev, const char *guid_string, UINT8 instance, 534 ACPI_BUFFER *out) 535 { 536 char wc_method[5] = "WCxx"; 537 char wq_method[5] = "WQxx"; 538 ACPI_OBJECT_LIST wc_input; 539 ACPI_OBJECT_LIST wq_input; 540 ACPI_OBJECT wc_params[1]; 541 ACPI_OBJECT wq_params[1]; 542 ACPI_HANDLE wc_handle; 543 struct acpi_wmi_softc *sc; 544 struct wmi_info *winfo; 545 ACPI_STATUS status; 546 ACPI_STATUS wc_status; 547 548 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 549 550 sc = device_get_softc(dev); 551 wc_status = AE_ERROR; 552 ACPI_SERIAL_BEGIN(acpi_wmi); 553 if (guid_string == NULL || out == NULL) 554 status = AE_BAD_PARAMETER; 555 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 556 == NULL) 557 status = AE_ERROR; 558 else if (instance > winfo->ginfo.max_instance) 559 status = AE_BAD_PARAMETER; 560 else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 561 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 562 status = AE_ERROR; 563 else { 564 wq_params[0].Type = ACPI_TYPE_INTEGER; 565 wq_params[0].Integer.Value = instance; 566 wq_input.Pointer = wq_params; 567 wq_input.Count = 1; 568 if (winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) { 569 wc_params[0].Type = ACPI_TYPE_INTEGER; 570 wc_params[0].Integer.Value = 1; 571 wc_input.Pointer = wc_params; 572 wc_input.Count = 1; 573 wc_method[2] = winfo->ginfo.oid[0]; 574 wc_method[3] = winfo->ginfo.oid[1]; 575 wc_status = AcpiGetHandle(sc->wmi_handle, wc_method, 576 &wc_handle); 577 if (ACPI_SUCCESS(wc_status)) 578 wc_status = AcpiEvaluateObject(wc_handle, 579 wc_method, &wc_input, NULL); 580 } 581 wq_method[2] = winfo->ginfo.oid[0]; 582 wq_method[3] = winfo->ginfo.oid[1]; 583 { 584 ACPI_HANDLE wq_handle; 585 ACPI_OBJECT_TYPE at; 586 587 if (ACPI_SUCCESS(AcpiGetHandle(sc->wmi_handle, wq_method, &wq_handle)) && 588 ACPI_SUCCESS(AcpiGetType(wq_handle, &at)) && 589 at != ACPI_TYPE_METHOD) { 590 wq_input.Count = 0; 591 } 592 } 593 status = AcpiEvaluateObject(sc->wmi_handle, wq_method, 594 &wq_input, out); 595 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) 596 && ACPI_SUCCESS(wc_status)) { 597 wc_params[0].Integer.Value = 0; 598 status = AcpiEvaluateObject(wc_handle, wc_method, 599 &wc_input, NULL); /* XXX this might be 600 the wrong status to 601 return? */ 602 } 603 } 604 ACPI_SERIAL_END(acpi_wmi); 605 606 return (status); 607 } 608 609 /* 610 * Write a block of data to the given GUID (using WSxx) 611 */ 612 static ACPI_STATUS 613 acpi_wmi_set_block_method(device_t dev, const char *guid_string, UINT8 instance, 614 const ACPI_BUFFER *in) 615 { 616 char method[5] = "WSxx"; 617 ACPI_OBJECT_LIST input; 618 ACPI_OBJECT params[2]; 619 struct wmi_info *winfo; 620 struct acpi_wmi_softc *sc; 621 ACPI_STATUS status; 622 623 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 624 625 sc = device_get_softc(dev); 626 ACPI_SERIAL_BEGIN(acpi_wmi); 627 if (guid_string == NULL || in == NULL) 628 status = AE_BAD_DATA; 629 else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 630 == NULL) 631 status = AE_ERROR; 632 else if (instance > winfo->ginfo.max_instance) 633 status = AE_BAD_PARAMETER; 634 else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 635 (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 636 status = AE_ERROR; 637 else { 638 params[0].Type = ACPI_TYPE_INTEGER; 639 params[0].Integer.Value = instance; 640 input.Pointer = params; 641 input.Count = 2; 642 params[1].Type = (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 643 ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 644 params[1].Buffer.Length = in->Length; 645 params[1].Buffer.Pointer = in->Pointer; 646 method[2] = winfo->ginfo.oid[0]; 647 method[3] = winfo->ginfo.oid[1]; 648 status = AcpiEvaluateObject(sc->wmi_handle, method, 649 &input, NULL); 650 } 651 ACPI_SERIAL_END(acpi_wmi); 652 653 return (status); 654 } 655 656 /* 657 * Handle events received and dispatch them to 658 * stakeholders that registered through ACPI_WMI_INSTALL_EVENT_HANDLER 659 */ 660 static void 661 acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 662 { 663 struct acpi_wmi_softc *sc = context; 664 ACPI_NOTIFY_HANDLER handler; 665 void *handler_data; 666 struct wmi_info *winfo; 667 668 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 669 670 handler = NULL; 671 handler_data = NULL; 672 ACPI_SERIAL_BEGIN(acpi_wmi); 673 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 674 if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 675 ((UINT8) winfo->ginfo.oid[0] == notify)) { 676 if (winfo->event_handler) { 677 handler = winfo->event_handler; 678 handler_data = winfo->event_handler_user_data; 679 break; 680 } 681 } 682 } 683 ACPI_SERIAL_END(acpi_wmi); 684 if (handler) { 685 handler(h, notify, handler_data); 686 } 687 } 688 689 /* 690 * Handle EC address space notifications reveived on the WDG node 691 * (this mimics EcAddressSpaceHandler in acpi_ec.c) 692 */ 693 static ACPI_STATUS 694 acpi_wmi_ec_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 695 UINT32 width, UINT64 *value, void *context, 696 void *region_context) 697 { 698 struct acpi_wmi_softc *sc; 699 int i; 700 UINT64 ec_data; 701 UINT8 ec_addr; 702 ACPI_STATUS status; 703 704 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)address); 705 706 sc = (struct acpi_wmi_softc *)context; 707 if (width % 8 != 0 || value == NULL || context == NULL) 708 return (AE_BAD_PARAMETER); 709 if (address + (width / 8) - 1 > 0xFF) 710 return (AE_BAD_ADDRESS); 711 if (sc->ec_dev == NULL) 712 return (AE_NOT_FOUND); 713 if (function == ACPI_READ) 714 *value = 0; 715 ec_addr = address; 716 status = AE_ERROR; 717 718 for (i = 0; i < width; i += 8, ++ec_addr) { 719 switch (function) { 720 case ACPI_READ: 721 status = ACPI_EC_READ(sc->ec_dev, ec_addr, &ec_data, 1); 722 if (ACPI_SUCCESS(status)) 723 *value |= ((UINT64)ec_data) << i; 724 break; 725 case ACPI_WRITE: 726 ec_data = (UINT8)((*value) >> i); 727 status = ACPI_EC_WRITE(sc->ec_dev, ec_addr, ec_data, 1); 728 break; 729 default: 730 device_printf(sc->wmi_dev, 731 "invalid acpi_wmi_ec_handler function %d\n", 732 function); 733 status = AE_BAD_PARAMETER; 734 break; 735 } 736 if (ACPI_FAILURE(status)) 737 break; 738 } 739 740 return (status); 741 } 742 743 /* 744 * Read GUID blocks from the _WDG node 745 * into wmi_info_list. 746 */ 747 static ACPI_STATUS 748 acpi_wmi_read_wdg_blocks(struct acpi_wmi_softc *sc, ACPI_HANDLE h) 749 { 750 ACPI_BUFFER out = {ACPI_ALLOCATE_BUFFER, NULL}; 751 struct guid_info *ginfo; 752 ACPI_OBJECT *obj; 753 struct wmi_info *winfo; 754 UINT32 i; 755 UINT32 wdg_block_count; 756 ACPI_STATUS status; 757 758 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 759 760 ACPI_SERIAL_ASSERT(acpi_wmi); 761 if (ACPI_FAILURE(status = AcpiEvaluateObject(h, "_WDG", NULL, &out))) 762 return (status); 763 obj = (ACPI_OBJECT*) out.Pointer; 764 wdg_block_count = obj->Buffer.Length / sizeof(struct guid_info); 765 if ((ginfo = malloc(obj->Buffer.Length, M_ACPIWMI, M_NOWAIT)) 766 == NULL) { 767 AcpiOsFree(out.Pointer); 768 return (AE_NO_MEMORY); 769 } 770 memcpy(ginfo, obj->Buffer.Pointer, obj->Buffer.Length); 771 for (i = 0; i < wdg_block_count; ++i) { 772 if ((winfo = malloc(sizeof(struct wmi_info), M_ACPIWMI, 773 M_NOWAIT | M_ZERO)) == NULL) { 774 AcpiOsFree(out.Pointer); 775 free(ginfo, M_ACPIWMI); 776 return (AE_NO_MEMORY); 777 } 778 winfo->ginfo = ginfo[i]; 779 TAILQ_INSERT_TAIL(&sc->wmi_info_list, winfo, wmi_list); 780 } 781 AcpiOsFree(out.Pointer); 782 free(ginfo, M_ACPIWMI); 783 784 return (status); 785 } 786 787 /* 788 * Toggle event generation in for the given GUID (passed by winfo) 789 * Turn on to get notified (through acpi_wmi_notify_handler) if events happen 790 * on the given GUID. 791 */ 792 static ACPI_STATUS 793 acpi_wmi_toggle_we_event_generation(device_t dev, struct wmi_info *winfo, 794 enum event_generation_state state) 795 { 796 char method[5] = "WExx"; 797 ACPI_OBJECT_LIST input; 798 ACPI_OBJECT params[1]; 799 struct acpi_wmi_softc *sc; 800 ACPI_STATUS status; 801 802 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 803 804 sc = device_get_softc(dev); 805 ACPI_SERIAL_ASSERT(acpi_wmi); 806 params[0].Type = ACPI_TYPE_INTEGER; 807 params[0].Integer.Value = state==EVENT_GENERATION_ON?1:0; 808 input.Pointer = params; 809 input.Count = 1; 810 811 UINT8 hi = ((UINT8) winfo->ginfo.oid[0]) >> 4; 812 UINT8 lo = ((UINT8) winfo->ginfo.oid[0]) & 0xf; 813 method[2] = (hi > 9 ? hi + 55: hi + 48); 814 method[3] = (lo > 9 ? lo + 55: lo + 48); 815 status = AcpiEvaluateObject(sc->wmi_handle, method, &input, NULL); 816 if (status == AE_NOT_FOUND) status = AE_OK; 817 818 return (status); 819 } 820 821 /* 822 * Convert given two digit hex string (hexin) to an UINT8 referenced 823 * by byteout. 824 * Return != 0 if the was a problem (invalid input) 825 */ 826 static __inline int acpi_wmi_hex_to_int(const UINT8 *hexin, UINT8 *byteout) 827 { 828 unsigned int hi; 829 unsigned int lo; 830 831 hi = hexin[0]; 832 lo = hexin[1]; 833 if ('0' <= hi && hi <= '9') 834 hi -= '0'; 835 else if ('A' <= hi && hi <= 'F') 836 hi -= ('A' - 10); 837 else if ('a' <= hi && hi <= 'f') 838 hi -= ('a' - 10); 839 else 840 return (1); 841 if ('0' <= lo && lo <= '9') 842 lo -= '0'; 843 else if ('A' <= lo && lo <= 'F') 844 lo -= ('A' - 10); 845 else if ('a' <= lo && lo <= 'f') 846 lo -= ('a' - 10); 847 else 848 return (1); 849 *byteout = (hi << 4) + lo; 850 851 return (0); 852 } 853 854 /* 855 * Convert a human readable 36 character GUID into a 16byte 856 * machine readable one. 857 * The basic algorithm looks as follows: 858 * Input: AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 859 * Output: DCBAFEHGIJKLMNOP 860 * (AA BB CC etc. represent two digit hex numbers == bytes) 861 * Return != 0 if passed guid string is invalid 862 */ 863 static int 864 acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, UINT8 *guid) 865 { 866 static const int mapping[20] = {3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 867 8, 9, -1, 10, 11, 12, 13, 14, 15}; 868 int i; 869 870 for (i = 0; i < 20; ++i, ++guid_string) { 871 if (mapping[i] >= 0) { 872 if (acpi_wmi_hex_to_int(guid_string, 873 &guid[mapping[i]])) 874 return (-1); 875 ++guid_string; 876 } else if (*guid_string != '-') 877 return (-1); 878 } 879 880 return (0); 881 } 882 883 /* 884 * Lookup a wmi_info structure in wmi_list based on a 885 * human readable GUID 886 * Return NULL if the GUID is unknown in the _WDG 887 */ 888 static struct wmi_info* 889 acpi_wmi_lookup_wmi_info_by_guid_string(struct acpi_wmi_softc *sc, const char *guid_string) 890 { 891 char guid[16]; 892 struct wmi_info *winfo; 893 894 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 895 896 ACPI_SERIAL_ASSERT(acpi_wmi); 897 898 if (!acpi_wmi_guid_string_to_guid(guid_string, guid)) { 899 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 900 if (!memcmp(winfo->ginfo.guid, guid, 16)) { 901 return (winfo); 902 } 903 } 904 } 905 906 return (NULL); 907 } 908 909 /* 910 * open wmistat device 911 */ 912 static int 913 acpi_wmi_wmistat_open(struct cdev* dev, int flags, int mode, struct thread *td) 914 { 915 struct acpi_wmi_softc *sc; 916 int ret; 917 918 if (dev == NULL || dev->si_drv1 == NULL) 919 return (EBADF); 920 sc = dev->si_drv1; 921 922 ACPI_SERIAL_BEGIN(acpi_wmi); 923 if (sc->wmistat_open_pid != 0) { 924 ret = EBUSY; 925 } 926 else { 927 if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 928 == NULL) { 929 ret = ENXIO; 930 } else { 931 sc->wmistat_open_pid = td->td_proc->p_pid; 932 sc->wmistat_bufptr = 0; 933 ret = 0; 934 } 935 } 936 ACPI_SERIAL_END(acpi_wmi); 937 938 return (ret); 939 } 940 941 /* 942 * close wmistat device 943 */ 944 static int 945 acpi_wmi_wmistat_close(struct cdev* dev, int flags, int mode, 946 struct thread *td) 947 { 948 struct acpi_wmi_softc *sc; 949 int ret; 950 951 if (dev == NULL || dev->si_drv1 == NULL) 952 return (EBADF); 953 sc = dev->si_drv1; 954 955 ACPI_SERIAL_BEGIN(acpi_wmi); 956 if (sc->wmistat_open_pid == 0) { 957 ret = EBADF; 958 } 959 else { 960 if (sc->wmistat_bufptr != -1) { 961 sbuf_delete(&sc->wmistat_sbuf); 962 sc->wmistat_bufptr = -1; 963 } 964 sc->wmistat_open_pid = 0; 965 ret = 0; 966 } 967 ACPI_SERIAL_END(acpi_wmi); 968 969 return (ret); 970 } 971 972 /* 973 * Read from wmistat guid information 974 */ 975 static int 976 acpi_wmi_wmistat_read(struct cdev *dev, struct uio *buf, int flag) 977 { 978 struct acpi_wmi_softc *sc; 979 struct wmi_info *winfo; 980 int l; 981 int ret; 982 UINT8* guid; 983 984 if (dev == NULL || dev->si_drv1 == NULL) 985 return (EBADF); 986 sc = dev->si_drv1; 987 988 ACPI_SERIAL_BEGIN(acpi_wmi); 989 if (sc->wmistat_bufptr == -1) { 990 ret = EBADF; 991 } 992 else { 993 if (!sbuf_done(&sc->wmistat_sbuf)) { 994 sbuf_printf(&sc->wmistat_sbuf, "GUID " 995 " INST EXPE METH STR " 996 "EVENT OID\n"); 997 TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 998 guid = (UINT8*)winfo->ginfo.guid; 999 sbuf_printf(&sc->wmistat_sbuf, 1000 "{%02X%02X%02X%02X-%02X%02X-" 1001 "%02X%02X-%02X%02X-%02X%02X" 1002 "%02X%02X%02X%02X} %3d %-5s", 1003 guid[3], guid[2], guid[1], guid[0], 1004 guid[5], guid[4], 1005 guid[7], guid[6], 1006 guid[8], guid[9], 1007 guid[10], guid[11], guid[12], 1008 guid[13], guid[14], guid[15], 1009 winfo->ginfo.max_instance, 1010 (winfo->ginfo.flags& 1011 ACPI_WMI_REGFLAG_EXPENSIVE)? 1012 "YES":"NO" 1013 ); 1014 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD) 1015 sbuf_printf(&sc->wmistat_sbuf, 1016 "WM%c%c ", 1017 winfo->ginfo.oid[0], 1018 winfo->ginfo.oid[1]); 1019 else 1020 sbuf_printf(&sc->wmistat_sbuf, "NO "); 1021 sbuf_printf(&sc->wmistat_sbuf, "%-4s", 1022 (winfo->ginfo.flags& 1023 ACPI_WMI_REGFLAG_STRING)?"YES":"NO" 1024 ); 1025 if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT) 1026 sbuf_printf(&sc->wmistat_sbuf, 1027 "0x%02X%s -\n", 1028 (UINT8)winfo->ginfo.oid[0], 1029 winfo->event_handler==NULL? 1030 " ":"+"); 1031 else 1032 sbuf_printf(&sc->wmistat_sbuf, 1033 "NO %c%c\n", 1034 winfo->ginfo.oid[0], 1035 winfo->ginfo.oid[1]); 1036 } 1037 sbuf_finish(&sc->wmistat_sbuf); 1038 } 1039 if (sbuf_len(&sc->wmistat_sbuf) <= 0) { 1040 sbuf_delete(&sc->wmistat_sbuf); 1041 sc->wmistat_bufptr = -1; 1042 sc->wmistat_open_pid = 0; 1043 ret = ENOMEM; 1044 } else { 1045 l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) - 1046 sc->wmistat_bufptr); 1047 ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) + 1048 sc->wmistat_bufptr, l, buf) : 0; 1049 sc->wmistat_bufptr += l; 1050 } 1051 } 1052 ACPI_SERIAL_END(acpi_wmi); 1053 1054 return (ret); 1055 } 1056