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
31class WMIAsus {
32public:
33								WMIAsus(device_node *node);
34								~WMIAsus();
35
36private:
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);
46private:
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
58WMIAsus::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
111WMIAsus::~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
123status_t
124WMIAsus::_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
150status_t
151WMIAsus::_EnableAls(uint32 enable)
152{
153	CALLED();
154	return _SetDevState(ASUS_WMI_DEVID_ALS_ENABLE, enable, NULL);
155}
156
157
158status_t
159WMIAsus::_GetDevState(uint32 devId, uint32 *value)
160{
161	return _EvaluateMethod(fDstsID, devId, 0, value);
162}
163
164
165status_t
166WMIAsus::_SetDevState(uint32 devId, uint32 arg, uint32 *value)
167{
168	return _EvaluateMethod(ASUS_WMI_METHODID_DEVS, devId, arg, value);
169}
170
171
172void
173WMIAsus::_NotifyHandler(acpi_handle handle, uint32 notify, void *context)
174{
175	WMIAsus* device = (WMIAsus*)context;
176	device->_Notify(handle, notify);
177}
178
179
180void
181WMIAsus::_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
235static float
236wmi_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
259static status_t
260wmi_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
273static status_t
274wmi_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
287static void
288wmi_asus_uninit_driver(void *driverCookie)
289{
290	CALLED();
291	WMIAsus* device = (WMIAsus*)driverCookie;
292	delete device;
293}
294
295
296static status_t
297wmi_asus_register_child_devices(void *cookie)
298{
299	CALLED();
300	return B_OK;
301}
302
303
304driver_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