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