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