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_acpi" 8 #include "WMIPrivate.h" 9 10 11 #define ACPI_NAME_ACPI_WMI "PNP0C14" 12 13 #define ACPI_WMI_REGFLAG_EXPENSIVE (1 << 0) 14 #define ACPI_WMI_REGFLAG_METHOD (1 << 1) 15 #define ACPI_WMI_REGFLAG_STRING (1 << 2) 16 #define ACPI_WMI_REGFLAG_EVENT (1 << 3) 17 18 19 device_manager_info *gDeviceManager; 20 smbios_module_info *gSMBios; 21 22 23 acpi_status wmi_acpi_adr_space_handler(uint32 function, 24 acpi_physical_address address, uint32 bitWidth, int *value, 25 void *handlerContext, void *regionContext) 26 { 27 return B_OK; 28 } 29 30 31 WMIACPI::WMIACPI(device_node *node) 32 : 33 fNode(node) 34 { 35 CALLED(); 36 37 device_node *parent; 38 parent = gDeviceManager->get_parent_node(node); 39 gDeviceManager->get_driver(parent, (driver_module_info **)&acpi, 40 (void **)&acpi_cookie); 41 gDeviceManager->get_attr_string(parent, ACPI_DEVICE_UID_ITEM, &fUid, 42 false); 43 gDeviceManager->put_node(parent); 44 45 // install notify handler 46 fStatus = acpi->install_notify_handler(acpi_cookie, 47 ACPI_ALL_NOTIFY, _NotifyHandler, this); 48 if (fStatus != B_OK) { 49 ERROR("install_notify_handler failed\n"); 50 return; 51 } 52 53 fStatus = acpi->install_address_space_handler(acpi_cookie, 54 ACPI_ADR_SPACE_EC, wmi_acpi_adr_space_handler, NULL, this); 55 if (fStatus != B_OK) { 56 ERROR("wmi_acpi_adr_space_handler failed\n"); 57 return; 58 } 59 60 acpi_data buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 61 fStatus = acpi->evaluate_method(acpi_cookie, "_WDG", NULL, &buffer); 62 if (fStatus != B_OK) { 63 ERROR("Method call _WDG failed\n"); 64 return; 65 } 66 67 acpi_object_type* object = (acpi_object_type*)buffer.pointer; 68 fWMIInfoCount = object->buffer.length / sizeof(guid_info); 69 guid_info *info = (guid_info*)object->buffer.buffer; 70 fWMIInfos = (wmi_info *)calloc(fWMIInfoCount, sizeof(wmi_info)); 71 TRACE("found %" B_PRIu32 " objects\n", fWMIInfoCount); 72 for (uint32 i = 0; i < fWMIInfoCount; i++, info++) { 73 wmi_info *wmi = &fWMIInfos[i]; 74 wmi->guid = *info; 75 fList.Add(wmi); 76 } 77 free(object); 78 } 79 80 81 WMIACPI::~WMIACPI() 82 { 83 free(fWMIInfos); 84 85 acpi->remove_notify_handler(acpi_cookie, 86 ACPI_ALL_NOTIFY, _NotifyHandler); 87 } 88 89 90 status_t 91 WMIACPI::InitCheck() 92 { 93 return fStatus; 94 } 95 96 97 status_t 98 WMIACPI::Scan() 99 { 100 CALLED(); 101 status_t status; 102 wmi_info* wmiInfo = NULL; 103 uint32 index = 0; 104 for (WMIInfoList::Iterator it = fList.GetIterator(); 105 (wmiInfo = it.Next()) != NULL; index++) { 106 uint8* guid = wmiInfo->guid.guid; 107 char guidString[37] = {}; 108 _GuidToGuidString(guid, guidString); 109 device_attr attrs[] = { 110 // connection 111 { WMI_GUID_STRING_ITEM, B_STRING_TYPE, { .string = guidString }}, 112 113 { WMI_BUS_COOKIE, B_UINT32_TYPE, { .ui32 = index }}, 114 115 // description of peripheral drivers 116 { B_DEVICE_BUS, B_STRING_TYPE, { .string = "wmi" }}, 117 118 { B_DEVICE_FLAGS, B_UINT32_TYPE, 119 { .ui32 = B_FIND_MULTIPLE_CHILDREN }}, 120 121 { NULL } 122 }; 123 124 status = gDeviceManager->register_node(fNode, WMI_DEVICE_MODULE_NAME, 125 attrs, NULL, NULL); 126 if (status != B_OK) 127 return status; 128 } 129 130 return B_OK; 131 132 } 133 134 135 status_t 136 WMIACPI::GetBlock(uint32 busCookie, uint8 instance, uint32 methodId, 137 acpi_data* out) 138 { 139 CALLED(); 140 if (busCookie >= fWMIInfoCount) 141 return B_BAD_VALUE; 142 wmi_info* info = &fWMIInfos[busCookie]; 143 if ((info->guid.flags & ACPI_WMI_REGFLAG_METHOD) != 0 144 || (info->guid.flags & ACPI_WMI_REGFLAG_EVENT) != 0) { 145 return B_BAD_VALUE; 146 } else if (instance > info->guid.max_instance) 147 return B_BAD_VALUE; 148 149 char method[5] = "WQ"; 150 strncat(method, info->guid.oid, 2); 151 char wcMethod[5] = "WC"; 152 strncat(wcMethod, info->guid.oid, 2); 153 status_t wcStatus = B_OK; 154 status_t status = B_OK; 155 156 if ((info->guid.flags & ACPI_WMI_REGFLAG_EXPENSIVE) != 0) 157 wcStatus = _EvaluateMethodSimple(wcMethod, 1); 158 159 acpi_object_type object; 160 object.object_type = ACPI_TYPE_INTEGER; 161 object.integer.integer = instance; 162 acpi_objects objects = { 1, &object}; 163 TRACE("GetBlock calling %s\n", method); 164 status = acpi->evaluate_method(acpi_cookie, method, &objects, out); 165 166 if ((info->guid.flags & ACPI_WMI_REGFLAG_EXPENSIVE) != 0 167 && wcStatus == B_OK) { 168 _EvaluateMethodSimple(wcMethod, 0); 169 } 170 171 return status; 172 } 173 174 175 status_t 176 WMIACPI::SetBlock(uint32 busCookie, uint8 instance, uint32 methodId, 177 const acpi_data* in) 178 { 179 CALLED(); 180 if (busCookie >= fWMIInfoCount) 181 return B_BAD_VALUE; 182 wmi_info* info = &fWMIInfos[busCookie]; 183 if ((info->guid.flags & ACPI_WMI_REGFLAG_METHOD) != 0 184 || (info->guid.flags & ACPI_WMI_REGFLAG_EVENT) != 0) { 185 return B_BAD_VALUE; 186 } else if (instance > info->guid.max_instance) 187 return B_BAD_VALUE; 188 189 char method[5] = "WS"; 190 strncat(method, info->guid.oid, 2); 191 192 acpi_object_type object[2]; 193 object[0].object_type = ACPI_TYPE_INTEGER; 194 object[0].integer.integer = instance; 195 object[1].object_type = ACPI_TYPE_BUFFER; 196 if ((info->guid.flags & ACPI_WMI_REGFLAG_STRING) != 0) 197 object[1].object_type = ACPI_TYPE_STRING; 198 object[1].buffer.buffer = in->pointer; 199 object[1].buffer.length = in->length; 200 acpi_objects objects = { 2, object}; 201 TRACE("SetBlock calling %s\n", method); 202 return acpi->evaluate_method(acpi_cookie, method, &objects, NULL); 203 } 204 205 206 status_t 207 WMIACPI::EvaluateMethod(uint32 busCookie, uint8 instance, uint32 methodId, 208 const acpi_data* in, acpi_data* out) 209 { 210 CALLED(); 211 if (busCookie >= fWMIInfoCount) 212 return B_BAD_VALUE; 213 wmi_info* info = &fWMIInfos[busCookie]; 214 if ((info->guid.flags & ACPI_WMI_REGFLAG_METHOD) == 0) 215 return B_BAD_VALUE; 216 217 char method[5] = "WM"; 218 strncat(method, info->guid.oid, 2); 219 220 acpi_object_type object[3]; 221 object[0].object_type = ACPI_TYPE_INTEGER; 222 object[0].integer.integer = instance; 223 object[1].object_type = ACPI_TYPE_INTEGER; 224 object[1].integer.integer = methodId; 225 uint32 count = 2; 226 if (in != NULL) { 227 object[2].object_type = ACPI_TYPE_BUFFER; 228 if ((info->guid.flags & ACPI_WMI_REGFLAG_STRING) != 0) 229 object[2].object_type = ACPI_TYPE_STRING; 230 object[2].buffer.buffer = in->pointer; 231 object[2].buffer.length = in->length; 232 count++; 233 } 234 acpi_objects objects = { count, object}; 235 TRACE("EvaluateMethod calling %s\n", method); 236 return acpi->evaluate_method(acpi_cookie, method, &objects, out); 237 } 238 239 240 status_t 241 WMIACPI::InstallEventHandler(const char* guidString, 242 acpi_notify_handler handler, void* context) 243 { 244 CALLED(); 245 char string[37] = {}; 246 for (uint32 i = 0; i < fWMIInfoCount; i++) { 247 wmi_info* info = &fWMIInfos[i]; 248 _GuidToGuidString(info->guid.guid, string); 249 if (strcmp(guidString, string) == 0) { 250 status_t status = B_OK; 251 if (info->handler == NULL) 252 status = _SetEventGeneration(info, true); 253 if (status == B_OK) { 254 info->handler = handler; 255 info->handler_context = context; 256 } 257 return status; 258 } 259 } 260 return B_ENTRY_NOT_FOUND; 261 } 262 263 264 status_t 265 WMIACPI::RemoveEventHandler(const char* guidString) 266 { 267 CALLED(); 268 char string[37] = {}; 269 for (uint32 i = 0; i < fWMIInfoCount; i++) { 270 wmi_info* info = &fWMIInfos[i]; 271 _GuidToGuidString(info->guid.guid, string); 272 if (strcmp(guidString, string) == 0) { 273 status_t status = _SetEventGeneration(info, false); 274 info->handler = NULL; 275 info->handler_context = NULL; 276 return status; 277 } 278 } 279 return B_ENTRY_NOT_FOUND; 280 } 281 282 283 status_t 284 WMIACPI::GetEventData(uint32 notify, acpi_data* out) 285 { 286 CALLED(); 287 288 acpi_object_type object; 289 object.object_type = ACPI_TYPE_INTEGER; 290 object.integer.integer = notify; 291 acpi_objects objects = { 1, &object }; 292 293 for (uint32 i = 0; i < fWMIInfoCount; i++) { 294 wmi_info* info = &fWMIInfos[i]; 295 if (info->guid.notify_id == notify 296 && (info->guid.flags & ACPI_WMI_REGFLAG_EVENT) != 0) { 297 return acpi->evaluate_method(acpi_cookie, "_WED", &objects, out); 298 } 299 } 300 return B_ENTRY_NOT_FOUND; 301 } 302 303 304 const char* 305 WMIACPI::GetUid(uint32 busCookie) 306 { 307 return fUid; 308 } 309 310 311 status_t 312 WMIACPI::_SetEventGeneration(wmi_info* info, bool enabled) 313 { 314 char method[5]; 315 sprintf(method, "WE%02X", info->guid.notify_id); 316 TRACE("_SetEventGeneration calling %s\n", method); 317 status_t status = _EvaluateMethodSimple(method, enabled ? 1 : 0); 318 // the method is allowed not to exist 319 if (status == B_ERROR) 320 status = B_OK; 321 return status; 322 } 323 324 325 status_t 326 WMIACPI::_EvaluateMethodSimple(const char* method, uint64 integer) 327 { 328 acpi_object_type object; 329 object.object_type = ACPI_TYPE_INTEGER; 330 object.integer.integer = integer; 331 acpi_objects objects = { 1, &object}; 332 return acpi->evaluate_method(acpi_cookie, method, &objects, NULL); 333 } 334 335 336 void 337 WMIACPI::_NotifyHandler(acpi_handle device, uint32 value, void *context) 338 { 339 WMIACPI* bus = (WMIACPI*)context; 340 bus->_Notify(device, value); 341 } 342 343 344 void 345 WMIACPI::_Notify(acpi_handle device, uint32 value) 346 { 347 for (uint32 i = 0; i < fWMIInfoCount; i++) { 348 wmi_info* wmi = &fWMIInfos[i]; 349 if (wmi->guid.notify_id == value) { 350 TRACE("_Notify found event 0x%" B_PRIx32 "\n", value); 351 if (wmi->handler != NULL) { 352 TRACE("_Notify found handler for event 0x%" B_PRIx32 "\n", 353 value); 354 wmi->handler(device, value, wmi->handler_context); 355 } 356 break; 357 } 358 } 359 } 360 361 362 void 363 WMIACPI::_GuidToGuidString(uint8 guid[16], char* guidString) 364 { 365 sprintf(guidString, 366 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", 367 guid[3], guid[2], guid[1], guid[0], guid[5], guid[4], guid[7], guid[6], 368 guid[8], guid[9], guid[10], guid[11], guid[12], guid[13], guid[14], 369 guid[15]); 370 } 371 372 373 // #pragma mark - driver module API 374 375 376 static float 377 wmi_acpi_support(device_node *parent) 378 { 379 CALLED(); 380 381 // make sure parent is really the ACPI bus manager 382 const char *bus; 383 if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 384 return -1; 385 386 if (strcmp(bus, "acpi")) 387 return 0.0; 388 389 // check whether it's really a device 390 uint32 device_type; 391 if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 392 &device_type, false) != B_OK 393 || device_type != ACPI_TYPE_DEVICE) { 394 return 0.0; 395 } 396 397 // check whether it's an acpi wmi device 398 const char *name; 399 if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name, 400 false) != B_OK || strcmp(name, ACPI_NAME_ACPI_WMI) != 0) { 401 return 0.0; 402 } 403 404 TRACE("found an acpi wmi device\n"); 405 406 return 0.6; 407 } 408 409 410 static status_t 411 wmi_acpi_register_device(device_node *node) 412 { 413 CALLED(); 414 device_attr attrs[] = { 415 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "WMI ACPI" }}, 416 { NULL } 417 }; 418 419 return gDeviceManager->register_node(node, WMI_ACPI_DRIVER_NAME, attrs, 420 NULL, NULL); 421 } 422 423 424 static status_t 425 wmi_acpi_init_driver(device_node *node, void **driverCookie) 426 { 427 CALLED(); 428 WMIACPI* device = new(std::nothrow) WMIACPI(node); 429 if (device == NULL) 430 return B_NO_MEMORY; 431 432 *driverCookie = device; 433 434 return B_OK; 435 } 436 437 438 static void 439 wmi_acpi_uninit_driver(void *driverCookie) 440 { 441 CALLED(); 442 WMIACPI *device = (WMIACPI*)driverCookie; 443 444 delete device; 445 } 446 447 448 static status_t 449 wmi_acpi_register_child_devices(void *cookie) 450 { 451 CALLED(); 452 WMIACPI *device = (WMIACPI*)cookie; 453 return device->Scan(); 454 } 455 456 457 module_dependency module_dependencies[] = { 458 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, 459 { SMBIOS_MODULE_NAME, (module_info**)&gSMBios }, 460 {} 461 }; 462 463 464 static driver_module_info sWMIACPIDriverModule = { 465 { 466 WMI_ACPI_DRIVER_NAME, 467 0, 468 NULL 469 }, 470 471 wmi_acpi_support, 472 wmi_acpi_register_device, 473 wmi_acpi_init_driver, 474 wmi_acpi_uninit_driver, 475 wmi_acpi_register_child_devices, 476 NULL, // rescan 477 NULL, // removed 478 }; 479 480 481 module_info *modules[] = { 482 (module_info *)&sWMIACPIDriverModule, 483 (module_info *)&gWMIAsusDriverModule, 484 (module_info *)&gWMIDeviceModule, 485 NULL 486 }; 487