acpi_wmi.c revision 195185
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: head/sys/dev/acpi_support/acpi_wmi.c 195185 2009-06-30 09:51:41Z rpaulo $"); 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 56194701SrpauloMALLOC_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 */ 77194701Srpaulo}; 78194701Srpaulo 79194701Srpaulo/* 80194701Srpaulo * Struct that holds information about 81194701Srpaulo * about a single GUID entry in _WDG 82194701Srpaulo */ 83194701Srpaulostruct guid_info { 84194701Srpaulo char guid[16]; /* 16 byte non human readable GUID */ 85194701Srpaulo char oid[2]; /* object id or event notify id (first byte) */ 86194701Srpaulo UINT8 max_instance; /* highest instance known for this GUID */ 87194701Srpaulo UINT8 flags; /* ACPI_WMI_REGFLAG_%s */ 88194701Srpaulo}; 89194701Srpaulo 90194701Srpaulo/* WExx event generation state (on/off) */ 91194701Srpauloenum event_generation_state { 92194701Srpaulo EVENT_GENERATION_ON = 1, 93194701Srpaulo EVENT_GENERATION_OFF = 0 94194701Srpaulo}; 95194701Srpaulo 96194701Srpaulo 97194701Srpaulo/* 98194701Srpaulo * Information about one entry in _WDG. 99194701Srpaulo * List of those is used to lookup information by GUID. 100194701Srpaulo */ 101194701Srpaulostruct wmi_info { 102194701Srpaulo TAILQ_ENTRY(wmi_info) wmi_list; 103194701Srpaulo struct guid_info ginfo; /* information on guid */ 104194701Srpaulo ACPI_NOTIFY_HANDLER event_handler;/* client provided event handler */ 105194701Srpaulo void *event_handler_user_data; /* ev handler cookie */ 106194701Srpaulo}; 107194701Srpaulo 108194701SrpauloTAILQ_HEAD(wmi_info_list_head, wmi_info) 109194701Srpaulo wmi_info_list = TAILQ_HEAD_INITIALIZER(wmi_info_list); 110194701Srpaulo 111194701SrpauloACPI_SERIAL_DECL(acpi_wmi, "ACPI-WMI Mapping"); 112194701Srpaulo 113194701Srpaulo/* public interface - declaration */ 114194701Srpaulo/* standard device interface*/ 115194701Srpaulostatic int acpi_wmi_probe(device_t dev); 116194701Srpaulostatic int acpi_wmi_attach(device_t dev); 117194701Srpaulostatic int acpi_wmi_detach(device_t dev); 118194701Srpaulo/* see acpi_wmi_if.m */ 119194701Srpaulostatic int acpi_wmi_provides_guid_string_method(device_t dev, 120194701Srpaulo const char *guid_string); 121194701Srpaulostatic ACPI_STATUS acpi_wmi_evaluate_call_method(device_t dev, 122194701Srpaulo const char *guid_string, UINT8 instance, 123194701Srpaulo UINT32 method_id, const ACPI_BUFFER *in, 124194701Srpaulo ACPI_BUFFER *out); 125194701Srpaulostatic ACPI_STATUS acpi_wmi_install_event_handler_method(device_t dev, 126194701Srpaulo const char *guid_string, ACPI_NOTIFY_HANDLER handler, 127194701Srpaulo void *data); 128194701Srpaulostatic ACPI_STATUS acpi_wmi_remove_event_handler_method(device_t dev, 129194701Srpaulo const char *guid_string); 130194701Srpaulostatic ACPI_STATUS acpi_wmi_get_event_data_method(device_t dev, 131194701Srpaulo UINT32 event_id, ACPI_BUFFER *out); 132194701Srpaulostatic ACPI_STATUS acpi_wmi_get_block_method(device_t dev, 133194701Srpaulo const char *guid_string, 134194701Srpaulo UINT8 instance, ACPI_BUFFER *out); 135194701Srpaulostatic ACPI_STATUS acpi_wmi_set_block_method(device_t dev, 136194701Srpaulo const char *guid_string, 137194701Srpaulo UINT8 instance, const ACPI_BUFFER *in); 138194701Srpaulo/* private interface - declaration */ 139194701Srpaulo/* callbacks */ 140194701Srpaulostatic void acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, 141194701Srpaulo void *context); 142194701Srpaulostatic ACPI_STATUS acpi_wmi_ec_handler(UINT32 function, 143194701Srpaulo ACPI_PHYSICAL_ADDRESS address, UINT32 width, 144194701Srpaulo ACPI_INTEGER *value, void *context, 145194701Srpaulo void *region_context); 146194701Srpaulo/* helpers */ 147194701Srpaulostatic ACPI_STATUS acpi_wmi_read_wdg_blocks(ACPI_HANDLE h); 148194701Srpaulostatic ACPI_STATUS acpi_wmi_toggle_we_event_generation(device_t dev, 149194701Srpaulo struct wmi_info *winfo, 150194701Srpaulo enum event_generation_state state); 151194701Srpaulostatic int acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, 152194701Srpaulo UINT8 *guid); 153194701Srpaulostatic struct wmi_info* acpi_wmi_lookup_wmi_info_by_guid_string( 154194701Srpaulo const char *guid_string); 155194701Srpaulo 156194701Srpaulostatic d_open_t acpi_wmi_wmistat_open; 157194701Srpaulostatic d_close_t acpi_wmi_wmistat_close; 158194701Srpaulostatic d_read_t acpi_wmi_wmistat_read; 159194701Srpaulo 160194701Srpaulo/* handler /dev/wmistat device */ 161194701Srpaulostatic struct cdevsw wmistat_cdevsw = { 162194701Srpaulo .d_version = D_VERSION, 163194701Srpaulo .d_open = acpi_wmi_wmistat_open, 164194701Srpaulo .d_close = acpi_wmi_wmistat_close, 165194701Srpaulo .d_read = acpi_wmi_wmistat_read, 166194701Srpaulo .d_name = "wmistat", 167194701Srpaulo}; 168194701Srpaulo 169194701Srpaulo 170194701Srpaulostatic device_method_t acpi_wmi_methods[] = { 171194701Srpaulo /* Device interface */ 172194701Srpaulo DEVMETHOD(device_probe, acpi_wmi_probe), 173194701Srpaulo DEVMETHOD(device_attach, acpi_wmi_attach), 174194701Srpaulo DEVMETHOD(device_detach, acpi_wmi_detach), 175194701Srpaulo 176194701Srpaulo /* acpi_wmi interface */ 177194701Srpaulo DEVMETHOD(acpi_wmi_provides_guid_string, 178194701Srpaulo acpi_wmi_provides_guid_string_method), 179194701Srpaulo DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method), 180194701Srpaulo DEVMETHOD(acpi_wmi_install_event_handler, 181194701Srpaulo acpi_wmi_install_event_handler_method), 182194701Srpaulo DEVMETHOD(acpi_wmi_remove_event_handler, 183194701Srpaulo acpi_wmi_remove_event_handler_method), 184194701Srpaulo DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method), 185194701Srpaulo DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method), 186194701Srpaulo DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method), 187194701Srpaulo 188194701Srpaulo {0, 0} 189194701Srpaulo}; 190194701Srpaulo 191194701Srpaulostatic driver_t acpi_wmi_driver = { 192194701Srpaulo "acpi_wmi", 193194701Srpaulo acpi_wmi_methods, 194194701Srpaulo sizeof(struct acpi_wmi_softc), 195194701Srpaulo}; 196194701Srpaulo 197194701Srpaulostatic devclass_t acpi_wmi_devclass; 198194701SrpauloDRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, 0, 0); 199194701SrpauloMODULE_VERSION(acpi_wmi, 1); 200194701SrpauloMODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1); 201194701Srpaulostatic char *wmi_ids[] = {"PNP0C14", "PNP0c14", NULL}; 202194701Srpaulo 203194701Srpaulo/* 204194701Srpaulo * Probe for the PNP0C14 ACPI node 205194701Srpaulo */ 206194701Srpaulostatic int 207194701Srpauloacpi_wmi_probe(device_t dev) 208194701Srpaulo{ 209194701Srpaulo if (acpi_disabled("wmi") || 210194701Srpaulo ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids) == NULL) 211194701Srpaulo return (ENXIO); 212194701Srpaulo device_set_desc(dev, "ACPI-WMI mapping"); 213194701Srpaulo 214194701Srpaulo return (0); 215194701Srpaulo} 216194701Srpaulo 217194701Srpaulo/* 218194701Srpaulo * Attach the device by: 219194701Srpaulo * - Looking for the first ACPI EC device 220194701Srpaulo * - Install the notify handler 221194701Srpaulo * - Install the EC address space handler 222194701Srpaulo * - Look for the _WDG node and read GUID information blocks 223194701Srpaulo */ 224194701Srpaulostatic int 225194701Srpauloacpi_wmi_attach(device_t dev) 226194701Srpaulo{ 227194701Srpaulo struct acpi_wmi_softc *sc; 228194701Srpaulo int ret; 229194701Srpaulo ACPI_STATUS status; 230194701Srpaulo 231194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 232194701Srpaulo sc = device_get_softc(dev); 233194701Srpaulo ret = ENXIO; 234194701Srpaulo 235194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 236194701Srpaulo sc->wmi_dev = dev; 237194701Srpaulo sc->wmi_handle = acpi_get_handle(dev); 238194701Srpaulo TAILQ_INIT(&wmi_info_list); 239194701Srpaulo /* XXX Only works with one EC, but nearly all systems only have one. */ 240194701Srpaulo if ((sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0)) 241194701Srpaulo == NULL) 242194701Srpaulo device_printf(dev, "cannot find EC device\n"); 243194701Srpaulo else if (ACPI_FAILURE((status = AcpiInstallNotifyHandler(sc->wmi_handle, 244194701Srpaulo ACPI_DEVICE_NOTIFY, acpi_wmi_notify_handler, sc)))) 245194701Srpaulo device_printf(sc->wmi_dev, "couldn't install notify handler - %s\n", 246194701Srpaulo AcpiFormatException(status)); 247194701Srpaulo else if (ACPI_FAILURE((status = AcpiInstallAddressSpaceHandler( 248194701Srpaulo sc->wmi_handle, ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler, 249194701Srpaulo NULL, sc)))) { 250194701Srpaulo device_printf(sc->wmi_dev, "couldn't install EC handler - %s\n", 251194701Srpaulo AcpiFormatException(status)); 252194701Srpaulo AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 253194701Srpaulo acpi_wmi_notify_handler); 254194701Srpaulo } else if (ACPI_FAILURE((status = acpi_wmi_read_wdg_blocks( 255194701Srpaulo sc->wmi_handle)))) { 256194701Srpaulo device_printf(sc->wmi_dev, "couldn't parse _WDG - %s\n", 257194701Srpaulo AcpiFormatException(status)); 258194701Srpaulo AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 259194701Srpaulo acpi_wmi_notify_handler); 260194701Srpaulo AcpiRemoveAddressSpaceHandler(sc->wmi_handle, ACPI_ADR_SPACE_EC, 261194701Srpaulo acpi_wmi_ec_handler); 262194701Srpaulo } else { 263194701Srpaulo sc->wmistat_dev_t = make_dev(&wmistat_cdevsw, 0, UID_ROOT, 264194701Srpaulo GID_WHEEL, 0644, "wmistat"); 265194701Srpaulo sc->wmistat_dev_t->si_drv1 = sc; 266194701Srpaulo sc->wmistat_open_pid = 0; 267194701Srpaulo sc->wmistat_bufptr = -1; 268194701Srpaulo ret = 0; 269194701Srpaulo } 270194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 271194701Srpaulo 272194701Srpaulo return (ret); 273194701Srpaulo} 274194701Srpaulo 275194701Srpaulo/* 276194701Srpaulo * Detach the driver by: 277194701Srpaulo * - Removing notification handler 278194701Srpaulo * - Removing address space handler 279194701Srpaulo * - Turning off event generation for all WExx event activated by 280194701Srpaulo * child drivers 281194701Srpaulo */ 282194701Srpaulostatic int 283194701Srpauloacpi_wmi_detach(device_t dev) 284194701Srpaulo{ 285194701Srpaulo struct wmi_info *winfo, *tmp; 286194701Srpaulo struct acpi_wmi_softc *sc; 287194701Srpaulo int ret; 288194701Srpaulo 289194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 290194701Srpaulo sc = device_get_softc(dev); 291194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 292194701Srpaulo 293194701Srpaulo if (sc->wmistat_open_pid != 0) { 294194701Srpaulo ret = EBUSY; 295194701Srpaulo } else { 296194701Srpaulo AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY, 297194701Srpaulo acpi_wmi_notify_handler); 298194701Srpaulo AcpiRemoveAddressSpaceHandler(sc->wmi_handle, 299194701Srpaulo ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler); 300194701Srpaulo TAILQ_FOREACH_SAFE(winfo, &wmi_info_list, wmi_list, tmp) { 301194701Srpaulo if (winfo->event_handler) 302194701Srpaulo acpi_wmi_toggle_we_event_generation(dev, 303194701Srpaulo winfo, EVENT_GENERATION_OFF); 304194701Srpaulo TAILQ_REMOVE(&wmi_info_list, winfo, wmi_list); 305194701Srpaulo free(winfo, M_ACPIWMI); 306194701Srpaulo } 307194701Srpaulo if (sc->wmistat_bufptr != -1) { 308194701Srpaulo sbuf_delete(&sc->wmistat_sbuf); 309194701Srpaulo sc->wmistat_bufptr = -1; 310194701Srpaulo } 311194701Srpaulo sc->wmistat_open_pid = 0; 312194701Srpaulo destroy_dev(sc->wmistat_dev_t); 313194701Srpaulo ret = 0; 314194701Srpaulo } 315194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 316194701Srpaulo 317194701Srpaulo return (ret); 318194701Srpaulo} 319194701Srpaulo 320194701Srpaulo 321194701Srpaulo/* 322194701Srpaulo * Check if the given GUID string (human readable format 323194701Srpaulo * AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP) 324194701Srpaulo * exists within _WDG 325194701Srpaulo */ 326194701Srpaulostatic int 327194701Srpauloacpi_wmi_provides_guid_string_method(device_t dev, const char *guid_string) 328194701Srpaulo{ 329195185Srpaulo struct wmi_info *winfo; 330194701Srpaulo int ret; 331194701Srpaulo 332194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 333194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 334195185Srpaulo winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string); 335195185Srpaulo ret = (winfo == NULL)?0:winfo->ginfo.max_instance+1; 336194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 337194701Srpaulo 338194701Srpaulo return (ret); 339194701Srpaulo} 340194701Srpaulo 341194701Srpaulo/* 342194701Srpaulo * Call a method "method_id" on the given GUID block 343194701Srpaulo * write result into user provided output buffer 344194701Srpaulo */ 345194701Srpaulostatic ACPI_STATUS 346194701Srpauloacpi_wmi_evaluate_call_method(device_t dev, const char *guid_string, 347194701Srpaulo UINT8 instance, UINT32 method_id, const ACPI_BUFFER *in, ACPI_BUFFER *out) 348194701Srpaulo{ 349194701Srpaulo ACPI_OBJECT params[3]; 350194701Srpaulo ACPI_OBJECT_LIST input; 351194701Srpaulo char method[5] = "WMxx"; 352194701Srpaulo struct wmi_info *winfo; 353194701Srpaulo struct acpi_wmi_softc *sc; 354194701Srpaulo ACPI_STATUS status; 355194701Srpaulo 356194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 357194701Srpaulo 358194701Srpaulo sc = device_get_softc(dev); 359194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 360194701Srpaulo if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 361194701Srpaulo == NULL) 362194701Srpaulo status = AE_NOT_FOUND; 363194701Srpaulo else if (!(winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 364194701Srpaulo status = AE_BAD_DATA; 365194701Srpaulo else if (instance > winfo->ginfo.max_instance) 366194701Srpaulo status = AE_BAD_PARAMETER; 367194701Srpaulo else { 368194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 369194701Srpaulo params[0].Integer.Value = instance; 370194701Srpaulo params[1].Type = ACPI_TYPE_INTEGER; 371194701Srpaulo params[1].Integer.Value = method_id; 372194701Srpaulo input.Pointer = params; 373194701Srpaulo input.Count = 2; 374194701Srpaulo if (in) { 375194701Srpaulo params[2].Type = 376194701Srpaulo (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 377194701Srpaulo ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 378194701Srpaulo params[2].Buffer.Length = in->Length; 379194701Srpaulo params[2].Buffer.Pointer = in->Pointer; 380194701Srpaulo input.Count = 3; 381194701Srpaulo } 382194701Srpaulo method[2] = winfo->ginfo.oid[0]; 383194701Srpaulo method[3] = winfo->ginfo.oid[1]; 384194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, method, 385194701Srpaulo &input, out); 386194701Srpaulo } 387194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 388194701Srpaulo 389194701Srpaulo return (status); 390194701Srpaulo} 391194701Srpaulo 392194701Srpaulo/* 393194701Srpaulo * Install a user provided event_handler on the given GUID 394194701Srpaulo * provided *data will be passed on callback 395194701Srpaulo * If there is already an existing event handler registered it will be silently 396194701Srpaulo * discarded 397194701Srpaulo */ 398194701Srpaulostatic ACPI_STATUS 399194701Srpauloacpi_wmi_install_event_handler_method(device_t dev, const char *guid_string, 400194701Srpaulo ACPI_NOTIFY_HANDLER event_handler, void *data) 401194701Srpaulo{ 402194701Srpaulo struct wmi_info *winfo; 403194701Srpaulo ACPI_STATUS status; 404194701Srpaulo 405194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 406194701Srpaulo 407194701Srpaulo status = AE_OK; 408194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 409194701Srpaulo if (guid_string == NULL || event_handler == NULL) 410194701Srpaulo status = AE_BAD_PARAMETER; 411194701Srpaulo else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 412194701Srpaulo == NULL) 413194701Srpaulo status = AE_NOT_EXIST; 414194701Srpaulo else if (winfo->event_handler != NULL || 415194701Srpaulo (status = acpi_wmi_toggle_we_event_generation(dev, winfo, 416194701Srpaulo EVENT_GENERATION_ON)) == AE_OK) { 417194701Srpaulo winfo->event_handler = event_handler; 418194701Srpaulo winfo->event_handler_user_data = data; 419194701Srpaulo } 420194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 421194701Srpaulo 422194701Srpaulo return (status); 423194701Srpaulo} 424194701Srpaulo 425194701Srpaulo/* 426194701Srpaulo * Remove a previously installed event handler from the given GUID 427194701Srpaulo * If there was none installed, this call is silently discarded and 428194701Srpaulo * reported as AE_OK 429194701Srpaulo */ 430194701Srpaulostatic ACPI_STATUS 431194701Srpauloacpi_wmi_remove_event_handler_method(device_t dev, const char *guid_string) 432194701Srpaulo{ 433194701Srpaulo struct wmi_info *winfo; 434194701Srpaulo ACPI_STATUS status; 435194701Srpaulo 436194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 437194701Srpaulo 438194701Srpaulo status = AE_OK; 439194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 440194701Srpaulo if (guid_string && 441194701Srpaulo (winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 442194701Srpaulo != NULL && winfo->event_handler) { 443194701Srpaulo status = acpi_wmi_toggle_we_event_generation(dev, winfo, 444194701Srpaulo EVENT_GENERATION_OFF); 445194701Srpaulo winfo->event_handler = NULL; 446194701Srpaulo winfo->event_handler_user_data = NULL; 447194701Srpaulo } 448194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 449194701Srpaulo 450194701Srpaulo return (status); 451194701Srpaulo} 452194701Srpaulo 453194701Srpaulo/* 454194701Srpaulo * Get details on an event received through a callback registered 455194701Srpaulo * through ACPI_WMI_REMOVE_EVENT_HANDLER into a user provided output buffer. 456194701Srpaulo * (event_id equals "notify" passed in the callback) 457194701Srpaulo */ 458194701Srpaulostatic ACPI_STATUS 459194701Srpauloacpi_wmi_get_event_data_method(device_t dev, UINT32 event_id, ACPI_BUFFER *out) 460194701Srpaulo{ 461194701Srpaulo ACPI_OBJECT_LIST input; 462194701Srpaulo ACPI_OBJECT params[1]; 463194701Srpaulo struct acpi_wmi_softc *sc; 464194701Srpaulo struct wmi_info *winfo; 465194701Srpaulo ACPI_STATUS status; 466194701Srpaulo 467194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 468194701Srpaulo 469194701Srpaulo sc = device_get_softc(dev); 470194701Srpaulo status = AE_NOT_FOUND; 471194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 472194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 473194701Srpaulo params[0].Integer.Value = event_id; 474194701Srpaulo input.Pointer = params; 475194701Srpaulo input.Count = 1; 476194701Srpaulo TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 477194701Srpaulo if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 478194701Srpaulo ((UINT8) winfo->ginfo.oid[0] == event_id)) { 479194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, "_WED", 480194701Srpaulo &input, out); 481194701Srpaulo break; 482194701Srpaulo } 483194701Srpaulo } 484194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 485194701Srpaulo 486194701Srpaulo return (status); 487194701Srpaulo} 488194701Srpaulo 489194701Srpaulo/* 490194701Srpaulo * Read a block of data from the given GUID (using WQxx (query)) 491194701Srpaulo * Will be returned in a user provided buffer (out). 492194701Srpaulo * If the method is marked as expensive (ACPI_WMI_REGFLAG_EXPENSIVE) 493194701Srpaulo * we will first call the WCxx control method to lock the node to 494194701Srpaulo * lock the node for data collection and release it afterwards. 495194701Srpaulo * (Failed WCxx calls are ignored to "support" broken implementations) 496194701Srpaulo */ 497194701Srpaulostatic ACPI_STATUS 498194701Srpauloacpi_wmi_get_block_method(device_t dev, const char *guid_string, UINT8 instance, 499194701Srpaulo ACPI_BUFFER *out) 500194701Srpaulo{ 501194701Srpaulo char wc_method[5] = "WCxx"; 502194701Srpaulo char wq_method[5] = "WQxx"; 503194701Srpaulo ACPI_OBJECT_LIST wc_input; 504194701Srpaulo ACPI_OBJECT_LIST wq_input; 505194701Srpaulo ACPI_OBJECT wc_params[1]; 506194701Srpaulo ACPI_OBJECT wq_params[1]; 507194701Srpaulo ACPI_HANDLE wc_handle; 508194701Srpaulo struct acpi_wmi_softc *sc; 509194701Srpaulo struct wmi_info *winfo; 510194701Srpaulo ACPI_STATUS status; 511194701Srpaulo ACPI_STATUS wc_status; 512194701Srpaulo 513194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 514194701Srpaulo 515194701Srpaulo sc = device_get_softc(dev); 516194701Srpaulo wc_status = AE_ERROR; 517194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 518194701Srpaulo if (guid_string == NULL || out == NULL) 519194701Srpaulo status = AE_BAD_PARAMETER; 520194701Srpaulo else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 521194701Srpaulo == NULL) 522194701Srpaulo status = AE_ERROR; 523194701Srpaulo else if (instance > winfo->ginfo.max_instance) 524194701Srpaulo status = AE_BAD_PARAMETER; 525194701Srpaulo else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 526194701Srpaulo (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 527194701Srpaulo status = AE_ERROR; 528194701Srpaulo else { 529194701Srpaulo wq_params[0].Type = ACPI_TYPE_INTEGER; 530194701Srpaulo wq_params[0].Integer.Value = instance; 531194701Srpaulo wq_input.Pointer = wq_params; 532194701Srpaulo wq_input.Count = 1; 533194701Srpaulo if (winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) { 534194701Srpaulo wc_params[0].Type = ACPI_TYPE_INTEGER; 535194701Srpaulo wc_params[0].Integer.Value = 1; 536194701Srpaulo wc_input.Pointer = wc_params; 537194701Srpaulo wc_input.Count = 1; 538194701Srpaulo wc_method[2] = winfo->ginfo.oid[0]; 539194701Srpaulo wc_method[3] = winfo->ginfo.oid[1]; 540194701Srpaulo wc_status = AcpiGetHandle(sc->wmi_handle, wc_method, 541194701Srpaulo &wc_handle); 542194701Srpaulo if (ACPI_SUCCESS(wc_status)) 543194701Srpaulo wc_status = AcpiEvaluateObject(wc_handle, 544194701Srpaulo wc_method, &wc_input, NULL); 545194701Srpaulo } 546194701Srpaulo wq_method[2] = winfo->ginfo.oid[0]; 547194701Srpaulo wq_method[3] = winfo->ginfo.oid[1]; 548194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, wq_method, 549194701Srpaulo &wq_input, out); 550194701Srpaulo if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) 551194701Srpaulo && ACPI_SUCCESS(wc_status)) { 552194701Srpaulo wc_params[0].Integer.Value = 0; 553194701Srpaulo status = AcpiEvaluateObject(wc_handle, wc_method, 554194701Srpaulo &wc_input, NULL); /* XXX this might be 555194701Srpaulo the wrong status to 556194701Srpaulo return? */ 557194701Srpaulo } 558194701Srpaulo } 559194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 560194701Srpaulo 561194701Srpaulo return (status); 562194701Srpaulo} 563194701Srpaulo 564194701Srpaulo/* 565194701Srpaulo * Write a block of data to the given GUID (using WSxx) 566194701Srpaulo */ 567194701Srpaulostatic ACPI_STATUS 568194701Srpauloacpi_wmi_set_block_method(device_t dev, const char *guid_string, UINT8 instance, 569194701Srpaulo const ACPI_BUFFER *in) 570194701Srpaulo{ 571194701Srpaulo char method[5] = "WSxx"; 572194701Srpaulo ACPI_OBJECT_LIST input; 573194701Srpaulo ACPI_OBJECT params[2]; 574194701Srpaulo struct wmi_info *winfo; 575194701Srpaulo struct acpi_wmi_softc *sc; 576194701Srpaulo ACPI_STATUS status; 577194701Srpaulo 578194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 579194701Srpaulo 580194701Srpaulo sc = device_get_softc(dev); 581194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 582194701Srpaulo if (guid_string == NULL || in == NULL) 583194701Srpaulo status = AE_BAD_DATA; 584194701Srpaulo else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string)) 585194701Srpaulo == NULL) 586194701Srpaulo status = AE_ERROR; 587194701Srpaulo else if (instance > winfo->ginfo.max_instance) 588194701Srpaulo status = AE_BAD_PARAMETER; 589194701Srpaulo else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) || 590194701Srpaulo (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD)) 591194701Srpaulo status = AE_ERROR; 592194701Srpaulo else { 593194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 594194701Srpaulo params[0].Integer.Value = instance; 595194701Srpaulo input.Pointer = params; 596194701Srpaulo input.Count = 2; 597194701Srpaulo params[1].Type = (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING) 598194701Srpaulo ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER; 599194701Srpaulo params[1].Buffer.Length = in->Length; 600194701Srpaulo params[1].Buffer.Pointer = in->Pointer; 601194701Srpaulo method[2] = winfo->ginfo.oid[0]; 602194701Srpaulo method[3] = winfo->ginfo.oid[1]; 603194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, method, 604194701Srpaulo &input, NULL); 605194701Srpaulo } 606194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 607194701Srpaulo 608194701Srpaulo return (status); 609194701Srpaulo} 610194701Srpaulo 611194701Srpaulo/* 612194701Srpaulo * Handle events received and dispatch them to 613194701Srpaulo * stakeholders that registered through ACPI_WMI_INSTALL_EVENT_HANDLER 614194701Srpaulo */ 615194701Srpaulostatic void 616194701Srpauloacpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 617194701Srpaulo{ 618194701Srpaulo ACPI_NOTIFY_HANDLER handler; 619194701Srpaulo void *handler_data; 620194701Srpaulo struct wmi_info *winfo; 621194701Srpaulo 622194701Srpaulo ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 623194701Srpaulo 624194701Srpaulo handler = NULL; 625194701Srpaulo handler_data = NULL; 626194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 627194701Srpaulo TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 628194701Srpaulo if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) && 629194701Srpaulo ((UINT8) winfo->ginfo.oid[0] == notify)) { 630194701Srpaulo if (winfo->event_handler) { 631194701Srpaulo handler = winfo->event_handler; 632194701Srpaulo handler_data = winfo->event_handler_user_data; 633194701Srpaulo break; 634194701Srpaulo } 635194701Srpaulo } 636194701Srpaulo } 637194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 638194701Srpaulo if (handler) { 639194701Srpaulo handler(h, notify, handler_data); 640194701Srpaulo } 641194701Srpaulo} 642194701Srpaulo 643194701Srpaulo/* 644194701Srpaulo * Handle EC address space notifications reveived on the WDG node 645194701Srpaulo * (this mimics EcAddressSpaceHandler in acpi_ec.c) 646194701Srpaulo */ 647194701Srpaulostatic ACPI_STATUS 648194701Srpauloacpi_wmi_ec_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 649194701Srpaulo UINT32 width, ACPI_INTEGER *value, void *context, 650194701Srpaulo void *region_context) 651194701Srpaulo{ 652194701Srpaulo struct acpi_wmi_softc *sc; 653194701Srpaulo int i; 654194701Srpaulo ACPI_INTEGER ec_data; 655194701Srpaulo UINT8 ec_addr; 656194701Srpaulo ACPI_STATUS status; 657194701Srpaulo 658194716Srpaulo ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)address); 659194701Srpaulo 660194701Srpaulo sc = (struct acpi_wmi_softc *)context; 661194701Srpaulo if (width % 8 != 0 || value == NULL || context == NULL) 662194701Srpaulo return (AE_BAD_PARAMETER); 663194701Srpaulo if (address + (width / 8) - 1 > 0xFF) 664194701Srpaulo return (AE_BAD_ADDRESS); 665194701Srpaulo if (function == ACPI_READ) 666194701Srpaulo *value = 0; 667194701Srpaulo ec_addr = address; 668194701Srpaulo status = AE_ERROR; 669194701Srpaulo 670194701Srpaulo for (i = 0; i < width; i += 8, ++ec_addr) { 671194701Srpaulo switch (function) { 672194701Srpaulo case ACPI_READ: 673194701Srpaulo status = ACPI_EC_READ(sc->ec_dev, ec_addr, &ec_data, 1); 674194701Srpaulo if (ACPI_SUCCESS(status)) 675194701Srpaulo *value |= ((ACPI_INTEGER)ec_data) << i; 676194701Srpaulo break; 677194701Srpaulo case ACPI_WRITE: 678194701Srpaulo ec_data = (UINT8)((*value) >> i); 679194701Srpaulo status = ACPI_EC_WRITE(sc->ec_dev, ec_addr, ec_data, 1); 680194701Srpaulo break; 681194701Srpaulo default: 682194701Srpaulo device_printf(sc->wmi_dev, 683194701Srpaulo "invalid acpi_wmi_ec_handler function %d\n", 684194701Srpaulo function); 685194701Srpaulo status = AE_BAD_PARAMETER; 686194701Srpaulo break; 687194701Srpaulo } 688194701Srpaulo if (ACPI_FAILURE(status)) 689194701Srpaulo break; 690194701Srpaulo } 691194701Srpaulo 692194701Srpaulo return (status); 693194701Srpaulo} 694194701Srpaulo 695194701Srpaulo/* 696194701Srpaulo * Read GUID blocks from the _WDG node 697194701Srpaulo * into wmi_info_list. 698194701Srpaulo */ 699194701Srpaulostatic ACPI_STATUS 700194701Srpauloacpi_wmi_read_wdg_blocks(ACPI_HANDLE h) 701194701Srpaulo{ 702194701Srpaulo ACPI_BUFFER out = {ACPI_ALLOCATE_BUFFER, NULL}; 703194701Srpaulo struct guid_info *ginfo; 704194701Srpaulo ACPI_OBJECT *obj; 705194701Srpaulo struct wmi_info *winfo; 706194701Srpaulo UINT32 i; 707194701Srpaulo UINT32 wdg_block_count; 708194701Srpaulo ACPI_STATUS status; 709194701Srpaulo 710194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 711194701Srpaulo 712194701Srpaulo ACPI_SERIAL_ASSERT(acpi_wmi); 713194701Srpaulo if (ACPI_FAILURE(status = AcpiEvaluateObject(h, "_WDG", NULL, &out))) 714194701Srpaulo return (status); 715194701Srpaulo obj = (ACPI_OBJECT*) out.Pointer; 716194701Srpaulo wdg_block_count = obj->Buffer.Length / sizeof(struct guid_info); 717194701Srpaulo if ((ginfo = malloc(obj->Buffer.Length, M_ACPIWMI, M_NOWAIT)) 718194701Srpaulo == NULL) { 719194701Srpaulo AcpiOsFree(out.Pointer); 720194701Srpaulo return (AE_NO_MEMORY); 721194701Srpaulo } 722194701Srpaulo memcpy(ginfo, obj->Buffer.Pointer, obj->Buffer.Length); 723194701Srpaulo for (i = 0; i < wdg_block_count; ++i) { 724194701Srpaulo if ((winfo = malloc(sizeof(struct wmi_info), M_ACPIWMI, 725194701Srpaulo M_NOWAIT | M_ZERO)) == NULL) { 726194701Srpaulo AcpiOsFree(out.Pointer); 727194701Srpaulo free(ginfo, M_ACPIWMI); 728194701Srpaulo return (AE_NO_MEMORY); 729194701Srpaulo } 730194701Srpaulo winfo->ginfo = ginfo[i]; 731194701Srpaulo TAILQ_INSERT_TAIL(&wmi_info_list, winfo, wmi_list); 732194701Srpaulo } 733194701Srpaulo AcpiOsFree(out.Pointer); 734194701Srpaulo free(ginfo, M_ACPIWMI); 735194701Srpaulo 736194701Srpaulo return (status); 737194701Srpaulo} 738194701Srpaulo 739194701Srpaulo/* 740194701Srpaulo * Toggle event generation in for the given GUID (passed by winfo) 741194701Srpaulo * Turn on to get notified (through acpi_wmi_notify_handler) if events happen 742194701Srpaulo * on the given GUID. 743194701Srpaulo */ 744194701Srpaulostatic ACPI_STATUS 745194701Srpauloacpi_wmi_toggle_we_event_generation(device_t dev, struct wmi_info *winfo, 746194701Srpaulo enum event_generation_state state) 747194701Srpaulo{ 748194701Srpaulo char method[5] = "WExx"; 749194701Srpaulo ACPI_OBJECT_LIST input; 750194701Srpaulo ACPI_OBJECT params[1]; 751194701Srpaulo struct acpi_wmi_softc *sc; 752194701Srpaulo ACPI_STATUS status; 753194701Srpaulo 754194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 755194701Srpaulo 756194701Srpaulo sc = device_get_softc(dev); 757194701Srpaulo ACPI_SERIAL_ASSERT(acpi_wmi); 758194701Srpaulo params[0].Type = ACPI_TYPE_INTEGER; 759194701Srpaulo params[0].Integer.Value = state==EVENT_GENERATION_ON?1:0; 760194701Srpaulo input.Pointer = params; 761194701Srpaulo input.Count = 1; 762194701Srpaulo 763194701Srpaulo UINT8 hi = ((UINT8) winfo->ginfo.oid[0]) >> 4; 764194701Srpaulo UINT8 lo = ((UINT8) winfo->ginfo.oid[0]) & 0xf; 765194701Srpaulo method[2] = (hi > 9 ? hi + 55: hi + 48); 766194701Srpaulo method[3] = (lo > 9 ? lo + 55: lo + 48); 767194701Srpaulo status = AcpiEvaluateObject(sc->wmi_handle, method, &input, NULL); 768194701Srpaulo if (status == AE_NOT_FOUND) status = AE_OK; 769194701Srpaulo 770194701Srpaulo return (status); 771194701Srpaulo} 772194701Srpaulo 773194701Srpaulo/* 774194701Srpaulo * Convert given two digit hex string (hexin) to an UINT8 referenced 775194701Srpaulo * by byteout. 776194701Srpaulo * Return != 0 if the was a problem (invalid input) 777194701Srpaulo */ 778194701Srpaulostatic __inline int acpi_wmi_hex_to_int(const UINT8 *hexin, UINT8 *byteout) 779194701Srpaulo{ 780194701Srpaulo unsigned int hi; 781194701Srpaulo unsigned int lo; 782194701Srpaulo 783194701Srpaulo hi = hexin[0]; 784194701Srpaulo lo = hexin[1]; 785194701Srpaulo if ('0' <= hi && hi <= '9') 786194701Srpaulo hi -= '0'; 787194701Srpaulo else if ('A' <= hi && hi <= 'F') 788194701Srpaulo hi -= ('A' - 10); 789194701Srpaulo else if ('a' <= hi && hi <= 'f') 790194701Srpaulo hi -= ('a' - 10); 791194701Srpaulo else 792194701Srpaulo return (1); 793194701Srpaulo if ('0' <= lo && lo <= '9') 794194701Srpaulo lo -= '0'; 795194701Srpaulo else if ('A' <= lo && lo <= 'F') 796194701Srpaulo lo -= ('A' - 10); 797194701Srpaulo else if ('a' <= lo && lo <= 'f') 798194701Srpaulo lo -= ('a' - 10); 799194701Srpaulo else 800194701Srpaulo return (1); 801194701Srpaulo *byteout = (hi << 4) + lo; 802194701Srpaulo 803194701Srpaulo return (0); 804194701Srpaulo} 805194701Srpaulo 806194701Srpaulo/* 807194701Srpaulo * Convert a human readable 36 character GUID into a 16byte 808194701Srpaulo * machine readable one. 809194701Srpaulo * The basic algorithm looks as follows: 810194701Srpaulo * Input: AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 811194701Srpaulo * Output: DCBAFEHGIJKLMNOP 812194701Srpaulo * (AA BB CC etc. represent two digit hex numbers == bytes) 813194701Srpaulo * Return != 0 if passed guid string is invalid 814194701Srpaulo */ 815194701Srpaulostatic int 816194701Srpauloacpi_wmi_guid_string_to_guid(const UINT8 *guid_string, UINT8 *guid) 817194701Srpaulo{ 818194701Srpaulo static const int mapping[20] = {3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 819194701Srpaulo 8, 9, -1, 10, 11, 12, 13, 14, 15}; 820194701Srpaulo int i; 821194701Srpaulo 822194701Srpaulo for (i = 0; i < 20; ++i, ++guid_string) { 823194701Srpaulo if (mapping[i] >= 0) { 824194701Srpaulo if (acpi_wmi_hex_to_int(guid_string, 825194701Srpaulo &guid[mapping[i]])) 826194701Srpaulo return (-1); 827194701Srpaulo ++guid_string; 828194701Srpaulo } else if (*guid_string != '-') 829194701Srpaulo return (-1); 830194701Srpaulo } 831194701Srpaulo 832194701Srpaulo return (0); 833194701Srpaulo} 834194701Srpaulo 835194701Srpaulo/* 836194701Srpaulo * Lookup a wmi_info structure in wmi_list based on a 837194701Srpaulo * human readable GUID 838194701Srpaulo * Return NULL if the GUID is unknown in the _WDG 839194701Srpaulo */ 840194701Srpaulostatic struct wmi_info* 841194701Srpauloacpi_wmi_lookup_wmi_info_by_guid_string(const char *guid_string) 842194701Srpaulo{ 843194701Srpaulo char guid[16]; 844194701Srpaulo struct wmi_info *winfo; 845194701Srpaulo 846194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 847194701Srpaulo 848194701Srpaulo ACPI_SERIAL_ASSERT(acpi_wmi); 849194701Srpaulo 850194701Srpaulo if (!acpi_wmi_guid_string_to_guid(guid_string, guid)) { 851194701Srpaulo TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 852194701Srpaulo if (!memcmp(winfo->ginfo.guid, guid, 16)) { 853194701Srpaulo return (winfo); 854194701Srpaulo } 855194701Srpaulo } 856194701Srpaulo } 857194701Srpaulo 858194701Srpaulo return (NULL); 859194701Srpaulo} 860194701Srpaulo 861194701Srpaulo/* 862194701Srpaulo * open wmistat device 863194701Srpaulo */ 864194701Srpaulostatic int 865194701Srpauloacpi_wmi_wmistat_open(struct cdev* dev, int flags, int mode, struct thread *td) 866194701Srpaulo{ 867194701Srpaulo struct acpi_wmi_softc *sc; 868194701Srpaulo int ret; 869194701Srpaulo 870194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 871194701Srpaulo return (EBADF); 872194701Srpaulo sc = dev->si_drv1; 873194701Srpaulo 874194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 875194701Srpaulo if (sc->wmistat_open_pid != 0) { 876194701Srpaulo ret = EBUSY; 877194701Srpaulo } 878194701Srpaulo else { 879194701Srpaulo if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 880194701Srpaulo == NULL) { 881194701Srpaulo ret = ENXIO; 882194701Srpaulo } else { 883194701Srpaulo sc->wmistat_open_pid = td->td_proc->p_pid; 884194701Srpaulo sc->wmistat_bufptr = 0; 885194701Srpaulo ret = 0; 886194701Srpaulo } 887194701Srpaulo } 888194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 889194701Srpaulo 890194701Srpaulo return (ret); 891194701Srpaulo} 892194701Srpaulo 893194701Srpaulo/* 894194701Srpaulo * close wmistat device 895194701Srpaulo */ 896194701Srpaulostatic int 897194701Srpauloacpi_wmi_wmistat_close(struct cdev* dev, int flags, int mode, 898194701Srpaulo struct thread *td) 899194701Srpaulo{ 900194701Srpaulo struct acpi_wmi_softc *sc; 901194701Srpaulo int ret; 902194701Srpaulo 903194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 904194701Srpaulo return (EBADF); 905194701Srpaulo sc = dev->si_drv1; 906194701Srpaulo 907194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 908194701Srpaulo if (sc->wmistat_open_pid == 0) { 909194701Srpaulo ret = EBADF; 910194701Srpaulo } 911194701Srpaulo else { 912194701Srpaulo if (sc->wmistat_bufptr != -1) { 913194701Srpaulo sbuf_delete(&sc->wmistat_sbuf); 914194701Srpaulo sc->wmistat_bufptr = -1; 915194701Srpaulo } 916194701Srpaulo sc->wmistat_open_pid = 0; 917194701Srpaulo ret = 0; 918194701Srpaulo } 919194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 920194701Srpaulo 921194701Srpaulo return (ret); 922194701Srpaulo} 923194701Srpaulo 924194701Srpaulo/* 925194701Srpaulo * Read from wmistat guid information 926194701Srpaulo */ 927194701Srpaulostatic int 928194701Srpauloacpi_wmi_wmistat_read(struct cdev *dev, struct uio *buf, int flag) 929194701Srpaulo{ 930194701Srpaulo struct acpi_wmi_softc *sc; 931194701Srpaulo struct wmi_info *winfo; 932194701Srpaulo int l; 933194701Srpaulo int ret; 934194701Srpaulo UINT8* guid; 935194701Srpaulo 936194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 937194701Srpaulo return (EBADF); 938194701Srpaulo sc = dev->si_drv1; 939194701Srpaulo 940194701Srpaulo ACPI_SERIAL_BEGIN(acpi_wmi); 941194701Srpaulo if (sc->wmistat_open_pid != buf->uio_td->td_proc->p_pid || 942194701Srpaulo sc->wmistat_bufptr == -1) { 943194701Srpaulo ret = EBADF; 944194701Srpaulo } 945194701Srpaulo else { 946194701Srpaulo if (!sbuf_done(&sc->wmistat_sbuf)) { 947194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, "GUID " 948194701Srpaulo " INST EXPE METH STR " 949194701Srpaulo "EVENT OID\n"); 950194701Srpaulo TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) { 951194701Srpaulo guid = (UINT8*)winfo->ginfo.guid; 952194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 953194701Srpaulo "{%02X%02X%02X%02X-%02X%02X-" 954194701Srpaulo "%02X%02X-%02X%02X-%02X%02X" 955194701Srpaulo "%02X%02X%02X%02X} %3d %-5s", 956194701Srpaulo guid[3], guid[2], guid[1], guid[0], 957194701Srpaulo guid[5], guid[4], 958194701Srpaulo guid[7], guid[6], 959194701Srpaulo guid[8], guid[9], 960194701Srpaulo guid[10], guid[11], guid[12], 961194701Srpaulo guid[13], guid[14], guid[15], 962194701Srpaulo winfo->ginfo.max_instance, 963194701Srpaulo (winfo->ginfo.flags& 964194701Srpaulo ACPI_WMI_REGFLAG_EXPENSIVE)? 965194701Srpaulo "YES":"NO" 966194701Srpaulo ); 967194701Srpaulo if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD) 968194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 969194701Srpaulo "WM%c%c ", 970194701Srpaulo winfo->ginfo.oid[0], 971194701Srpaulo winfo->ginfo.oid[1]); 972194701Srpaulo else 973194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, "NO "); 974194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, "%-4s", 975194701Srpaulo (winfo->ginfo.flags& 976194701Srpaulo ACPI_WMI_REGFLAG_STRING)?"YES":"NO" 977194701Srpaulo ); 978194701Srpaulo if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT) 979194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 980194701Srpaulo "0x%02X%s -\n", 981194701Srpaulo (UINT8)winfo->ginfo.oid[0], 982194701Srpaulo winfo->event_handler==NULL? 983194701Srpaulo " ":"+"); 984194701Srpaulo else 985194701Srpaulo sbuf_printf(&sc->wmistat_sbuf, 986194701Srpaulo "NO %c%c\n", 987194701Srpaulo winfo->ginfo.oid[0], 988194701Srpaulo winfo->ginfo.oid[1]); 989194701Srpaulo } 990194701Srpaulo sbuf_finish(&sc->wmistat_sbuf); 991194701Srpaulo } 992194701Srpaulo if (sbuf_len(&sc->wmistat_sbuf) <= 0) { 993194701Srpaulo sbuf_delete(&sc->wmistat_sbuf); 994194701Srpaulo sc->wmistat_bufptr = -1; 995194701Srpaulo sc->wmistat_open_pid = 0; 996194701Srpaulo ret = ENOMEM; 997194701Srpaulo } else { 998194701Srpaulo l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) - 999194701Srpaulo sc->wmistat_bufptr); 1000194701Srpaulo ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) + 1001194701Srpaulo sc->wmistat_bufptr, l, buf) : 0; 1002194701Srpaulo sc->wmistat_bufptr += l; 1003194701Srpaulo } 1004194701Srpaulo } 1005194701Srpaulo ACPI_SERIAL_END(acpi_wmi); 1006194701Srpaulo 1007194701Srpaulo return (ret); 1008194701Srpaulo} 1009