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
wmi_acpi_adr_space_handler(uint32 function,acpi_physical_address address,uint32 bitWidth,int * value,void * handlerContext,void * regionContext)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
WMIACPI(device_node * node)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
~WMIACPI()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
InitCheck()91 WMIACPI::InitCheck()
92 {
93 return fStatus;
94 }
95
96
97 status_t
Scan()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
GetBlock(uint32 busCookie,uint8 instance,uint32 methodId,acpi_data * out)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
SetBlock(uint32 busCookie,uint8 instance,uint32 methodId,const acpi_data * in)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
EvaluateMethod(uint32 busCookie,uint8 instance,uint32 methodId,const acpi_data * in,acpi_data * out)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
InstallEventHandler(const char * guidString,acpi_notify_handler handler,void * context)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
RemoveEventHandler(const char * guidString)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
GetEventData(uint32 notify,acpi_data * out)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*
GetUid(uint32 busCookie)305 WMIACPI::GetUid(uint32 busCookie)
306 {
307 return fUid;
308 }
309
310
311 status_t
_SetEventGeneration(wmi_info * info,bool enabled)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
_EvaluateMethodSimple(const char * method,uint64 integer)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
_NotifyHandler(acpi_handle device,uint32 value,void * context)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
_Notify(acpi_handle device,uint32 value)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
_GuidToGuidString(uint8 guid[16],char * guidString)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
wmi_acpi_support(device_node * parent)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
wmi_acpi_register_device(device_node * node)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
wmi_acpi_init_driver(device_node * node,void ** driverCookie)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
wmi_acpi_uninit_driver(void * driverCookie)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
wmi_acpi_register_child_devices(void * cookie)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