From 1baf45b64bf05bb58c5f1b0d191aec1e9c13c07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 15 Apr 2020 19:37:07 +0200 Subject: [PATCH] wmi: add ACPI WMI implementation add a WMI Asus driver, to control keyboard backlight brightness. Change-Id: Ib86f70b4a407178b0a1f532269387a55915cc460 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2485 Reviewed-by: Fredrik Holmqvist Reviewed-by: Adrien Destugues --- headers/private/wmi/wmi.h | 43 ++ src/add-ons/kernel/drivers/Jamfile | 2 + src/add-ons/kernel/drivers/wmi/Jamfile | 11 + src/add-ons/kernel/drivers/wmi/WMIACPI.cpp | 485 +++++++++++++++++++ src/add-ons/kernel/drivers/wmi/WMIAsus.cpp | 272 +++++++++++ src/add-ons/kernel/drivers/wmi/WMIDevice.cpp | 200 ++++++++ src/add-ons/kernel/drivers/wmi/WMIPrivate.h | 142 ++++++ 7 files changed, 1155 insertions(+) create mode 100644 headers/private/wmi/wmi.h create mode 100644 src/add-ons/kernel/drivers/wmi/Jamfile create mode 100644 src/add-ons/kernel/drivers/wmi/WMIACPI.cpp create mode 100644 src/add-ons/kernel/drivers/wmi/WMIAsus.cpp create mode 100644 src/add-ons/kernel/drivers/wmi/WMIDevice.cpp create mode 100644 src/add-ons/kernel/drivers/wmi/WMIPrivate.h diff --git a/headers/private/wmi/wmi.h b/headers/private/wmi/wmi.h new file mode 100644 index 0000000000..b82676dc2e --- /dev/null +++ b/headers/private/wmi/wmi.h @@ -0,0 +1,43 @@ +/* + * Copyright 2020, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _WMI_H_ +#define _WMI_H_ + + +#include +#include +#include + + + +// Device node + +// guid (string) +#define WMI_GUID_STRING_ITEM "wmi/guid_string" + +// node type +#define WMI_DEVICE_TYPE_NAME "wmi/device/v1" + +// device cookie, issued by wmi bus manager +typedef void* wmi_device; + + +// bus manager device interface for peripheral driver +typedef struct { + driver_module_info info; + + status_t (*evaluate_method)(wmi_device device, uint8 instance, + uint32 methodId, const acpi_data* in, acpi_data* out); + status_t (*install_event_handler)(wmi_device device, + const char* guidString, acpi_notify_handler handler, void* context); + status_t (*remove_event_handler)(wmi_device device, + const char* guidString); + status_t (*get_event_data)(wmi_device device, uint32 notify, + acpi_data* out); + const char* (*get_uid)(wmi_device device); +} wmi_device_interface; + + +#endif /* _WMI_H_ */ diff --git a/src/add-ons/kernel/drivers/Jamfile b/src/add-ons/kernel/drivers/Jamfile index 887bd51522..7bbda5d0ea 100644 --- a/src/add-ons/kernel/drivers/Jamfile +++ b/src/add-ons/kernel/drivers/Jamfile @@ -18,3 +18,5 @@ SubInclude HAIKU_TOP src add-ons kernel drivers power ; SubInclude HAIKU_TOP src add-ons kernel drivers timer ; SubInclude HAIKU_TOP src add-ons kernel drivers tty ; SubInclude HAIKU_TOP src add-ons kernel drivers video ; +SubInclude HAIKU_TOP src add-ons kernel drivers wmi ; + diff --git a/src/add-ons/kernel/drivers/wmi/Jamfile b/src/add-ons/kernel/drivers/wmi/Jamfile new file mode 100644 index 0000000000..dcb773e130 --- /dev/null +++ b/src/add-ons/kernel/drivers/wmi/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src add-ons kernel drivers wmi ; + +UsePrivateHeaders wmi ; +UsePrivateKernelHeaders ; + +KernelAddon wmi : + WMIACPI.cpp + WMIAsus.cpp + WMIDevice.cpp + ; + diff --git a/src/add-ons/kernel/drivers/wmi/WMIACPI.cpp b/src/add-ons/kernel/drivers/wmi/WMIACPI.cpp new file mode 100644 index 0000000000..4d9c3c496b --- /dev/null +++ b/src/add-ons/kernel/drivers/wmi/WMIACPI.cpp @@ -0,0 +1,485 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT license. + */ + + +#define DRIVER_NAME "wmi_acpi" +#include "WMIPrivate.h" + + +#define ACPI_NAME_ACPI_WMI "PNP0C14" + +#define ACPI_WMI_REGFLAG_EXPENSIVE (1 << 0) +#define ACPI_WMI_REGFLAG_METHOD (1 << 1) +#define ACPI_WMI_REGFLAG_STRING (1 << 2) +#define ACPI_WMI_REGFLAG_EVENT (1 << 3) + + +device_manager_info *gDeviceManager; + + +acpi_status wmi_acpi_adr_space_handler(uint32 function, + acpi_physical_address address, uint32 bitWidth, int *value, + void *handlerContext, void *regionContext) +{ + return B_OK; +} + + +WMIACPI::WMIACPI(device_node *node) + : + fNode(node) +{ + CALLED(); + + device_node *parent; + parent = gDeviceManager->get_parent_node(node); + gDeviceManager->get_driver(parent, (driver_module_info **)&acpi, + (void **)&acpi_cookie); + gDeviceManager->get_attr_string(parent, ACPI_DEVICE_UID_ITEM, &fUid, + false); + gDeviceManager->put_node(parent); + + // install notify handler + fStatus = acpi->install_notify_handler(acpi_cookie, + ACPI_ALL_NOTIFY, _NotifyHandler, this); + if (fStatus != B_OK) { + ERROR("install_notify_handler failed\n"); + return; + } + + fStatus = acpi->install_address_space_handler(acpi_cookie, + ACPI_ADR_SPACE_EC, wmi_acpi_adr_space_handler, NULL, this); + if (fStatus != B_OK) { + ERROR("wmi_acpi_adr_space_handler failed\n"); + return; + } + + acpi_data buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + fStatus = acpi->evaluate_method(acpi_cookie, "_WDG", NULL, &buffer); + if (fStatus != B_OK) { + ERROR("Method call _WDG failed\n"); + return; + } + + acpi_object_type* object = (acpi_object_type*)buffer.pointer; + fWMIInfoCount = object->buffer.length / sizeof(guid_info); + guid_info *info = (guid_info*)object->buffer.buffer; + fWMIInfos = (wmi_info *)calloc(fWMIInfoCount, sizeof(wmi_info)); + TRACE("found %" B_PRIu32 " objects\n", fWMIInfoCount); + for (uint32 i = 0; i < fWMIInfoCount; i++, info++) { + wmi_info *wmi = &fWMIInfos[i]; + wmi->guid = *info; + fList.Add(wmi); + } + free(object); +} + + +WMIACPI::~WMIACPI() +{ + free(fWMIInfos); + + acpi->remove_notify_handler(acpi_cookie, + ACPI_ALL_NOTIFY, _NotifyHandler); +} + + +status_t +WMIACPI::InitCheck() +{ + return fStatus; +} + + +status_t +WMIACPI::Scan() +{ + CALLED(); + status_t status; + wmi_info* wmiInfo = NULL; + uint32 index = 0; + for (WMIInfoList::Iterator it = fList.GetIterator(); + (wmiInfo = it.Next()) != NULL; index++) { + uint8* guid = wmiInfo->guid.guid; + char guidString[37] = {}; + _GuidToGuidString(guid, guidString); + device_attr attrs[] = { + // connection + { WMI_GUID_STRING_ITEM, B_STRING_TYPE, { string: guidString }}, + + { WMI_BUS_COOKIE, B_UINT32_TYPE, { ui32: index }}, + + // description of peripheral drivers + { B_DEVICE_BUS, B_STRING_TYPE, { string: "wmi" }}, + + { B_DEVICE_FLAGS, B_UINT32_TYPE, + { ui32: B_FIND_MULTIPLE_CHILDREN }}, + + { NULL } + }; + + status = gDeviceManager->register_node(fNode, WMI_DEVICE_MODULE_NAME, + attrs, NULL, NULL); + if (status != B_OK) + return status; + } + + return B_OK; + +} + + +status_t +WMIACPI::GetBlock(uint32 busCookie, uint8 instance, uint32 methodId, + acpi_data* out) +{ + CALLED(); + if (busCookie >= fWMIInfoCount) + return B_BAD_VALUE; + wmi_info* info = &fWMIInfos[busCookie]; + if ((info->guid.flags & ACPI_WMI_REGFLAG_METHOD) != 0 + || (info->guid.flags & ACPI_WMI_REGFLAG_EVENT) != 0) { + return B_BAD_VALUE; + } else if (instance > info->guid.max_instance) + return B_BAD_VALUE; + + char method[5] = "WQ"; + strncat(method, info->guid.oid, 2); + char wcMethod[5] = "WC"; + strncat(wcMethod, info->guid.oid, 2); + status_t wcStatus = B_OK; + status_t status = B_OK; + + if ((info->guid.flags & ACPI_WMI_REGFLAG_EXPENSIVE) != 0) + wcStatus = _EvaluateMethodSimple(wcMethod, 1); + + acpi_object_type object; + object.object_type = ACPI_TYPE_INTEGER; + object.integer.integer = instance; + acpi_objects objects = { 1, &object}; + TRACE("GetBlock calling %s\n", method); + status = acpi->evaluate_method(acpi_cookie, method, &objects, out); + + if ((info->guid.flags & ACPI_WMI_REGFLAG_EXPENSIVE) != 0 + && wcStatus == B_OK) { + _EvaluateMethodSimple(wcMethod, 0); + } + + return status; +} + + +status_t +WMIACPI::SetBlock(uint32 busCookie, uint8 instance, uint32 methodId, + const acpi_data* in) +{ + CALLED(); + if (busCookie >= fWMIInfoCount) + return B_BAD_VALUE; + wmi_info* info = &fWMIInfos[busCookie]; + if ((info->guid.flags & ACPI_WMI_REGFLAG_METHOD) != 0 + || (info->guid.flags & ACPI_WMI_REGFLAG_EVENT) != 0) { + return B_BAD_VALUE; + } else if (instance > info->guid.max_instance) + return B_BAD_VALUE; + + char method[5] = "WS"; + strncat(method, info->guid.oid, 2); + + acpi_object_type object[2]; + object[0].object_type = ACPI_TYPE_INTEGER; + object[0].integer.integer = instance; + object[1].object_type = ACPI_TYPE_BUFFER; + if ((info->guid.flags & ACPI_WMI_REGFLAG_STRING) != 0) + object[1].object_type = ACPI_TYPE_STRING; + object[1].buffer.buffer = in->pointer; + object[1].buffer.length = in->length; + acpi_objects objects = { 2, object}; + TRACE("SetBlock calling %s\n", method); + return acpi->evaluate_method(acpi_cookie, method, &objects, NULL); +} + + +status_t +WMIACPI::EvaluateMethod(uint32 busCookie, uint8 instance, uint32 methodId, + const acpi_data* in, acpi_data* out) +{ + CALLED(); + if (busCookie >= fWMIInfoCount) + return B_BAD_VALUE; + wmi_info* info = &fWMIInfos[busCookie]; + if ((info->guid.flags & ACPI_WMI_REGFLAG_METHOD) == 0) + return B_BAD_VALUE; + + char method[5] = "WM"; + strncat(method, info->guid.oid, 2); + + acpi_object_type object[3]; + object[0].object_type = ACPI_TYPE_INTEGER; + object[0].integer.integer = instance; + object[1].object_type = ACPI_TYPE_INTEGER; + object[1].integer.integer = methodId; + uint32 count = 2; + if (in != NULL) { + object[2].object_type = ACPI_TYPE_BUFFER; + if ((info->guid.flags & ACPI_WMI_REGFLAG_STRING) != 0) + object[2].object_type = ACPI_TYPE_STRING; + object[2].buffer.buffer = in->pointer; + object[2].buffer.length = in->length; + count++; + } + acpi_objects objects = { count, object}; + TRACE("EvaluateMethod calling %s\n", method); + return acpi->evaluate_method(acpi_cookie, method, &objects, out); +} + + +status_t +WMIACPI::InstallEventHandler(const char* guidString, + acpi_notify_handler handler, void* context) +{ + CALLED(); + char string[37] = {}; + for (uint32 i = 0; i < fWMIInfoCount; i++) { + wmi_info* info = &fWMIInfos[i]; + _GuidToGuidString(info->guid.guid, string); + if (strcmp(guidString, string) == 0) { + status_t status = B_OK; + if (info->handler == NULL) + status = _SetEventGeneration(info, true); + if (status == B_OK) { + info->handler = handler; + info->handler_context = context; + } + return status; + } + } + return B_ENTRY_NOT_FOUND; +} + + +status_t +WMIACPI::RemoveEventHandler(const char* guidString) +{ + CALLED(); + char string[37] = {}; + for (uint32 i = 0; i < fWMIInfoCount; i++) { + wmi_info* info = &fWMIInfos[i]; + _GuidToGuidString(info->guid.guid, string); + if (strcmp(guidString, string) == 0) { + status_t status = _SetEventGeneration(info, false); + info->handler = NULL; + info->handler_context = NULL; + return status; + } + } + return B_ENTRY_NOT_FOUND; +} + + +status_t +WMIACPI::GetEventData(uint32 notify, acpi_data* out) +{ + CALLED(); + + acpi_object_type object; + object.object_type = ACPI_TYPE_INTEGER; + object.integer.integer = notify; + acpi_objects objects = { 1, &object }; + + for (uint32 i = 0; i < fWMIInfoCount; i++) { + wmi_info* info = &fWMIInfos[i]; + if (info->guid.notify_id == notify + && (info->guid.flags & ACPI_WMI_REGFLAG_EVENT) != 0) { + return acpi->evaluate_method(acpi_cookie, "_WED", &objects, out); + } + } + return B_ENTRY_NOT_FOUND; +} + + +const char* +WMIACPI::GetUid(uint32 busCookie) +{ + return fUid; +} + + +status_t +WMIACPI::_SetEventGeneration(wmi_info* info, bool enabled) +{ + char method[5]; + sprintf(method, "WE%02X", info->guid.notify_id); + TRACE("_SetEventGeneration calling %s\n", method); + status_t status = _EvaluateMethodSimple(method, enabled ? 1 : 0); + // the method is allowed not to exist + if (status == B_ERROR) + status = B_OK; + return status; +} + + +status_t +WMIACPI::_EvaluateMethodSimple(const char* method, uint64 integer) +{ + acpi_object_type object; + object.object_type = ACPI_TYPE_INTEGER; + object.integer.integer = integer; + acpi_objects objects = { 1, &object}; + return acpi->evaluate_method(acpi_cookie, method, &objects, NULL); +} + + +void +WMIACPI::_NotifyHandler(acpi_handle device, uint32 value, void *context) +{ + WMIACPI* bus = (WMIACPI*)context; + bus->_Notify(device, value); +} + + +void +WMIACPI::_Notify(acpi_handle device, uint32 value) +{ + for (uint32 i = 0; i < fWMIInfoCount; i++) { + wmi_info* wmi = &fWMIInfos[i]; + if (wmi->guid.notify_id == value) { + TRACE("_Notify found event 0x%" B_PRIx32 "\n", value); + if (wmi->handler != NULL) { + TRACE("_Notify found handler for event 0x%" B_PRIx32 "\n", + value); + wmi->handler(device, value, wmi->handler_context); + } + break; + } + } +} + + +void +WMIACPI::_GuidToGuidString(uint8 guid[16], char* guidString) +{ + sprintf(guidString, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + guid[3], guid[2], guid[1], guid[0], guid[5], guid[4], guid[7], guid[6], + guid[8], guid[9], guid[10], guid[11], guid[12], guid[13], guid[14], + guid[15]); +} + + +// #pragma mark - driver module API + + +static float +wmi_acpi_support(device_node *parent) +{ + CALLED(); + + // make sure parent is really the ACPI bus manager + const char *bus; + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return -1; + + if (strcmp(bus, "acpi")) + return 0.0; + + // check whether it's really a device + uint32 device_type; + if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, + &device_type, false) != B_OK + || device_type != ACPI_TYPE_DEVICE) { + return 0.0; + } + + // check whether it's an acpi wmi device + const char *name; + if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name, + false) != B_OK || strcmp(name, ACPI_NAME_ACPI_WMI) != 0) { + return 0.0; + } + + TRACE("found an acpi wmi device\n"); + + return 0.6; +} + + +static status_t +wmi_acpi_register_device(device_node *node) +{ + CALLED(); + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "WMI ACPI" }}, + { NULL } + }; + + return gDeviceManager->register_node(node, WMI_ACPI_DRIVER_NAME, attrs, + NULL, NULL); +} + + +static status_t +wmi_acpi_init_driver(device_node *node, void **driverCookie) +{ + CALLED(); + WMIACPI* device = new(std::nothrow) WMIACPI(node); + if (device == NULL) + return B_NO_MEMORY; + + *driverCookie = device; + + return B_OK; +} + + +static void +wmi_acpi_uninit_driver(void *driverCookie) +{ + CALLED(); + WMIACPI *device = (WMIACPI*)driverCookie; + + delete device; +} + + +static status_t +wmi_acpi_register_child_devices(void *cookie) +{ + CALLED(); + WMIACPI *device = (WMIACPI*)cookie; + return device->Scan(); +} + + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, + {} +}; + + +static driver_module_info sWMIACPIDriverModule = { + { + WMI_ACPI_DRIVER_NAME, + 0, + NULL + }, + + wmi_acpi_support, + wmi_acpi_register_device, + wmi_acpi_init_driver, + wmi_acpi_uninit_driver, + wmi_acpi_register_child_devices, + NULL, // rescan + NULL, // removed +}; + + +module_info *modules[] = { + (module_info *)&sWMIACPIDriverModule, + (module_info *)&gWMIAsusDriverModule, + (module_info *)&gWMIDeviceModule, + NULL +}; diff --git a/src/add-ons/kernel/drivers/wmi/WMIAsus.cpp b/src/add-ons/kernel/drivers/wmi/WMIAsus.cpp new file mode 100644 index 0000000000..80d84eb8a7 --- /dev/null +++ b/src/add-ons/kernel/drivers/wmi/WMIAsus.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT license. + */ + + +#define DRIVER_NAME "wmi_asus" +#include "WMIPrivate.h" + + +#define ACPI_ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" +#define ACPI_ASUS_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" +#define ACPI_ASUS_UID_ASUSWMI "ASUSWMI" + +#define ASUS_WMI_METHODID_INIT 0x54494E49 +#define ASUS_WMI_METHODID_SPEC 0x43455053 +#define ASUS_WMI_METHODID_SFUN 0x4E554653 +#define ASUS_WMI_METHODID_DCTS 0x53544344 +#define ASUS_WMI_METHODID_DSTS 0x53545344 +#define ASUS_WMI_METHODID_DEVS 0x53564544 + +#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 +#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 + + +class WMIAsus { +public: + WMIAsus(device_node *node); + ~WMIAsus(); + +private: + status_t _EvaluateMethod(uint32 methodId, uint32 arg0, + uint32 arg1, uint32 *returnValue); + status_t _GetDevState(uint32 devId, uint32* value); + status_t _SetDevState(uint32 devId, uint32 arg, + uint32* value); + static void _NotifyHandler(acpi_handle handle, + uint32 notify, void *context); + void _Notify(acpi_handle handle, uint32 notify); +private: + device_node* fNode; + wmi_device_interface* wmi; + wmi_device wmi_cookie; + uint32 fDstsID; + const char* fEventGuidString; +}; + + + +WMIAsus::WMIAsus(device_node *node) + : + fNode(node), + fDstsID(ASUS_WMI_METHODID_DSTS), + fEventGuidString(NULL) +{ + CALLED(); + + device_node *parent; + parent = gDeviceManager->get_parent_node(node); + gDeviceManager->get_driver(parent, (driver_module_info **)&wmi, + (void **)&wmi_cookie); + + gDeviceManager->put_node(parent); + + const char* uid = wmi->get_uid(wmi_cookie); + if (uid != NULL && strcmp(uid, ACPI_ASUS_UID_ASUSWMI) == 0) + fDstsID = ASUS_WMI_METHODID_DCTS; + TRACE("WMIAsus using _UID %s\n", uid); + uint32 value; + if (_EvaluateMethod(ASUS_WMI_METHODID_INIT, 0, 0, &value) == B_OK) { + TRACE("_INIT: %x\n", value); + } + if (_EvaluateMethod(ASUS_WMI_METHODID_SPEC, 0, 9, &value) == B_OK) { + TRACE("_SPEC: %x\n", value); + } + if (_EvaluateMethod(ASUS_WMI_METHODID_SFUN, 0, 0, &value) == B_OK) { + TRACE("_SFUN: %x\n", value); + } + + // install event handler + if (wmi->install_event_handler(wmi_cookie, ACPI_ASUS_WMI_EVENT_GUID, + _NotifyHandler, this) == B_OK) { + fEventGuidString = ACPI_ASUS_WMI_EVENT_GUID; + } +} + + +WMIAsus::~WMIAsus() +{ + if (fEventGuidString != NULL) + wmi->remove_event_handler(wmi_cookie, fEventGuidString); +} + + +status_t +WMIAsus::_EvaluateMethod(uint32 methodId, uint32 arg0, uint32 arg1, + uint32 *returnValue) +{ + CALLED(); + uint32 params[] = { arg0, arg1 }; + acpi_data inBuffer = { sizeof(params), params }; + acpi_data outBuffer = { ACPI_ALLOCATE_BUFFER, NULL }; + status_t status = wmi->evaluate_method(wmi_cookie, 0, methodId, &inBuffer, + &outBuffer); + if (status != B_OK) + return status; + + acpi_object_type* object = (acpi_object_type*)outBuffer.pointer; + uint32 result = 0; + if (object != NULL) { + if (object->object_type == ACPI_TYPE_INTEGER) + result = object->integer.integer; + free(object); + } + if (returnValue != NULL) + *returnValue = result; + + return B_OK; +} + + +status_t +WMIAsus::_GetDevState(uint32 devId, uint32 *value) +{ + return _EvaluateMethod(fDstsID, devId, 0, value); +} + + +status_t +WMIAsus::_SetDevState(uint32 devId, uint32 arg, uint32 *value) +{ + return _EvaluateMethod(ASUS_WMI_METHODID_DEVS, devId, arg, value); +} + + +void +WMIAsus::_NotifyHandler(acpi_handle handle, uint32 notify, void *context) +{ + WMIAsus* device = (WMIAsus*)context; + device->_Notify(handle, notify); +} + + +void +WMIAsus::_Notify(acpi_handle handle, uint32 notify) +{ + CALLED(); + + acpi_data response = { ACPI_ALLOCATE_BUFFER, NULL }; + wmi->get_event_data(wmi_cookie, notify, &response); + + acpi_object_type* object = (acpi_object_type*)response.pointer; + uint32 result = 0; + if (object != NULL) { + if (object->object_type == ACPI_TYPE_INTEGER) + result = object->integer.integer; + free(object); + } + if (result != 0) { + if (result == 0xc4 || result == 0xc5) { + TRACE("WMIAsus::_Notify() keyboard backlight key\n"); + uint32 value; + if (_GetDevState(ASUS_WMI_DEVID_KBD_BACKLIGHT, &value) == B_OK) { + TRACE("WMIAsus::_Notify() getkeyboard backlight key %" + B_PRIx32 "\n", value); + value &= 0x7; + if (result == 0xc4) { + if (value < 3) + value++; + } else if (value > 0) + value--; + TRACE("WMIAsus::_Notify() set keyboard backlight key %" + B_PRIx32 "\n", value); + _SetDevState(ASUS_WMI_DEVID_KBD_BACKLIGHT, value | 0x80, NULL); + } + } else if (result == 0x6b) { + TRACE("WMIAsus::_Notify() touchpad control\n"); + } else { + TRACE("WMIAsus::_Notify() key 0x%" B_PRIx32 "\n", result); + } + } +} + + +// #pragma mark - driver module API + + +static float +wmi_asus_support(device_node *parent) +{ + // make sure parent is really a wmi device + const char *bus; + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return -1; + + if (strcmp(bus, "wmi")) + return 0.0; + + // check whether it's an asus wmi device + const char *guid; + if (gDeviceManager->get_attr_string(parent, WMI_GUID_STRING_ITEM, &guid, + false) != B_OK || strcmp(guid, ACPI_ASUS_WMI_MGMT_GUID) != 0) { + return 0.0; + } + + TRACE("found an asus wmi device\n"); + + return 0.6; +} + + +static status_t +wmi_asus_register_device(device_node *node) +{ + CALLED(); + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "WMI ASUS" }}, + { NULL } + }; + + return gDeviceManager->register_node(node, WMI_ASUS_DRIVER_NAME, attrs, + NULL, NULL); +} + + +static status_t +wmi_asus_init_driver(device_node *node, void **driverCookie) +{ + CALLED(); + + WMIAsus* device = new(std::nothrow) WMIAsus(node); + if (device == NULL) + return B_NO_MEMORY; + *driverCookie = device; + + return B_OK; +} + + +static void +wmi_asus_uninit_driver(void *driverCookie) +{ + CALLED(); + WMIAsus* device = (WMIAsus*)driverCookie; + delete device; +} + + +static status_t +wmi_asus_register_child_devices(void *cookie) +{ + CALLED(); + return B_OK; +} + + +driver_module_info gWMIAsusDriverModule = { + { + WMI_ASUS_DRIVER_NAME, + 0, + NULL + }, + + wmi_asus_support, + wmi_asus_register_device, + wmi_asus_init_driver, + wmi_asus_uninit_driver, + wmi_asus_register_child_devices, + NULL, // rescan + NULL, // removed +}; + diff --git a/src/add-ons/kernel/drivers/wmi/WMIDevice.cpp b/src/add-ons/kernel/drivers/wmi/WMIDevice.cpp new file mode 100644 index 0000000000..582070fc99 --- /dev/null +++ b/src/add-ons/kernel/drivers/wmi/WMIDevice.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT License. + */ + + +#include "WMIPrivate.h" + + +WMIDevice::WMIDevice(device_node *node) + : + fNode(node), + fBus(NULL), + fBusCookie(UINT32_MAX), + fInitStatus(B_OK) +{ + CALLED(); + + { + device_node *parent = gDeviceManager->get_parent_node(node); + gDeviceManager->get_driver(parent, NULL, (void **)&fBus); + gDeviceManager->put_node(parent); + } + + fInitStatus = gDeviceManager->get_attr_uint32(node, WMI_BUS_COOKIE, + &fBusCookie, false); +} + + +WMIDevice::~WMIDevice() +{ +} + + +status_t +WMIDevice::InitCheck() +{ + return fInitStatus; +} + + +status_t +WMIDevice::EvaluateMethod(uint8 instance, uint32 methodId, const acpi_data* in, + acpi_data* out) +{ + CALLED(); + return fBus->EvaluateMethod(fBusCookie, instance, methodId, in, out); +} + + +status_t +WMIDevice::InstallEventHandler(const char* guidString, + acpi_notify_handler handler, void* context) +{ + CALLED(); + if (guidString == NULL || handler == NULL) + return B_BAD_VALUE; + + return fBus->InstallEventHandler(guidString, handler, context); +} + + +status_t +WMIDevice::RemoveEventHandler(const char* guidString) +{ + CALLED(); + if (guidString == NULL) + return B_BAD_VALUE; + + return fBus->RemoveEventHandler(guidString); +} + + +status_t +WMIDevice::GetEventData(uint32 notify, acpi_data* out) +{ + CALLED(); + return fBus->GetEventData(notify, out); +} + + +const char* +WMIDevice::GetUid() +{ + CALLED(); + return fBus->GetUid(fBusCookie); +} + + +// #pragma mark - driver module API + + +static status_t +wmi_init_device(device_node *node, void **_device) +{ + CALLED(); + WMIDevice *device = new(std::nothrow) WMIDevice(node); + if (device == NULL) + return B_NO_MEMORY; + + status_t result = device->InitCheck(); + if (result != B_OK) { + ERROR("failed to set up wmi device object\n"); + return result; + } + + *_device = device; + + return B_OK; +} + + +static void +wmi_uninit_device(void *_device) +{ + CALLED(); + WMIDevice *device = (WMIDevice *)_device; + delete device; +} + + +static status_t +wmi_evaluate_method(wmi_device _device, uint8 instance, uint32 methodId, + const acpi_data* in, acpi_data* out) +{ + WMIDevice *device = (WMIDevice *)_device; + return device->EvaluateMethod(instance, methodId, in, out); +} + + +static status_t +wmi_install_event_handler(wmi_device _device, const char* guidString, + acpi_notify_handler handler, void* context) +{ + WMIDevice *device = (WMIDevice *)_device; + return device->InstallEventHandler(guidString, handler, context); +} + + +static status_t +wmi_remove_event_handler(wmi_device _device, const char* guidString) +{ + WMIDevice *device = (WMIDevice *)_device; + return device->RemoveEventHandler(guidString); +} + + +static status_t +wmi_get_event_data(wmi_device _device, uint32 notify, acpi_data* out) +{ + WMIDevice *device = (WMIDevice *)_device; + return device->GetEventData(notify, out); +} + + +static const char* +wmi_get_uid(wmi_device _device) +{ + WMIDevice *device = (WMIDevice *)_device; + return device->GetUid(); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + return B_ERROR; + } +} + + +wmi_device_interface gWMIDeviceModule = { + { + { + WMI_DEVICE_MODULE_NAME, + 0, + std_ops + }, + + NULL, // supported devices + NULL, // register node + wmi_init_device, + wmi_uninit_device, + NULL, // register child devices + NULL, // rescan + NULL, // device_removed + }, + + wmi_evaluate_method, + wmi_install_event_handler, + wmi_remove_event_handler, + wmi_get_event_data, + wmi_get_uid, +}; diff --git a/src/add-ons/kernel/drivers/wmi/WMIPrivate.h b/src/add-ons/kernel/drivers/wmi/WMIPrivate.h new file mode 100644 index 0000000000..ec8be7f930 --- /dev/null +++ b/src/add-ons/kernel/drivers/wmi/WMIPrivate.h @@ -0,0 +1,142 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT License. + */ +#ifndef WMI_PRIVATE_H +#define WMI_PRIVATE_H + + +#include +#include +#include +#include + +#include +#include +#include + + +//#define WMI_TRACE +#ifndef DRIVER_NAME +# define DRIVER_NAME "wmi" +#endif +#ifdef WMI_TRACE +# define TRACE(x...) dprintf("\33[33m" DRIVER_NAME ":\33[0m " x) +#else +# define TRACE(x...) +#endif +#define TRACE_ALWAYS(x...) dprintf("\33[33m" DRIVER_NAME ":\33[0m " x) +#define ERROR(x...) TRACE_ALWAYS(x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +#define WMI_ACPI_DRIVER_NAME "drivers/wmi/acpi/driver_v1" +#define WMI_DEVICE_MODULE_NAME "drivers/wmi/device/driver_v1" + +#define WMI_ASUS_DRIVER_NAME "drivers/wmi/asus/driver_v1" + +#define WMI_BUS_COOKIE "wmi/bus/index" + + +class WMIACPI; +class WMIDevice; + + +extern device_manager_info *gDeviceManager; +extern wmi_device_interface gWMIDeviceModule; +extern driver_module_info gWMIAsusDriverModule; + + +class WMIDevice { +public: + WMIDevice(device_node* node); + ~WMIDevice(); + + status_t InitCheck(); + status_t EvaluateMethod(uint8 instance, uint32 methodId, + const acpi_data* in, acpi_data* out); + status_t InstallEventHandler(const char* guidString, + acpi_notify_handler handler, void* context); + status_t RemoveEventHandler(const char* guidString); + status_t GetEventData(uint32 notify, acpi_data* out); + const char* GetUid(); +private: + +private: + device_node* fNode; + WMIACPI* fBus; + uint32 fBusCookie; + status_t fInitStatus; +}; + + +typedef struct { + uint8 guid[16]; + union { + char oid[2]; + struct { + uint8 notify_id; + }; + }; + uint8 max_instance; + uint8 flags; +} guid_info; + + +struct wmi_info; +typedef struct wmi_info : DoublyLinkedListLinkImpl { + guid_info guid; + acpi_notify_handler handler; + void* handler_context; +} wmi_info; +typedef DoublyLinkedList WMIInfoList; + + + +class WMIACPI { +public: + WMIACPI(device_node *node); + ~WMIACPI(); + + status_t InitCheck(); + + status_t Scan(); + + status_t GetBlock(uint32 busCookie, + uint8 instance, uint32 methodId, + acpi_data* out); + status_t SetBlock(uint32 busCookie, + uint8 instance, uint32 methodId, + const acpi_data* in); + status_t EvaluateMethod(uint32 busCookie, + uint8 instance, uint32 methodId, + const acpi_data* in, acpi_data* out); + status_t InstallEventHandler(const char* guidString, + acpi_notify_handler handler, void* context); + status_t RemoveEventHandler(const char* guidString); + status_t GetEventData(uint32 notify, acpi_data* out); + const char* GetUid(uint32 busCookie); +private: + status_t _SetEventGeneration(wmi_info* info, + bool enabled); + status_t _EvaluateMethodSimple(const char* method, + uint64 integer); + void _Notify(acpi_handle device, uint32 value); + static void _NotifyHandler(acpi_handle device, + uint32 value, void *context); + + void _GuidToGuidString(uint8 guid[16], + char* guidString); +private: + device_node* fNode; + acpi_device_module_info* acpi; + acpi_device acpi_cookie; + status_t fStatus; + WMIInfoList fList; + wmi_info* fWMIInfos; + uint32 fWMIInfoCount; + const char* fUid; +}; + + +#endif // WMI_PRIVATE_H