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