1 /* 2 * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. 3 * Distributed under the terms of the MIT license. 4 */ 5 6 7 #define DRIVER_NAME "wmi_asus" 8 #include "WMIPrivate.h" 9 10 11 #define ACPI_ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" 12 #define ACPI_ASUS_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" 13 #define ACPI_ASUS_UID_ASUSWMI "ASUSWMI" 14 15 #define ASUS_WMI_METHODID_INIT 0x54494E49 16 #define ASUS_WMI_METHODID_SPEC 0x43455053 17 #define ASUS_WMI_METHODID_SFUN 0x4E554653 18 #define ASUS_WMI_METHODID_DCTS 0x53544344 19 #define ASUS_WMI_METHODID_DSTS 0x53545344 20 #define ASUS_WMI_METHODID_DEVS 0x53564544 21 22 23 #define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 24 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 25 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 26 #define ASUS_WMI_DEVID_FN_LOCK 0x00100023 27 28 #define ASUS_WMI_DSTS_PRESENCE_BIT 0x10000 29 30 31 class WMIAsus { 32 public: 33 WMIAsus(device_node *node); 34 ~WMIAsus(); 35 36 private: 37 status_t _EvaluateMethod(uint32 methodId, uint32 arg0, 38 uint32 arg1, uint32 *returnValue); 39 status_t _GetDevState(uint32 devId, uint32* value); 40 status_t _SetDevState(uint32 devId, uint32 arg, 41 uint32* value); 42 static void _NotifyHandler(acpi_handle handle, 43 uint32 notify, void *context); 44 void _Notify(acpi_handle handle, uint32 notify); 45 status_t _EnableAls(uint32 enable); 46 private: 47 device_node* fNode; 48 wmi_device_interface* wmi; 49 wmi_device wmi_cookie; 50 uint32 fDstsID; 51 const char* fEventGuidString; 52 bool fEnableALS; 53 bool fFnLock; 54 }; 55 56 57 58 WMIAsus::WMIAsus(device_node *node) 59 : 60 fNode(node), 61 fDstsID(ASUS_WMI_METHODID_DSTS), 62 fEventGuidString(NULL), 63 fEnableALS(false), 64 fFnLock(true) 65 { 66 CALLED(); 67 68 device_node *parent; 69 parent = gDeviceManager->get_parent_node(node); 70 gDeviceManager->get_driver(parent, (driver_module_info **)&wmi, 71 (void **)&wmi_cookie); 72 73 gDeviceManager->put_node(parent); 74 75 const char* uid = wmi->get_uid(wmi_cookie); 76 if (uid != NULL && strcmp(uid, ACPI_ASUS_UID_ASUSWMI) == 0) 77 fDstsID = ASUS_WMI_METHODID_DCTS; 78 TRACE("WMIAsus using _UID %s\n", uid); 79 uint32 value; 80 if (_EvaluateMethod(ASUS_WMI_METHODID_INIT, 0, 0, &value) == B_OK) { 81 TRACE("_INIT: %x\n", value); 82 } 83 if (_EvaluateMethod(ASUS_WMI_METHODID_SPEC, 0, 9, &value) == B_OK) { 84 TRACE("_SPEC: %x\n", value); 85 } 86 if (_EvaluateMethod(ASUS_WMI_METHODID_SFUN, 0, 0, &value) == B_OK) { 87 TRACE("_SFUN: %x\n", value); 88 } 89 90 // some ASUS laptops need to be ALS forced 91 fEnableALS = 92 gSMBios->match_vendor_product("ASUSTeK COMPUTER INC.", "UX430UAR"); 93 if (fEnableALS && _EnableAls(1) == B_OK) { 94 TRACE("ALSC enabled\n"); 95 } 96 97 if (_GetDevState(ASUS_WMI_DEVID_FN_LOCK, &value) == B_OK 98 && (value & ASUS_WMI_DSTS_PRESENCE_BIT) != 0) { 99 // set fn lock 100 _SetDevState(ASUS_WMI_DEVID_FN_LOCK, fFnLock, NULL); 101 } 102 103 // install event handler 104 if (wmi->install_event_handler(wmi_cookie, ACPI_ASUS_WMI_EVENT_GUID, 105 _NotifyHandler, this) == B_OK) { 106 fEventGuidString = ACPI_ASUS_WMI_EVENT_GUID; 107 } 108 } 109 110 111 WMIAsus::~WMIAsus() 112 { 113 // for ALS 114 if (fEnableALS && _EnableAls(0) == B_OK) { 115 TRACE("ALSC disabled\n"); 116 } 117 118 if (fEventGuidString != NULL) 119 wmi->remove_event_handler(wmi_cookie, fEventGuidString); 120 } 121 122 123 status_t 124 WMIAsus::_EvaluateMethod(uint32 methodId, uint32 arg0, uint32 arg1, 125 uint32 *returnValue) 126 { 127 CALLED(); 128 uint32 params[] = { arg0, arg1, 0, 0, 0 }; 129 acpi_data inBuffer = { sizeof(params), params }; 130 acpi_data outBuffer = { ACPI_ALLOCATE_BUFFER, NULL }; 131 status_t status = wmi->evaluate_method(wmi_cookie, 0, methodId, &inBuffer, 132 &outBuffer); 133 if (status != B_OK) 134 return status; 135 136 acpi_object_type* object = (acpi_object_type*)outBuffer.pointer; 137 uint32 result = 0; 138 if (object != NULL) { 139 if (object->object_type == ACPI_TYPE_INTEGER) 140 result = object->integer.integer; 141 free(object); 142 } 143 if (returnValue != NULL) 144 *returnValue = result; 145 146 return B_OK; 147 } 148 149 150 status_t 151 WMIAsus::_EnableAls(uint32 enable) 152 { 153 CALLED(); 154 return _SetDevState(ASUS_WMI_DEVID_ALS_ENABLE, enable, NULL); 155 } 156 157 158 status_t 159 WMIAsus::_GetDevState(uint32 devId, uint32 *value) 160 { 161 return _EvaluateMethod(fDstsID, devId, 0, value); 162 } 163 164 165 status_t 166 WMIAsus::_SetDevState(uint32 devId, uint32 arg, uint32 *value) 167 { 168 return _EvaluateMethod(ASUS_WMI_METHODID_DEVS, devId, arg, value); 169 } 170 171 172 void 173 WMIAsus::_NotifyHandler(acpi_handle handle, uint32 notify, void *context) 174 { 175 WMIAsus* device = (WMIAsus*)context; 176 device->_Notify(handle, notify); 177 } 178 179 180 void 181 WMIAsus::_Notify(acpi_handle handle, uint32 notify) 182 { 183 CALLED(); 184 185 acpi_data response = { ACPI_ALLOCATE_BUFFER, NULL }; 186 wmi->get_event_data(wmi_cookie, notify, &response); 187 188 acpi_object_type* object = (acpi_object_type*)response.pointer; 189 uint32 result = 0; 190 if (object != NULL) { 191 if (object->object_type == ACPI_TYPE_INTEGER) 192 result = object->integer.integer; 193 free(object); 194 } 195 if (result != 0) { 196 if (result == 0x4e) { 197 TRACE("WMIAsus::_Notify() keyboard fnlock key\n"); 198 fFnLock = !fFnLock; 199 _SetDevState(ASUS_WMI_DEVID_FN_LOCK, fFnLock, NULL); 200 TRACE("WMIAsus::_Notify() keyboard fnlock key %" B_PRIx32 "\n", 201 fFnLock); 202 } else if (result == 0xc4 || result == 0xc5 || result == 0xc7) { 203 TRACE("WMIAsus::_Notify() keyboard backlight key\n"); 204 uint32 value; 205 if (_GetDevState(ASUS_WMI_DEVID_KBD_BACKLIGHT, &value) == B_OK) { 206 TRACE("WMIAsus::_Notify() getkeyboard backlight key %" 207 B_PRIx32 "\n", value); 208 value &= 0x7; 209 if (result == 0xc4) { 210 if (value < 3) 211 value++; 212 } else if (result == 0xc5) { 213 if (value > 0) 214 value--; 215 } else { 216 value++; 217 value &= 0x3; 218 } 219 TRACE("WMIAsus::_Notify() set keyboard backlight key %" 220 B_PRIx32 "\n", value); 221 _SetDevState(ASUS_WMI_DEVID_KBD_BACKLIGHT, value | 0x80, NULL); 222 } 223 } else if (result == 0x6b) { 224 TRACE("WMIAsus::_Notify() touchpad control\n"); 225 } else { 226 TRACE("WMIAsus::_Notify() key 0x%" B_PRIx32 "\n", result); 227 } 228 } 229 } 230 231 232 // #pragma mark - driver module API 233 234 235 static float 236 wmi_asus_support(device_node *parent) 237 { 238 // make sure parent is really a wmi device 239 const char *bus; 240 if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 241 return -1; 242 243 if (strcmp(bus, "wmi")) 244 return 0.0; 245 246 // check whether it's an asus wmi device 247 const char *guid; 248 if (gDeviceManager->get_attr_string(parent, WMI_GUID_STRING_ITEM, &guid, 249 false) != B_OK || strcmp(guid, ACPI_ASUS_WMI_MGMT_GUID) != 0) { 250 return 0.0; 251 } 252 253 TRACE("found an asus wmi device\n"); 254 255 return 0.6; 256 } 257 258 259 static status_t 260 wmi_asus_register_device(device_node *node) 261 { 262 CALLED(); 263 device_attr attrs[] = { 264 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "WMI ASUS" }}, 265 { NULL } 266 }; 267 268 return gDeviceManager->register_node(node, WMI_ASUS_DRIVER_NAME, attrs, 269 NULL, NULL); 270 } 271 272 273 static status_t 274 wmi_asus_init_driver(device_node *node, void **driverCookie) 275 { 276 CALLED(); 277 278 WMIAsus* device = new(std::nothrow) WMIAsus(node); 279 if (device == NULL) 280 return B_NO_MEMORY; 281 *driverCookie = device; 282 283 return B_OK; 284 } 285 286 287 static void 288 wmi_asus_uninit_driver(void *driverCookie) 289 { 290 CALLED(); 291 WMIAsus* device = (WMIAsus*)driverCookie; 292 delete device; 293 } 294 295 296 static status_t 297 wmi_asus_register_child_devices(void *cookie) 298 { 299 CALLED(); 300 return B_OK; 301 } 302 303 304 driver_module_info gWMIAsusDriverModule = { 305 { 306 WMI_ASUS_DRIVER_NAME, 307 0, 308 NULL 309 }, 310 311 wmi_asus_support, 312 wmi_asus_register_device, 313 wmi_asus_init_driver, 314 wmi_asus_uninit_driver, 315 wmi_asus_register_child_devices, 316 NULL, // rescan 317 NULL, // removed 318 }; 319 320