xref: /haiku/src/add-ons/kernel/drivers/wmi/WMIACPI.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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