1194701Srpaulo/*- 2194701Srpaulo * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de> 3194701Srpaulo * All rights reserved. 4194701Srpaulo * 5194701Srpaulo * Redistribution and use in source and binary forms, with or without 6194701Srpaulo * modification, are permitted provided that the following conditions 7194701Srpaulo * are met: 8194701Srpaulo * 1. Redistributions of source code must retain the above copyright 9194701Srpaulo * notice, this list of conditions and the following disclaimer. 10194701Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11194701Srpaulo * notice, this list of conditions and the following disclaimer in the 12194701Srpaulo * documentation and/or other materials provided with the distribution. 13194701Srpaulo * 14194701Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15194701Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16194701Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17194701Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18194701Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19194701Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20194701Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21194701Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22194701Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23194701Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24194701Srpaulo * SUCH DAMAGE. 25194701Srpaulo */ 26194701Srpaulo 27194701Srpaulo#include <sys/cdefs.h> 28194701Srpaulo__FBSDID("$FreeBSD: releng/10.3/sys/dev/acpi_support/acpi_wmi.c 241537 2012-10-14 09:31:11Z avg $"); 29194701Srpaulo 30194701Srpaulo/* 31194701Srpaulo * Driver for acpi-wmi mapping, provides an interface for vendor specific 32194701Srpaulo * implementations (e.g. HP and Acer laptops). 33194701Srpaulo * Inspired by the ACPI-WMI mapping driver (c) 2008-2008 Carlos Corbacho which 34194701Srpaulo * implements this functionality for Linux. 35194701Srpaulo * 36194701Srpaulo * WMI and ACPI: http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx 37194701Srpaulo * acpi-wmi for Linux: http://www.kernel.org 38194701Srpaulo */ 39194701Srpaulo 40194701Srpaulo#include "opt_acpi.h" 41194701Srpaulo#include <sys/param.h> 42194701Srpaulo#include <sys/conf.h> 43194701Srpaulo#include <sys/uio.h> 44194701Srpaulo#include <sys/proc.h> 45194701Srpaulo#include <sys/kernel.h> 46194701Srpaulo#include <sys/malloc.h> 47194701Srpaulo#include <sys/sbuf.h> 48194701Srpaulo#include <sys/module.h> 49194701Srpaulo#include <sys/bus.h> 50194701Srpaulo 51194701Srpaulo#include <contrib/dev/acpica/include/acpi.h> 52194701Srpaulo#include <contrib/dev/acpica/include/accommon.h> 53194701Srpaulo#include <dev/acpica/acpivar.h> 54194701Srpaulo#include "acpi_wmi_if.h" 55194701Srpaulo 56227293Sedstatic MALLOC_DEFINE(M_ACPIWMI, "acpiwmi", "ACPI-WMI mapping"); 57194701Srpaulo 58194701Srpaulo#define _COMPONENT ACPI_OEM 59194701SrpauloACPI_MODULE_NAME("ACPI_WMI"); 60194701Srpaulo 61194701Srpaulo#define ACPI_WMI_REGFLAG_EXPENSIVE 0x1 /* GUID flag: Expensive operation */ 62194701Srpaulo#define ACPI_WMI_REGFLAG_METHOD 0x2 /* GUID flag: Method call */ 63194701Srpaulo#define ACPI_WMI_REGFLAG_STRING 0x4 /* GUID flag: String */ 64194701Srpaulo#define ACPI_WMI_REGFLAG_EVENT 0x8 /* GUID flag: Event */ 65194701Srpaulo 66194701Srpaulo/* 67194701Srpaulo * acpi_wmi driver private structure 68194701Srpaulo */ 69194701Srpaulostruct acpi_wmi_softc { 70194701Srpaulo device_t wmi_dev; /* wmi device id */ 71194701Srpaulo ACPI_HANDLE wmi_handle; /* handle of the PNP0C14 node */ 72194701Srpaulo device_t ec_dev; /* acpi_ec0 */ 73194701Srpaulo struct cdev *wmistat_dev_t; /* wmistat device handle */ 74194701Srpaulo struct sbuf wmistat_sbuf; /* sbuf for /dev/wmistat output */ 75194701Srpaulo pid_t wmistat_open_pid; /* pid operating on /dev/wmistat */ 76194701Srpaulo int wmistat_bufptr; /* /dev/wmistat ptr to buffer position */ 77241537Savg TAILQ_HEAD(wmi_info_list_head, wmi_info) wmi_info_list; 78194701Srpaulo}; 79194701Srpaulo 80194701Srpaulo/* 81194701Srpaulo * Struct that holds information about 82194701Srpaulo * about a single GUID entry in _WDG 83194701Srpaulo */ 84194701Srpaulostruct guid_info { 85194701Srpaulo char guid[16]; /* 16 byte non human readable GUID */ 86194701Srpaulo char oid[2]; /* object id or event notify id (first byte) */ 87194701Srpaulo UINT8 max_instance; /* highest instance known for this GUID */ 88194701Srpaulo UINT8 flags; /* ACPI_WMI_REGFLAG_%s */ 89194701Srpaulo}; 90194701Srpaulo 91194701Srpaulo/* WExx event generation state (on/off) */ 92194701Srpauloenum event_generation_state { 93194701Srpaulo EVENT_GENERATION_ON = 1, 94194701Srpaulo EVENT_GENERATION_OFF = 0 95194701Srpaulo}; 96194701Srpaulo 97194701Srpaulo 98194701Srpaulo/* 99194701Srpaulo * Information about one entry in _WDG. 100194701Srpaulo * List of those is used to lookup information by GUID. 101194701Srpaulo */ 102194701Srpaulostruct wmi_info { 103194701Srpaulo TAILQ_ENTRY(wmi_info) wmi_list; 104194701Srpaulo struct guid_info ginfo; /* information on guid */ 105194701Srpaulo ACPI_NOTIFY_HANDLER event_handler;/* client provided event handler */ 106194701Srpaulo void *event_handler_user_data; /* ev handler cookie */ 107194701Srpaulo}; 108194701Srpaulo 109194701Srpaulo 110194701SrpauloACPI_SERIAL_DECL(acpi_wmi, "ACPI-WMI Mapping"); 111194701Srpaulo 112194701Srpaulo/* public interface - declaration */ 113194701Srpaulo/* standard device interface*/ 114194701Srpaulostatic int acpi_wmi_probe(device_t dev); 115194701Srpaulostatic int acpi_wmi_attach(device_t dev); 116194701Srpaulostatic int acpi_wmi_detach(device_t dev); 117194701Srpaulo/* see acpi_wmi_if.m */ 118194701Srpaulostatic int acpi_wmi_provides_guid_string_method(device_t dev, 119194701Srpaulo const char *guid_string); 120194701Srpaulostatic ACPI_STATUS acpi_wmi_evaluate_call_method(device_t dev, 121194701Srpaulo const char *guid_string, UINT8 instance, 122194701Srpaulo UINT32 method_id, const ACPI_BUFFER *in, 123194701Srpaulo ACPI_BUFFER *out); 124194701Srpaulostatic ACPI_STATUS acpi_wmi_install_event_handler_method(device_t dev, 125194701Srpaulo const char *guid_string, ACPI_NOTIFY_HANDLER handler, 126194701Srpaulo void *data); 127194701Srpaulostatic ACPI_STATUS acpi_wmi_remove_event_handler_method(device_t dev, 128194701Srpaulo const char *guid_string); 129194701Srpaulostatic ACPI_STATUS acpi_wmi_get_event_data_method(device_t dev, 130194701Srpaulo UINT32 event_id, ACPI_BUFFER *out); 131194701Srpaulostatic ACPI_STATUS acpi_wmi_get_block_method(device_t dev, 132194701Srpaulo const char *guid_string, 133194701Srpaulo UINT8 instance, ACPI_BUFFER *out); 134194701Srpaulostatic ACPI_STATUS acpi_wmi_set_block_method(device_t dev, 135194701Srpaulo const char *guid_string, 136194701Srpaulo UINT8 instance, const ACPI_BUFFER *in); 137194701Srpaulo/* private interface - declaration */ 138194701Srpaulo/* callbacks */ 139194701Srpaulostatic void acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, 140194701Srpaulo void *context); 141194701Srpaulostatic ACPI_STATUS acpi_wmi_ec_handler(UINT32 function, 142194701Srpaulo ACPI_PHYSICAL_ADDRESS address, UINT32 width, 143202771Sjkim UINT64 *value, void *context, 144194701Srpaulo void *region_context); 145194701Srpaulo/* helpers */ 146241537Savgstatic ACPI_STATUS acpi_wmi_read_wdg_blocks(struct acpi_wmi_softc *sc, ACPI_HANDLE h); 147194701Srpaulostatic ACPI_STATUS acpi_wmi_toggle_we_event_generation(device_t dev, 148194701Srpaulo struct wmi_info *winfo, 149194701Srpaulo enum event_generation_state state); 150194701Srpaulostatic int acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, 151194701Srpaulo UINT8 *guid); 152241537Savgstatic struct wmi_info* acpi_wmi_lookup_wmi_info_by_guid_string(struct acpi_wmi_softc *sc, 153194701Srpaulo const char *guid_string); 154194701Srpaulo 155194701Srpaulostatic d_open_t acpi_wmi_wmistat_open; 156194701Srpaulostatic d_close_t acpi_wmi_wmistat_close; 157194701Srpaulostatic d_read_t acpi_wmi_wmistat_read; 158194701Srpaulo 159194701Srpaulo/* handler /dev/wmistat device */ 160194701Srpaulostatic struct cdevsw wmistat_cdevsw = { 161194701Srpaulo .d_version = D_VERSION, 162194701Srpaulo .d_open = acpi_wmi_wmistat_open, 163194701Srpaulo .d_close = acpi_wmi_wmistat_close, 164194701Srpaulo .d_read = acpi_wmi_wmistat_read, 165194701Srpaulo .d_name = "wmistat", 166194701Srpaulo}; 167194701Srpaulo 168194701Srpaulo 169194701Srpaulostatic device_method_t acpi_wmi_methods[] = { 170194701Srpaulo /* Device interface */ 171194701Srpaulo DEVMETHOD(device_probe, acpi_wmi_probe), 172194701Srpaulo DEVMETHOD(device_attach, acpi_wmi_attach), 173194701Srpaulo DEVMETHOD(device_detach, acpi_wmi_detach), 174194701Srpaulo 175212457Savg /* bus interface */ 176212457Savg DEVMETHOD(bus_add_child, bus_generic_add_child), 177212457Savg 178194701Srpaulo /* acpi_wmi interface */ 179194701Srpaulo DEVMETHOD(acpi_wmi_provides_guid_string, 180194701Srpaulo acpi_wmi_provides_guid_string_method), 181194701Srpaulo DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method), 182194701Srpaulo DEVMETHOD(acpi_wmi_install_event_handler, 183194701Srpaulo acpi_wmi_install_event_handler_method), 184194701Srpaulo DEVMETHOD(acpi_wmi_remove_event_handler, 185194701Srpaulo acpi_wmi_remove_event_handler_method), 186194701Srpaulo DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method), 187194701Srpaulo DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method), 188194701Srpaulo DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method), 189194701Srpaulo 190227843Smarius DEVMETHOD_END 191194701Srpaulo}; 192194701Srpaulo 193194701Srpaulostatic driver_t acpi_wmi_driver = { 194194701Srpaulo "acpi_wmi", 195194701Srpaulo acpi_wmi_methods, 196194701Srpaulo sizeof(struct acpi_wmi_softc), 197194701Srpaulo}; 198194701Srpaulo 199194701Srpaulostatic devclass_t acpi_wmi_devclass; 200194701SrpauloDRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, 0, 0); 201194701SrpauloMODULE_VERSION(acpi_wmi, 1); 202194701SrpauloMODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 203212762Sjkimstatic char *wmi_ids[] = {"PNP0C14", NULL}; 204194701Srpaulo 205194701Srpaulo/* 206194701Srpaulo * Probe for the PNP0C14 ACPI node 207194701Srpaulo */ 208194701Srpaulostatic int 209194701Srpauloacpi_wmi_probe(device_t dev) 210194701Srpaulo{ 211194701Srpaulo if (acpi_disabled("wmi") || 212194701Srpaulo ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids) == NULL) 213194701Srpaulo return (ENXIO); 214194701Srpaulo device_set_desc(dev, "ACPI-WMI mapping"); 215194701Srpaulo 216194701Srpaulo return (0); 217194701Srpaulo} 218194701Srpaulo 219194701Srpaulo/* 220194701Srpaulo * Attach the device by: 221194701Srpaulo * - Looking for the first ACPI EC device 222194701Srpaulo * - Install the notify handler 223194701Srpaulo * - Install the EC address space handler 224194701Srpaulo * - Look for the _WDG node and read GUID information blocks 225194701Srpaulo */ 226194701Srpaulostatic int 227194701Srpauloacpi_wmi_attach(device_t dev) 228194701Srpaulo{ 229194701Srpaulo struct acpi_wmi_softc *sc; 230194701Srpaulo int ret; 231194701Srpaulo ACPI_STATUS status; 232194701Srpaulo 233194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 234194701Srpaulo sc = device_get_softc(dev); 235194701Srpaulo ret = ENXIO; 236194701Srpaulo 237194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 238194701Srpaulo sc->wmi_dev = dev; 239194701Srpaulo sc->wmi_handle = acpi_get_handle(dev); 240241537Savg TAILQ_INIT(&sc->wmi_info_list); 241194701Srpaulo /* XXX Only works with one EC, but nearly all systems only have one. */ 242194701Srpaulo if ((sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0)) 243194701Srpaulo == NULL) 244194701Srpaulo device_printf(dev, "cannot find EC device\n"); 245194701Srpaulo else if (ACPI_FAILURE((status = AcpiInstallNotifyHandler(sc->wmi_handle, 246194701Srpaulo ACPI_DEVICE_NOTIFY, acpi_wmi_notify_handler, sc)))) 247194701Srpaulo device_printf(sc->wmi_dev, "couldn't install notify handler - %s\n", 248194701Srpaulo AcpiFormatException(status)); 249194701Srpaulo else if (ACPI_FAILURE((status = AcpiInstallAddressSpaceHandler( 250194701Srpaulo sc->wmi_handle, ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler, 251194701Srpaulo NULL, sc)))) { 252194701Srpaulo device_printf(sc->wmi_dev, "couldn't install EC handler - %s\n", 253194701Srpaulo AcpiFormatException(status)); 254194701Srpaulo AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 255194701Srpaulo acpi_wmi_notify_handler); 256241537Savg } else if (ACPI_FAILURE((status = acpi_wmi_read_wdg_blocks(sc, 257194701Srpaulo sc->wmi_handle)))) { 258194701Srpaulo device_printf(sc->wmi_dev, "couldn't parse _WDG - %s\n", 259194701Srpaulo AcpiFormatException(status)); 260194701Srpaulo AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 261194701Srpaulo acpi_wmi_notify_handler); 262194701Srpaulo AcpiRemoveAddressSpaceHandler(sc->wmi_handle, ACPI_ADR_SPACE_EC, 263194701Srpaulo acpi_wmi_ec_handler); 264194701Srpaulo } else { 265194701Srpaulo sc->wmistat_dev_t = make_dev(&wmistat_cdevsw, 0, UID_ROOT, 266227823Sjh GID_WHEEL, 0644, "wmistat%d", device_get_unit(dev)); 267194701Srpaulo sc->wmistat_dev_t->si_drv1 = sc; 268194701Srpaulo sc->wmistat_open_pid = 0; 269194701Srpaulo sc->wmistat_bufptr = -1; 270194701Srpaulo ret = 0; 271194701Srpaulo } 272194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 273194701Srpaulo 274212457Savg if (ret == 0) { 275212457Savg bus_generic_probe(dev); 276212457Savg ret = bus_generic_attach(dev); 277212457Savg } 278212457Savg 279194701Srpaulo return (ret); 280194701Srpaulo} 281194701Srpaulo 282194701Srpaulo/* 283194701Srpaulo * Detach the driver by: 284194701Srpaulo * - Removing notification handler 285194701Srpaulo * - Removing address space handler 286194701Srpaulo * - Turning off event generation for all WExx event activated by 287194701Srpaulo * child drivers 288194701Srpaulo */ 289194701Srpaulostatic int 290194701Srpauloacpi_wmi_detach(device_t dev) 291194701Srpaulo{ 292194701Srpaulo struct wmi_info *winfo, *tmp; 293194701Srpaulo struct acpi_wmi_softc *sc; 294194701Srpaulo int ret; 295194701Srpaulo 296194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 297194701Srpaulo sc = device_get_softc(dev); 298194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 299194701Srpaulo 300194701Srpaulo if (sc->wmistat_open_pid != 0) { 301194701Srpaulo ret = EBUSY; 302194701Srpaulo } else { 303194701Srpaulo AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 304194701Srpaulo acpi_wmi_notify_handler); 305194701Srpaulo AcpiRemoveAddressSpaceHandler(sc->wmi_handle, 306194701Srpaulo ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler); 307241537Savg TAILQ_FOREACH_SAFE(winfo, &sc->wmi_info_list, wmi_list, tmp) { 308194701Srpaulo if (winfo->event_handler) 309194701Srpaulo acpi_wmi_toggle_we_event_generation(dev, 310194701Srpaulo winfo, EVENT_GENERATION_OFF); 311241537Savg TAILQ_REMOVE(&sc->wmi_info_list, winfo, wmi_list); 312194701Srpaulo free(winfo, M_ACPIWMI); 313194701Srpaulo } 314194701Srpaulo if (sc->wmistat_bufptr != -1) { 315194701Srpaulo sbuf_delete(&sc->wmistat_sbuf); 316194701Srpaulo sc->wmistat_bufptr = -1; 317194701Srpaulo } 318194701Srpaulo sc->wmistat_open_pid = 0; 319194701Srpaulo destroy_dev(sc->wmistat_dev_t); 320194701Srpaulo ret = 0; 321194701Srpaulo } 322194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 323194701Srpaulo 324194701Srpaulo return (ret); 325194701Srpaulo} 326194701Srpaulo 327194701Srpaulo 328194701Srpaulo/* 329194701Srpaulo * Check if the given GUID string (human readable format 330194701Srpaulo * AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP) 331194701Srpaulo * exists within _WDG 332194701Srpaulo */ 333194701Srpaulostatic int 334194701Srpauloacpi_wmi_provides_guid_string_method(device_t dev, const char *guid_string) 335194701Srpaulo{ 336241537Savg struct acpi_wmi_softc *sc; 337195185Srpaulo struct wmi_info *winfo; 338194701Srpaulo int ret; 339194701Srpaulo 340241537Savg sc = device_get_softc(dev); 341194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 342194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 343241537Savg winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string); 344195185Srpaulo ret = (winfo == NULL)?0:winfo->ginfo.max_instance+1; 345194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 346194701Srpaulo 347194701Srpaulo return (ret); 348194701Srpaulo} 349194701Srpaulo 350194701Srpaulo/* 351194701Srpaulo * Call a method "method_id" on the given GUID block 352194701Srpaulo * write result into user provided output buffer 353194701Srpaulo */ 354194701Srpaulostatic ACPI_STATUS 355194701Srpauloacpi_wmi_evaluate_call_method(device_t dev, const char *guid_string, 356194701Srpaulo UINT8 instance, UINT32 method_id, const ACPI_BUFFER *in, ACPI_BUFFER *out) 357194701Srpaulo{ 358194701Srpaulo ACPI_OBJECT params[3]; 359194701Srpaulo ACPI_OBJECT_LIST input; 360194701Srpaulo char method[5] = "WMxx"; 361194701Srpaulo struct wmi_info *winfo; 362194701Srpaulo struct acpi_wmi_softc *sc; 363194701Srpaulo ACPI_STATUS status; 364194701Srpaulo 365194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 366194701Srpaulo 367194701Srpaulo sc = device_get_softc(dev); 368194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 369241537Savg if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 370194701Srpaulo == NULL) 371194701Srpaulo status = AE_NOT_FOUND; 372194701Srpaulo else if (!(winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 373194701Srpaulo status = AE_BAD_DATA; 374194701Srpaulo else if (instance > winfo->ginfo.max_instance) 375194701Srpaulo status = AE_BAD_PARAMETER; 376194701Srpaulo else { 377194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 378194701Srpaulo params[0].Integer.Value = instance; 379194701Srpaulo params[1].Type = ACPI_TYPE_INTEGER; 380194701Srpaulo params[1].Integer.Value = method_id; 381194701Srpaulo input.Pointer = params; 382194701Srpaulo input.Count = 2; 383194701Srpaulo if (in) { 384194701Srpaulo params[2].Type = 385194701Srpaulo (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 386194701Srpaulo ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 387194701Srpaulo params[2].Buffer.Length = in->Length; 388194701Srpaulo params[2].Buffer.Pointer = in->Pointer; 389194701Srpaulo input.Count = 3; 390194701Srpaulo } 391194701Srpaulo method[2] = winfo->ginfo.oid[0]; 392194701Srpaulo method[3] = winfo->ginfo.oid[1]; 393194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, method, 394194701Srpaulo &input, out); 395194701Srpaulo } 396194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 397194701Srpaulo 398194701Srpaulo return (status); 399194701Srpaulo} 400194701Srpaulo 401194701Srpaulo/* 402194701Srpaulo * Install a user provided event_handler on the given GUID 403194701Srpaulo * provided *data will be passed on callback 404194701Srpaulo * If there is already an existing event handler registered it will be silently 405194701Srpaulo * discarded 406194701Srpaulo */ 407194701Srpaulostatic ACPI_STATUS 408194701Srpauloacpi_wmi_install_event_handler_method(device_t dev, const char *guid_string, 409194701Srpaulo ACPI_NOTIFY_HANDLER event_handler, void *data) 410194701Srpaulo{ 411241537Savg struct acpi_wmi_softc *sc = device_get_softc(dev); 412194701Srpaulo struct wmi_info *winfo; 413194701Srpaulo ACPI_STATUS status; 414194701Srpaulo 415194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 416194701Srpaulo 417194701Srpaulo status = AE_OK; 418194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 419194701Srpaulo if (guid_string == NULL || event_handler == NULL) 420194701Srpaulo status = AE_BAD_PARAMETER; 421241537Savg else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 422194701Srpaulo == NULL) 423194701Srpaulo status = AE_NOT_EXIST; 424194701Srpaulo else if (winfo->event_handler != NULL || 425194701Srpaulo (status = acpi_wmi_toggle_we_event_generation(dev, winfo, 426194701Srpaulo EVENT_GENERATION_ON)) == AE_OK) { 427194701Srpaulo winfo->event_handler = event_handler; 428194701Srpaulo winfo->event_handler_user_data = data; 429194701Srpaulo } 430194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 431194701Srpaulo 432194701Srpaulo return (status); 433194701Srpaulo} 434194701Srpaulo 435194701Srpaulo/* 436194701Srpaulo * Remove a previously installed event handler from the given GUID 437194701Srpaulo * If there was none installed, this call is silently discarded and 438194701Srpaulo * reported as AE_OK 439194701Srpaulo */ 440194701Srpaulostatic ACPI_STATUS 441194701Srpauloacpi_wmi_remove_event_handler_method(device_t dev, const char *guid_string) 442194701Srpaulo{ 443241537Savg struct acpi_wmi_softc *sc = device_get_softc(dev); 444194701Srpaulo struct wmi_info *winfo; 445194701Srpaulo ACPI_STATUS status; 446194701Srpaulo 447194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 448194701Srpaulo 449194701Srpaulo status = AE_OK; 450194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 451194701Srpaulo if (guid_string && 452241537Savg (winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 453194701Srpaulo != NULL && winfo->event_handler) { 454194701Srpaulo status = acpi_wmi_toggle_we_event_generation(dev, winfo, 455194701Srpaulo EVENT_GENERATION_OFF); 456194701Srpaulo winfo->event_handler = NULL; 457194701Srpaulo winfo->event_handler_user_data = NULL; 458194701Srpaulo } 459194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 460194701Srpaulo 461194701Srpaulo return (status); 462194701Srpaulo} 463194701Srpaulo 464194701Srpaulo/* 465194701Srpaulo * Get details on an event received through a callback registered 466194701Srpaulo * through ACPI_WMI_REMOVE_EVENT_HANDLER into a user provided output buffer. 467194701Srpaulo * (event_id equals "notify" passed in the callback) 468194701Srpaulo */ 469194701Srpaulostatic ACPI_STATUS 470194701Srpauloacpi_wmi_get_event_data_method(device_t dev, UINT32 event_id, ACPI_BUFFER *out) 471194701Srpaulo{ 472194701Srpaulo ACPI_OBJECT_LIST input; 473194701Srpaulo ACPI_OBJECT params[1]; 474194701Srpaulo struct acpi_wmi_softc *sc; 475194701Srpaulo struct wmi_info *winfo; 476194701Srpaulo ACPI_STATUS status; 477194701Srpaulo 478194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 479194701Srpaulo 480194701Srpaulo sc = device_get_softc(dev); 481194701Srpaulo status = AE_NOT_FOUND; 482194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 483194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 484194701Srpaulo params[0].Integer.Value = event_id; 485194701Srpaulo input.Pointer = params; 486194701Srpaulo input.Count = 1; 487241537Savg TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 488194701Srpaulo if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 489194701Srpaulo ((UINT8) winfo->ginfo.oid[0] == event_id)) { 490194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, "_WED", 491194701Srpaulo &input, out); 492194701Srpaulo break; 493194701Srpaulo } 494194701Srpaulo } 495194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 496194701Srpaulo 497194701Srpaulo return (status); 498194701Srpaulo} 499194701Srpaulo 500194701Srpaulo/* 501194701Srpaulo * Read a block of data from the given GUID (using WQxx (query)) 502194701Srpaulo * Will be returned in a user provided buffer (out). 503194701Srpaulo * If the method is marked as expensive (ACPI_WMI_REGFLAG_EXPENSIVE) 504194701Srpaulo * we will first call the WCxx control method to lock the node to 505194701Srpaulo * lock the node for data collection and release it afterwards. 506194701Srpaulo * (Failed WCxx calls are ignored to "support" broken implementations) 507194701Srpaulo */ 508194701Srpaulostatic ACPI_STATUS 509194701Srpauloacpi_wmi_get_block_method(device_t dev, const char *guid_string, UINT8 instance, 510194701Srpaulo ACPI_BUFFER *out) 511194701Srpaulo{ 512194701Srpaulo char wc_method[5] = "WCxx"; 513194701Srpaulo char wq_method[5] = "WQxx"; 514194701Srpaulo ACPI_OBJECT_LIST wc_input; 515194701Srpaulo ACPI_OBJECT_LIST wq_input; 516194701Srpaulo ACPI_OBJECT wc_params[1]; 517194701Srpaulo ACPI_OBJECT wq_params[1]; 518194701Srpaulo ACPI_HANDLE wc_handle; 519194701Srpaulo struct acpi_wmi_softc *sc; 520194701Srpaulo struct wmi_info *winfo; 521194701Srpaulo ACPI_STATUS status; 522194701Srpaulo ACPI_STATUS wc_status; 523194701Srpaulo 524194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 525194701Srpaulo 526194701Srpaulo sc = device_get_softc(dev); 527194701Srpaulo wc_status = AE_ERROR; 528194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 529194701Srpaulo if (guid_string == NULL || out == NULL) 530194701Srpaulo status = AE_BAD_PARAMETER; 531241537Savg else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 532194701Srpaulo == NULL) 533194701Srpaulo status = AE_ERROR; 534194701Srpaulo else if (instance > winfo->ginfo.max_instance) 535194701Srpaulo status = AE_BAD_PARAMETER; 536194701Srpaulo else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 537194701Srpaulo (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 538194701Srpaulo status = AE_ERROR; 539194701Srpaulo else { 540194701Srpaulo wq_params[0].Type = ACPI_TYPE_INTEGER; 541194701Srpaulo wq_params[0].Integer.Value = instance; 542194701Srpaulo wq_input.Pointer = wq_params; 543194701Srpaulo wq_input.Count = 1; 544194701Srpaulo if (winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) { 545194701Srpaulo wc_params[0].Type = ACPI_TYPE_INTEGER; 546194701Srpaulo wc_params[0].Integer.Value = 1; 547194701Srpaulo wc_input.Pointer = wc_params; 548194701Srpaulo wc_input.Count = 1; 549194701Srpaulo wc_method[2] = winfo->ginfo.oid[0]; 550194701Srpaulo wc_method[3] = winfo->ginfo.oid[1]; 551194701Srpaulo wc_status = AcpiGetHandle(sc->wmi_handle, wc_method, 552194701Srpaulo &wc_handle); 553194701Srpaulo if (ACPI_SUCCESS(wc_status)) 554194701Srpaulo wc_status = AcpiEvaluateObject(wc_handle, 555194701Srpaulo wc_method, &wc_input, NULL); 556194701Srpaulo } 557194701Srpaulo wq_method[2] = winfo->ginfo.oid[0]; 558194701Srpaulo wq_method[3] = winfo->ginfo.oid[1]; 559194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, wq_method, 560194701Srpaulo &wq_input, out); 561194701Srpaulo if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) 562194701Srpaulo && ACPI_SUCCESS(wc_status)) { 563194701Srpaulo wc_params[0].Integer.Value = 0; 564194701Srpaulo status = AcpiEvaluateObject(wc_handle, wc_method, 565194701Srpaulo &wc_input, NULL); /* XXX this might be 566194701Srpaulo the wrong status to 567194701Srpaulo return? */ 568194701Srpaulo } 569194701Srpaulo } 570194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 571194701Srpaulo 572194701Srpaulo return (status); 573194701Srpaulo} 574194701Srpaulo 575194701Srpaulo/* 576194701Srpaulo * Write a block of data to the given GUID (using WSxx) 577194701Srpaulo */ 578194701Srpaulostatic ACPI_STATUS 579194701Srpauloacpi_wmi_set_block_method(device_t dev, const char *guid_string, UINT8 instance, 580194701Srpaulo const ACPI_BUFFER *in) 581194701Srpaulo{ 582194701Srpaulo char method[5] = "WSxx"; 583194701Srpaulo ACPI_OBJECT_LIST input; 584194701Srpaulo ACPI_OBJECT params[2]; 585194701Srpaulo struct wmi_info *winfo; 586194701Srpaulo struct acpi_wmi_softc *sc; 587194701Srpaulo ACPI_STATUS status; 588194701Srpaulo 589194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 590194701Srpaulo 591194701Srpaulo sc = device_get_softc(dev); 592194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 593194701Srpaulo if (guid_string == NULL || in == NULL) 594194701Srpaulo status = AE_BAD_DATA; 595241537Savg else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(sc, guid_string)) 596194701Srpaulo == NULL) 597194701Srpaulo status = AE_ERROR; 598194701Srpaulo else if (instance > winfo->ginfo.max_instance) 599194701Srpaulo status = AE_BAD_PARAMETER; 600194701Srpaulo else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 601194701Srpaulo (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 602194701Srpaulo status = AE_ERROR; 603194701Srpaulo else { 604194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 605194701Srpaulo params[0].Integer.Value = instance; 606194701Srpaulo input.Pointer = params; 607194701Srpaulo input.Count = 2; 608194701Srpaulo params[1].Type = (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 609194701Srpaulo ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 610194701Srpaulo params[1].Buffer.Length = in->Length; 611194701Srpaulo params[1].Buffer.Pointer = in->Pointer; 612194701Srpaulo method[2] = winfo->ginfo.oid[0]; 613194701Srpaulo method[3] = winfo->ginfo.oid[1]; 614194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, method, 615194701Srpaulo &input, NULL); 616194701Srpaulo } 617194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 618194701Srpaulo 619194701Srpaulo return (status); 620194701Srpaulo} 621194701Srpaulo 622194701Srpaulo/* 623194701Srpaulo * Handle events received and dispatch them to 624194701Srpaulo * stakeholders that registered through ACPI_WMI_INSTALL_EVENT_HANDLER 625194701Srpaulo */ 626194701Srpaulostatic void 627194701Srpauloacpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 628194701Srpaulo{ 629241537Savg struct acpi_wmi_softc *sc = context; 630194701Srpaulo ACPI_NOTIFY_HANDLER handler; 631194701Srpaulo void *handler_data; 632194701Srpaulo struct wmi_info *winfo; 633194701Srpaulo 634194701Srpaulo ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 635194701Srpaulo 636194701Srpaulo handler = NULL; 637194701Srpaulo handler_data = NULL; 638194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 639241537Savg TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 640194701Srpaulo if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 641194701Srpaulo ((UINT8) winfo->ginfo.oid[0] == notify)) { 642194701Srpaulo if (winfo->event_handler) { 643194701Srpaulo handler = winfo->event_handler; 644194701Srpaulo handler_data = winfo->event_handler_user_data; 645194701Srpaulo break; 646194701Srpaulo } 647194701Srpaulo } 648194701Srpaulo } 649194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 650194701Srpaulo if (handler) { 651194701Srpaulo handler(h, notify, handler_data); 652194701Srpaulo } 653194701Srpaulo} 654194701Srpaulo 655194701Srpaulo/* 656194701Srpaulo * Handle EC address space notifications reveived on the WDG node 657194701Srpaulo * (this mimics EcAddressSpaceHandler in acpi_ec.c) 658194701Srpaulo */ 659194701Srpaulostatic ACPI_STATUS 660194701Srpauloacpi_wmi_ec_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 661202771Sjkim UINT32 width, UINT64 *value, void *context, 662194701Srpaulo void *region_context) 663194701Srpaulo{ 664194701Srpaulo struct acpi_wmi_softc *sc; 665194701Srpaulo int i; 666202771Sjkim UINT64 ec_data; 667194701Srpaulo UINT8 ec_addr; 668194701Srpaulo ACPI_STATUS status; 669194701Srpaulo 670194716Srpaulo ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)address); 671194701Srpaulo 672194701Srpaulo sc = (struct acpi_wmi_softc *)context; 673194701Srpaulo if (width % 8 != 0 || value == NULL || context == NULL) 674194701Srpaulo return (AE_BAD_PARAMETER); 675194701Srpaulo if (address + (width / 8) - 1 > 0xFF) 676194701Srpaulo return (AE_BAD_ADDRESS); 677194701Srpaulo if (function == ACPI_READ) 678194701Srpaulo *value = 0; 679194701Srpaulo ec_addr = address; 680194701Srpaulo status = AE_ERROR; 681194701Srpaulo 682194701Srpaulo for (i = 0; i < width; i += 8, ++ec_addr) { 683194701Srpaulo switch (function) { 684194701Srpaulo case ACPI_READ: 685194701Srpaulo status = ACPI_EC_READ(sc->ec_dev, ec_addr, &ec_data, 1); 686194701Srpaulo if (ACPI_SUCCESS(status)) 687202771Sjkim *value |= ((UINT64)ec_data) << i; 688194701Srpaulo break; 689194701Srpaulo case ACPI_WRITE: 690194701Srpaulo ec_data = (UINT8)((*value) >> i); 691194701Srpaulo status = ACPI_EC_WRITE(sc->ec_dev, ec_addr, ec_data, 1); 692194701Srpaulo break; 693194701Srpaulo default: 694194701Srpaulo device_printf(sc->wmi_dev, 695194701Srpaulo "invalid acpi_wmi_ec_handler function %d\n", 696194701Srpaulo function); 697194701Srpaulo status = AE_BAD_PARAMETER; 698194701Srpaulo break; 699194701Srpaulo } 700194701Srpaulo if (ACPI_FAILURE(status)) 701194701Srpaulo break; 702194701Srpaulo } 703194701Srpaulo 704194701Srpaulo return (status); 705194701Srpaulo} 706194701Srpaulo 707194701Srpaulo/* 708194701Srpaulo * Read GUID blocks from the _WDG node 709194701Srpaulo * into wmi_info_list. 710194701Srpaulo */ 711194701Srpaulostatic ACPI_STATUS 712241537Savgacpi_wmi_read_wdg_blocks(struct acpi_wmi_softc *sc, ACPI_HANDLE h) 713194701Srpaulo{ 714194701Srpaulo ACPI_BUFFER out = {ACPI_ALLOCATE_BUFFER, NULL}; 715194701Srpaulo struct guid_info *ginfo; 716194701Srpaulo ACPI_OBJECT *obj; 717194701Srpaulo struct wmi_info *winfo; 718194701Srpaulo UINT32 i; 719194701Srpaulo UINT32 wdg_block_count; 720194701Srpaulo ACPI_STATUS status; 721194701Srpaulo 722194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 723194701Srpaulo 724194701Srpaulo ACPI_SERIAL_ASSERT(acpi_wmi); 725194701Srpaulo if (ACPI_FAILURE(status = AcpiEvaluateObject(h, "_WDG", NULL, &out))) 726194701Srpaulo return (status); 727194701Srpaulo obj = (ACPI_OBJECT*) out.Pointer; 728194701Srpaulo wdg_block_count = obj->Buffer.Length / sizeof(struct guid_info); 729194701Srpaulo if ((ginfo = malloc(obj->Buffer.Length, M_ACPIWMI, M_NOWAIT)) 730194701Srpaulo == NULL) { 731194701Srpaulo AcpiOsFree(out.Pointer); 732194701Srpaulo return (AE_NO_MEMORY); 733194701Srpaulo } 734194701Srpaulo memcpy(ginfo, obj->Buffer.Pointer, obj->Buffer.Length); 735194701Srpaulo for (i = 0; i < wdg_block_count; ++i) { 736194701Srpaulo if ((winfo = malloc(sizeof(struct wmi_info), M_ACPIWMI, 737194701Srpaulo M_NOWAIT | M_ZERO)) == NULL) { 738194701Srpaulo AcpiOsFree(out.Pointer); 739194701Srpaulo free(ginfo, M_ACPIWMI); 740194701Srpaulo return (AE_NO_MEMORY); 741194701Srpaulo } 742194701Srpaulo winfo->ginfo = ginfo[i]; 743241537Savg TAILQ_INSERT_TAIL(&sc->wmi_info_list, winfo, wmi_list); 744194701Srpaulo } 745194701Srpaulo AcpiOsFree(out.Pointer); 746194701Srpaulo free(ginfo, M_ACPIWMI); 747194701Srpaulo 748194701Srpaulo return (status); 749194701Srpaulo} 750194701Srpaulo 751194701Srpaulo/* 752194701Srpaulo * Toggle event generation in for the given GUID (passed by winfo) 753194701Srpaulo * Turn on to get notified (through acpi_wmi_notify_handler) if events happen 754194701Srpaulo * on the given GUID. 755194701Srpaulo */ 756194701Srpaulostatic ACPI_STATUS 757194701Srpauloacpi_wmi_toggle_we_event_generation(device_t dev, struct wmi_info *winfo, 758194701Srpaulo enum event_generation_state state) 759194701Srpaulo{ 760194701Srpaulo char method[5] = "WExx"; 761194701Srpaulo ACPI_OBJECT_LIST input; 762194701Srpaulo ACPI_OBJECT params[1]; 763194701Srpaulo struct acpi_wmi_softc *sc; 764194701Srpaulo ACPI_STATUS status; 765194701Srpaulo 766194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 767194701Srpaulo 768194701Srpaulo sc = device_get_softc(dev); 769194701Srpaulo ACPI_SERIAL_ASSERT(acpi_wmi); 770194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 771194701Srpaulo params[0].Integer.Value = state==EVENT_GENERATION_ON?1:0; 772194701Srpaulo input.Pointer = params; 773194701Srpaulo input.Count = 1; 774194701Srpaulo 775194701Srpaulo UINT8 hi = ((UINT8) winfo->ginfo.oid[0]) >> 4; 776194701Srpaulo UINT8 lo = ((UINT8) winfo->ginfo.oid[0]) & 0xf; 777194701Srpaulo method[2] = (hi > 9 ? hi + 55: hi + 48); 778194701Srpaulo method[3] = (lo > 9 ? lo + 55: lo + 48); 779194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, method, &input, NULL); 780194701Srpaulo if (status == AE_NOT_FOUND) status = AE_OK; 781194701Srpaulo 782194701Srpaulo return (status); 783194701Srpaulo} 784194701Srpaulo 785194701Srpaulo/* 786194701Srpaulo * Convert given two digit hex string (hexin) to an UINT8 referenced 787194701Srpaulo * by byteout. 788194701Srpaulo * Return != 0 if the was a problem (invalid input) 789194701Srpaulo */ 790194701Srpaulostatic __inline int acpi_wmi_hex_to_int(const UINT8 *hexin, UINT8 *byteout) 791194701Srpaulo{ 792194701Srpaulo unsigned int hi; 793194701Srpaulo unsigned int lo; 794194701Srpaulo 795194701Srpaulo hi = hexin[0]; 796194701Srpaulo lo = hexin[1]; 797194701Srpaulo if ('0' <= hi && hi <= '9') 798194701Srpaulo hi -= '0'; 799194701Srpaulo else if ('A' <= hi && hi <= 'F') 800194701Srpaulo hi -= ('A' - 10); 801194701Srpaulo else if ('a' <= hi && hi <= 'f') 802194701Srpaulo hi -= ('a' - 10); 803194701Srpaulo else 804194701Srpaulo return (1); 805194701Srpaulo if ('0' <= lo && lo <= '9') 806194701Srpaulo lo -= '0'; 807194701Srpaulo else if ('A' <= lo && lo <= 'F') 808194701Srpaulo lo -= ('A' - 10); 809194701Srpaulo else if ('a' <= lo && lo <= 'f') 810194701Srpaulo lo -= ('a' - 10); 811194701Srpaulo else 812194701Srpaulo return (1); 813194701Srpaulo *byteout = (hi << 4) + lo; 814194701Srpaulo 815194701Srpaulo return (0); 816194701Srpaulo} 817194701Srpaulo 818194701Srpaulo/* 819194701Srpaulo * Convert a human readable 36 character GUID into a 16byte 820194701Srpaulo * machine readable one. 821194701Srpaulo * The basic algorithm looks as follows: 822194701Srpaulo * Input: AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 823194701Srpaulo * Output: DCBAFEHGIJKLMNOP 824194701Srpaulo * (AA BB CC etc. represent two digit hex numbers == bytes) 825194701Srpaulo * Return != 0 if passed guid string is invalid 826194701Srpaulo */ 827194701Srpaulostatic int 828194701Srpauloacpi_wmi_guid_string_to_guid(const UINT8 *guid_string, UINT8 *guid) 829194701Srpaulo{ 830194701Srpaulo static const int mapping[20] = {3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 831194701Srpaulo 8, 9, -1, 10, 11, 12, 13, 14, 15}; 832194701Srpaulo int i; 833194701Srpaulo 834194701Srpaulo for (i = 0; i < 20; ++i, ++guid_string) { 835194701Srpaulo if (mapping[i] >= 0) { 836194701Srpaulo if (acpi_wmi_hex_to_int(guid_string, 837194701Srpaulo &guid[mapping[i]])) 838194701Srpaulo return (-1); 839194701Srpaulo ++guid_string; 840194701Srpaulo } else if (*guid_string != '-') 841194701Srpaulo return (-1); 842194701Srpaulo } 843194701Srpaulo 844194701Srpaulo return (0); 845194701Srpaulo} 846194701Srpaulo 847194701Srpaulo/* 848194701Srpaulo * Lookup a wmi_info structure in wmi_list based on a 849194701Srpaulo * human readable GUID 850194701Srpaulo * Return NULL if the GUID is unknown in the _WDG 851194701Srpaulo */ 852194701Srpaulostatic struct wmi_info* 853241537Savgacpi_wmi_lookup_wmi_info_by_guid_string(struct acpi_wmi_softc *sc, const char *guid_string) 854194701Srpaulo{ 855194701Srpaulo char guid[16]; 856194701Srpaulo struct wmi_info *winfo; 857194701Srpaulo 858194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 859194701Srpaulo 860194701Srpaulo ACPI_SERIAL_ASSERT(acpi_wmi); 861194701Srpaulo 862194701Srpaulo if (!acpi_wmi_guid_string_to_guid(guid_string, guid)) { 863241537Savg TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 864194701Srpaulo if (!memcmp(winfo->ginfo.guid, guid, 16)) { 865194701Srpaulo return (winfo); 866194701Srpaulo } 867194701Srpaulo } 868194701Srpaulo } 869194701Srpaulo 870194701Srpaulo return (NULL); 871194701Srpaulo} 872194701Srpaulo 873194701Srpaulo/* 874194701Srpaulo * open wmistat device 875194701Srpaulo */ 876194701Srpaulostatic int 877194701Srpauloacpi_wmi_wmistat_open(struct cdev* dev, int flags, int mode, struct thread *td) 878194701Srpaulo{ 879194701Srpaulo struct acpi_wmi_softc *sc; 880194701Srpaulo int ret; 881194701Srpaulo 882194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 883194701Srpaulo return (EBADF); 884194701Srpaulo sc = dev->si_drv1; 885194701Srpaulo 886194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 887194701Srpaulo if (sc->wmistat_open_pid != 0) { 888194701Srpaulo ret = EBUSY; 889194701Srpaulo } 890194701Srpaulo else { 891194701Srpaulo if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 892194701Srpaulo == NULL) { 893194701Srpaulo ret = ENXIO; 894194701Srpaulo } else { 895194701Srpaulo sc->wmistat_open_pid = td->td_proc->p_pid; 896194701Srpaulo sc->wmistat_bufptr = 0; 897194701Srpaulo ret = 0; 898194701Srpaulo } 899194701Srpaulo } 900194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 901194701Srpaulo 902194701Srpaulo return (ret); 903194701Srpaulo} 904194701Srpaulo 905194701Srpaulo/* 906194701Srpaulo * close wmistat device 907194701Srpaulo */ 908194701Srpaulostatic int 909194701Srpauloacpi_wmi_wmistat_close(struct cdev* dev, int flags, int mode, 910194701Srpaulo struct thread *td) 911194701Srpaulo{ 912194701Srpaulo struct acpi_wmi_softc *sc; 913194701Srpaulo int ret; 914194701Srpaulo 915194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 916194701Srpaulo return (EBADF); 917194701Srpaulo sc = dev->si_drv1; 918194701Srpaulo 919194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 920194701Srpaulo if (sc->wmistat_open_pid == 0) { 921194701Srpaulo ret = EBADF; 922194701Srpaulo } 923194701Srpaulo else { 924194701Srpaulo if (sc->wmistat_bufptr != -1) { 925194701Srpaulo sbuf_delete(&sc->wmistat_sbuf); 926194701Srpaulo sc->wmistat_bufptr = -1; 927194701Srpaulo } 928194701Srpaulo sc->wmistat_open_pid = 0; 929194701Srpaulo ret = 0; 930194701Srpaulo } 931194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 932194701Srpaulo 933194701Srpaulo return (ret); 934194701Srpaulo} 935194701Srpaulo 936194701Srpaulo/* 937194701Srpaulo * Read from wmistat guid information 938194701Srpaulo */ 939194701Srpaulostatic int 940194701Srpauloacpi_wmi_wmistat_read(struct cdev *dev, struct uio *buf, int flag) 941194701Srpaulo{ 942194701Srpaulo struct acpi_wmi_softc *sc; 943194701Srpaulo struct wmi_info *winfo; 944194701Srpaulo int l; 945194701Srpaulo int ret; 946194701Srpaulo UINT8* guid; 947194701Srpaulo 948194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 949194701Srpaulo return (EBADF); 950194701Srpaulo sc = dev->si_drv1; 951194701Srpaulo 952194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 953194701Srpaulo if (sc->wmistat_open_pid != buf->uio_td->td_proc->p_pid || 954194701Srpaulo sc->wmistat_bufptr == -1) { 955194701Srpaulo ret = EBADF; 956194701Srpaulo } 957194701Srpaulo else { 958194701Srpaulo if (!sbuf_done(&sc->wmistat_sbuf)) { 959194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, "GUID " 960194701Srpaulo " INST EXPE METH STR " 961194701Srpaulo "EVENT OID\n"); 962241537Savg TAILQ_FOREACH(winfo, &sc->wmi_info_list, wmi_list) { 963194701Srpaulo guid = (UINT8*)winfo->ginfo.guid; 964194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 965194701Srpaulo "{%02X%02X%02X%02X-%02X%02X-" 966194701Srpaulo "%02X%02X-%02X%02X-%02X%02X" 967194701Srpaulo "%02X%02X%02X%02X} %3d %-5s", 968194701Srpaulo guid[3], guid[2], guid[1], guid[0], 969194701Srpaulo guid[5], guid[4], 970194701Srpaulo guid[7], guid[6], 971194701Srpaulo guid[8], guid[9], 972194701Srpaulo guid[10], guid[11], guid[12], 973194701Srpaulo guid[13], guid[14], guid[15], 974194701Srpaulo winfo->ginfo.max_instance, 975194701Srpaulo (winfo->ginfo.flags& 976194701Srpaulo ACPI_WMI_REGFLAG_EXPENSIVE)? 977194701Srpaulo "YES":"NO" 978194701Srpaulo ); 979194701Srpaulo if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD) 980194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 981194701Srpaulo "WM%c%c ", 982194701Srpaulo winfo->ginfo.oid[0], 983194701Srpaulo winfo->ginfo.oid[1]); 984194701Srpaulo else 985194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, "NO "); 986194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, "%-4s", 987194701Srpaulo (winfo->ginfo.flags& 988194701Srpaulo ACPI_WMI_REGFLAG_STRING)?"YES":"NO" 989194701Srpaulo ); 990194701Srpaulo if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT) 991194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 992194701Srpaulo "0x%02X%s -\n", 993194701Srpaulo (UINT8)winfo->ginfo.oid[0], 994194701Srpaulo winfo->event_handler==NULL? 995194701Srpaulo " ":"+"); 996194701Srpaulo else 997194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 998194701Srpaulo "NO %c%c\n", 999194701Srpaulo winfo->ginfo.oid[0], 1000194701Srpaulo winfo->ginfo.oid[1]); 1001194701Srpaulo } 1002194701Srpaulo sbuf_finish(&sc->wmistat_sbuf); 1003194701Srpaulo } 1004194701Srpaulo if (sbuf_len(&sc->wmistat_sbuf) <= 0) { 1005194701Srpaulo sbuf_delete(&sc->wmistat_sbuf); 1006194701Srpaulo sc->wmistat_bufptr = -1; 1007194701Srpaulo sc->wmistat_open_pid = 0; 1008194701Srpaulo ret = ENOMEM; 1009194701Srpaulo } else { 1010194701Srpaulo l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) - 1011194701Srpaulo sc->wmistat_bufptr); 1012194701Srpaulo ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) + 1013194701Srpaulo sc->wmistat_bufptr, l, buf) : 0; 1014194701Srpaulo sc->wmistat_bufptr += l; 1015194701Srpaulo } 1016194701Srpaulo } 1017194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 1018194701Srpaulo 1019194701Srpaulo return (ret); 1020194701Srpaulo} 1021