xref: /haiku/src/add-ons/kernel/drivers/wmi/WMIAsus.cpp (revision 42aa87de9ea1b23d7e9a6f1dac2432dae5658a12)
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 
WMIAsus(device_node * node)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 
~WMIAsus()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
_EvaluateMethod(uint32 methodId,uint32 arg0,uint32 arg1,uint32 * returnValue)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
_EnableAls(uint32 enable)151 WMIAsus::_EnableAls(uint32 enable)
152 {
153 	CALLED();
154 	return _SetDevState(ASUS_WMI_DEVID_ALS_ENABLE, enable, NULL);
155 }
156 
157 
158 status_t
_GetDevState(uint32 devId,uint32 * value)159 WMIAsus::_GetDevState(uint32 devId, uint32 *value)
160 {
161 	return _EvaluateMethod(fDstsID, devId, 0, value);
162 }
163 
164 
165 status_t
_SetDevState(uint32 devId,uint32 arg,uint32 * value)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
_NotifyHandler(acpi_handle handle,uint32 notify,void * context)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
_Notify(acpi_handle handle,uint32 notify)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
wmi_asus_support(device_node * parent)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
wmi_asus_register_device(device_node * node)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
wmi_asus_init_driver(device_node * node,void ** driverCookie)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
wmi_asus_uninit_driver(void * driverCookie)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
wmi_asus_register_child_devices(void * cookie)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