/* * 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_ALS_ENABLE 0x00050001 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_FN_LOCK 0x00100023 #define ASUS_WMI_DSTS_PRESENCE_BIT 0x10000 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); status_t _EnableAls(uint32 enable); private: device_node* fNode; wmi_device_interface* wmi; wmi_device wmi_cookie; uint32 fDstsID; const char* fEventGuidString; bool fEnableALS; bool fFnLock; }; WMIAsus::WMIAsus(device_node *node) : fNode(node), fDstsID(ASUS_WMI_METHODID_DSTS), fEventGuidString(NULL), fEnableALS(false), fFnLock(true) { 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); } // some ASUS laptops need to be ALS forced fEnableALS = gSMBios->match_vendor_product("ASUSTeK COMPUTER INC.", "UX430UAR"); if (fEnableALS && _EnableAls(1) == B_OK) { TRACE("ALSC enabled\n"); } if (_GetDevState(ASUS_WMI_DEVID_FN_LOCK, &value) == B_OK && (value & ASUS_WMI_DSTS_PRESENCE_BIT) != 0) { // set fn lock _SetDevState(ASUS_WMI_DEVID_FN_LOCK, fFnLock, NULL); } // 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() { // for ALS if (fEnableALS && _EnableAls(0) == B_OK) { TRACE("ALSC disabled\n"); } 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, 0, 0, 0 }; 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::_EnableAls(uint32 enable) { CALLED(); return _SetDevState(ASUS_WMI_DEVID_ALS_ENABLE, enable, NULL); } 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 == 0x4e) { TRACE("WMIAsus::_Notify() keyboard fnlock key\n"); fFnLock = !fFnLock; _SetDevState(ASUS_WMI_DEVID_FN_LOCK, fFnLock, NULL); TRACE("WMIAsus::_Notify() keyboard fnlock key %" B_PRIx32 "\n", fFnLock); } else if (result == 0xc4 || result == 0xc5 || result == 0xc7) { 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 (result == 0xc5) { if (value > 0) value--; } else { value++; value &= 0x3; } 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 };