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