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