]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/acpi_support/acpi_asus_wmi.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / acpi_support / acpi_asus_wmi.c
1 /*-
2  * Copyright (c) 2012 Alexander Motin <mav@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/conf.h>
33 #include <sys/uio.h>
34 #include <sys/proc.h>
35 #include <sys/kernel.h>
36 #include <sys/bus.h>
37 #include <sys/sbuf.h>
38 #include <sys/module.h>
39 #include <sys/sysctl.h>
40
41 #include <contrib/dev/acpica/include/acpi.h>
42 #include <contrib/dev/acpica/include/accommon.h>
43 #include <dev/acpica/acpivar.h>
44 #include "acpi_wmi_if.h"
45
46 #define _COMPONENT      ACPI_OEM
47 ACPI_MODULE_NAME("ASUS-WMI")
48
49 #define ACPI_ASUS_WMI_MGMT_GUID         "97845ED0-4E6D-11DE-8A39-0800200C9A66"
50 #define ACPI_ASUS_WMI_EVENT_GUID        "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
51 #define ACPI_EEEPC_WMI_EVENT_GUID       "ABBC0F72-8EA1-11D1-00A0-C90629100000"
52
53 /* WMI Methods */
54 #define ASUS_WMI_METHODID_SPEC          0x43455053
55 #define ASUS_WMI_METHODID_SFUN          0x4E554653
56 #define ASUS_WMI_METHODID_DSTS          0x53544344
57 #define ASUS_WMI_METHODID_DSTS2         0x53545344
58 #define ASUS_WMI_METHODID_DEVS          0x53564544
59 #define ASUS_WMI_METHODID_INIT          0x54494E49
60 #define ASUS_WMI_METHODID_HKEY          0x59454B48
61
62 #define ASUS_WMI_UNSUPPORTED_METHOD     0xFFFFFFFE
63
64 /* Wireless */
65 #define ASUS_WMI_DEVID_HW_SWITCH        0x00010001
66 #define ASUS_WMI_DEVID_WIRELESS_LED     0x00010002
67 #define ASUS_WMI_DEVID_CWAP             0x00010003
68 #define ASUS_WMI_DEVID_WLAN             0x00010011
69 #define ASUS_WMI_DEVID_BLUETOOTH        0x00010013
70 #define ASUS_WMI_DEVID_GPS              0x00010015
71 #define ASUS_WMI_DEVID_WIMAX            0x00010017
72 #define ASUS_WMI_DEVID_WWAN3G           0x00010019
73 #define ASUS_WMI_DEVID_UWB              0x00010021
74
75 /* LEDs */
76 #define ASUS_WMI_DEVID_LED1             0x00020011
77 #define ASUS_WMI_DEVID_LED2             0x00020012
78 #define ASUS_WMI_DEVID_LED3             0x00020013
79 #define ASUS_WMI_DEVID_LED4             0x00020014
80 #define ASUS_WMI_DEVID_LED5             0x00020015
81 #define ASUS_WMI_DEVID_LED6             0x00020016
82
83 /* Backlight and Brightness */
84 #define ASUS_WMI_DEVID_BACKLIGHT        0x00050011
85 #define ASUS_WMI_DEVID_BRIGHTNESS       0x00050012
86 #define ASUS_WMI_DEVID_KBD_BACKLIGHT    0x00050021
87 #define ASUS_WMI_DEVID_LIGHT_SENSOR     0x00050022
88
89 /* Misc */
90 #define ASUS_WMI_DEVID_CAMERA           0x00060013
91 #define ASUS_WMI_DEVID_CARDREADER       0x00080013
92 #define ASUS_WMI_DEVID_TOUCHPAD         0x00100011
93 #define ASUS_WMI_DEVID_TOUCHPAD_LED     0x00100012
94 #define ASUS_WMI_DEVID_THERMAL_CTRL     0x00110011
95 #define ASUS_WMI_DEVID_FAN_CTRL         0x00110012
96 #define ASUS_WMI_DEVID_PROCESSOR_STATE  0x00120012
97
98 /* DSTS masks */
99 #define ASUS_WMI_DSTS_STATUS_BIT        0x00000001
100 #define ASUS_WMI_DSTS_UNKNOWN_BIT       0x00000002
101 #define ASUS_WMI_DSTS_PRESENCE_BIT      0x00010000
102 #define ASUS_WMI_DSTS_USER_BIT          0x00020000
103 #define ASUS_WMI_DSTS_BIOS_BIT          0x00040000
104 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK   0x000000FF
105 #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK   0x0000FF00
106
107
108 struct acpi_asus_wmi_softc {
109         device_t        dev;
110         device_t        wmi_dev;
111         const char      *notify_guid;
112         struct sysctl_ctx_list  *sysctl_ctx;
113         struct sysctl_oid       *sysctl_tree;
114         int             dsts_id;
115         int             handle_keys;
116 };
117
118 static struct {
119         char    *name;
120         int     dev_id;
121         char    *description;
122         int     access;
123 } acpi_asus_wmi_sysctls[] = {
124         {
125                 .name           = "hw_switch",
126                 .dev_id         = ASUS_WMI_DEVID_HW_SWITCH,
127                 .description    = "hw_switch",
128                 .access         = CTLTYPE_INT | CTLFLAG_RW
129         },
130         {
131                 .name           = "wireless_led",
132                 .dev_id         = ASUS_WMI_DEVID_WIRELESS_LED,
133                 .description    = "Wireless LED control",
134                 .access         = CTLTYPE_INT | CTLFLAG_RW
135         },
136         {
137                 .name           = "cwap",
138                 .dev_id         = ASUS_WMI_DEVID_CWAP,
139                 .description    = "Alt+F2 function",
140                 .access         = CTLTYPE_INT | CTLFLAG_RW
141         },
142         {
143                 .name           = "wlan",
144                 .dev_id         = ASUS_WMI_DEVID_WLAN,
145                 .description    = "WLAN power control",
146                 .access         = CTLTYPE_INT | CTLFLAG_RW
147         },
148         {
149                 .name           = "bluetooth",
150                 .dev_id         = ASUS_WMI_DEVID_BLUETOOTH,
151                 .description    = "Bluetooth power control",
152                 .access         = CTLTYPE_INT | CTLFLAG_RW
153         },
154         {
155                 .name           = "gps",
156                 .dev_id         = ASUS_WMI_DEVID_GPS,
157                 .description    = "GPS power control",
158                 .access         = CTLTYPE_INT | CTLFLAG_RW
159         },
160         {
161                 .name           = "wimax",
162                 .dev_id         = ASUS_WMI_DEVID_WIMAX,
163                 .description    = "WiMAX power control",
164                 .access         = CTLTYPE_INT | CTLFLAG_RW
165         },
166         {
167                 .name           = "wwan3g",
168                 .dev_id         = ASUS_WMI_DEVID_WWAN3G,
169                 .description    = "WWAN-3G power control",
170                 .access         = CTLTYPE_INT | CTLFLAG_RW
171         },
172         {
173                 .name           = "uwb",
174                 .dev_id         = ASUS_WMI_DEVID_UWB,
175                 .description    = "UWB power control",
176                 .access         = CTLTYPE_INT | CTLFLAG_RW
177         },
178         {
179                 .name           = "led1",
180                 .dev_id         = ASUS_WMI_DEVID_LED1,
181                 .description    = "LED1 control",
182                 .access         = CTLTYPE_INT | CTLFLAG_RW
183         },
184         {
185                 .name           = "led2",
186                 .dev_id         = ASUS_WMI_DEVID_LED2,
187                 .description    = "LED2 control",
188                 .access         = CTLTYPE_INT | CTLFLAG_RW
189         },
190         {
191                 .name           = "led3",
192                 .dev_id         = ASUS_WMI_DEVID_LED3,
193                 .description    = "LED3 control",
194                 .access         = CTLTYPE_INT | CTLFLAG_RW
195         },
196         {
197                 .name           = "led4",
198                 .dev_id         = ASUS_WMI_DEVID_LED4,
199                 .description    = "LED4 control",
200                 .access         = CTLTYPE_INT | CTLFLAG_RW
201         },
202         {
203                 .name           = "led5",
204                 .dev_id         = ASUS_WMI_DEVID_LED5,
205                 .description    = "LED5 control",
206                 .access         = CTLTYPE_INT | CTLFLAG_RW
207         },
208         {
209                 .name           = "led6",
210                 .dev_id         = ASUS_WMI_DEVID_LED6,
211                 .description    = "LED6 control",
212                 .access         = CTLTYPE_INT | CTLFLAG_RW
213         },
214         {
215                 .name           = "backlight",
216                 .dev_id         = ASUS_WMI_DEVID_BACKLIGHT,
217                 .description    = "LCD backlight on/off control",
218                 .access         = CTLTYPE_INT | CTLFLAG_RW
219         },
220         {
221                 .name           = "brightness",
222                 .dev_id         = ASUS_WMI_DEVID_BRIGHTNESS,
223                 .description    = "LCD backlight brightness control",
224                 .access         = CTLTYPE_INT | CTLFLAG_RW
225         },
226         {
227                 .name           = "kbd_backlight",
228                 .dev_id         = ASUS_WMI_DEVID_KBD_BACKLIGHT,
229                 .description    = "Keyboard backlight brightness control",
230                 .access         = CTLTYPE_INT | CTLFLAG_RW
231         },
232         {
233                 .name           = "light_sensor",
234                 .dev_id         = ASUS_WMI_DEVID_LIGHT_SENSOR,
235                 .description    = "Ambient light sensor",
236                 .access         = CTLTYPE_INT | CTLFLAG_RW
237         },
238         {
239                 .name           = "camera",
240                 .dev_id         = ASUS_WMI_DEVID_CAMERA,
241                 .description    = "Camera power control",
242                 .access         = CTLTYPE_INT | CTLFLAG_RW
243         },
244         {
245                 .name           = "cardreader",
246                 .dev_id         = ASUS_WMI_DEVID_CARDREADER,
247                 .description    = "Cardreader power control",
248                 .access         = CTLTYPE_INT | CTLFLAG_RW
249         },
250         {
251                 .name           = "touchpad",
252                 .dev_id         = ASUS_WMI_DEVID_TOUCHPAD,
253                 .description    = "Touchpad control",
254                 .access         = CTLTYPE_INT | CTLFLAG_RW
255         },
256         {
257                 .name           = "touchpad_led",
258                 .dev_id         = ASUS_WMI_DEVID_TOUCHPAD_LED,
259                 .description    = "Touchpad LED control",
260                 .access         = CTLTYPE_INT | CTLFLAG_RW
261         },
262         {
263                 .name           = "themperature",
264                 .dev_id         = ASUS_WMI_DEVID_THERMAL_CTRL,
265                 .description    = "Temperature (C)",
266                 .access         = CTLTYPE_INT | CTLFLAG_RD
267         },
268         {
269                 .name           = "fan_speed",
270                 .dev_id         = ASUS_WMI_DEVID_FAN_CTRL,
271                 .description    = "Fan speed (0-3)",
272                 .access         = CTLTYPE_INT | CTLFLAG_RD
273         },
274         {
275                 .name           = "processor_state",
276                 .dev_id         = ASUS_WMI_DEVID_PROCESSOR_STATE,
277                 .description    = "Processor state",
278                 .access         = CTLTYPE_INT | CTLFLAG_RW
279         },
280         { NULL, 0, NULL, 0 }
281 };
282
283 ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device");
284
285 static void     acpi_asus_wmi_identify(driver_t *driver, device_t parent);
286 static int      acpi_asus_wmi_probe(device_t dev);
287 static int      acpi_asus_wmi_attach(device_t dev);
288 static int      acpi_asus_wmi_detach(device_t dev);
289
290 static int      acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);
291 static int      acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,
292                     int arg, int oldarg);
293 static int      acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id);
294 static int      acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
295                     UINT32 arg0, UINT32 arg1, UINT32 *retval);
296 static int      acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
297                     UINT32 dev_id, UINT32 *retval);
298 static int      acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
299                     UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
300 static void     acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
301
302 static device_method_t acpi_asus_wmi_methods[] = {
303         DEVMETHOD(device_identify, acpi_asus_wmi_identify),
304         DEVMETHOD(device_probe, acpi_asus_wmi_probe),
305         DEVMETHOD(device_attach, acpi_asus_wmi_attach),
306         DEVMETHOD(device_detach, acpi_asus_wmi_detach),
307
308         DEVMETHOD_END
309 };
310
311 static driver_t acpi_asus_wmi_driver = {
312         "acpi_asus_wmi",
313         acpi_asus_wmi_methods,
314         sizeof(struct acpi_asus_wmi_softc),
315 };
316
317 static devclass_t acpi_asus_wmi_devclass;
318
319 DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver,
320     acpi_asus_wmi_devclass, 0, 0);
321 MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);
322 MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);
323
324 static void
325 acpi_asus_wmi_identify(driver_t *driver, device_t parent)
326 {
327
328         /* Don't do anything if driver is disabled. */
329         if (acpi_disabled("asus_wmi"))
330                 return;
331
332         /* Add only a single device instance. */
333         if (device_find_child(parent, "acpi_asus_wmi", -1) != NULL)
334                 return;
335
336         /* Check management GUID to see whether system is compatible. */
337         if (!ACPI_WMI_PROVIDES_GUID_STRING(parent,
338             ACPI_ASUS_WMI_MGMT_GUID))
339                 return;
340
341         if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", -1) == NULL)
342                 device_printf(parent, "add acpi_asus_wmi child failed\n");
343 }
344
345 static int
346 acpi_asus_wmi_probe(device_t dev)
347 {
348
349         if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev),
350             ACPI_ASUS_WMI_MGMT_GUID))
351                 return (EINVAL);
352         device_set_desc(dev, "ASUS WMI device");
353         return (0);
354 }
355
356 static int
357 acpi_asus_wmi_attach(device_t dev)
358 {
359         struct acpi_asus_wmi_softc      *sc;
360         UINT32                  val;
361         int                     dev_id, i;
362
363         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
364
365         sc = device_get_softc(dev);
366         sc->dev = dev;
367         sc->wmi_dev = device_get_parent(dev);
368         sc->handle_keys = 1;
369
370         /* Check management GUID. */
371         if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
372             ACPI_ASUS_WMI_MGMT_GUID)) {
373                 device_printf(dev,
374                     "WMI device does not provide the ASUS management GUID\n");
375                 return (EINVAL);
376         }
377
378         /* Find proper DSTS method. */
379         sc->dsts_id = ASUS_WMI_METHODID_DSTS;
380 next:
381         for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {
382                 dev_id = acpi_asus_wmi_sysctls[i].dev_id;
383                 if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))
384                         continue;
385                 break;
386         }
387         if (acpi_asus_wmi_sysctls[i].name == NULL) {
388                 if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) {
389                         sc->dsts_id = ASUS_WMI_METHODID_DSTS2;
390                         goto next;
391                 } else {
392                         device_printf(dev, "Can not detect DSTS method ID\n");
393                         return (EINVAL);
394                 }
395         }
396
397         /* Find proper and attach to notufy GUID. */
398         if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
399             ACPI_ASUS_WMI_EVENT_GUID))
400                 sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID;
401         else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
402             ACPI_EEEPC_WMI_EVENT_GUID))
403                 sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID;
404         else
405                 sc->notify_guid = NULL;
406         if (sc->notify_guid != NULL) {
407                 if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
408                     sc->notify_guid, acpi_asus_wmi_notify, dev))
409                         sc->notify_guid = NULL;
410         }
411         if (sc->notify_guid == NULL)
412                 device_printf(dev, "Could not install event handler!\n");
413
414         /* Initialize. */
415         if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
416             ASUS_WMI_METHODID_INIT, 0, 0, &val) && bootverbose)
417                 device_printf(dev, "Initialization: %#x\n", val);
418         if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
419             ASUS_WMI_METHODID_SPEC, 0, 0x9, &val) && bootverbose)
420                 device_printf(dev, "WMI BIOS version: %d.%d\n",
421                     val >> 16, val & 0xFF);
422         if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
423             ASUS_WMI_METHODID_SFUN, 0, 0, &val) && bootverbose)
424                 device_printf(dev, "SFUN value: %#x\n", val);
425
426         ACPI_SERIAL_BEGIN(asus_wmi);
427
428         sc->sysctl_ctx = device_get_sysctl_ctx(dev);
429         sc->sysctl_tree = device_get_sysctl_tree(dev);
430         SYSCTL_ADD_INT(sc->sysctl_ctx,
431             SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
432             "handle_keys", CTLFLAG_RW, &sc->handle_keys,
433             0, "Handle some hardware keys inside the driver");
434         for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {
435                 dev_id = acpi_asus_wmi_sysctls[i].dev_id;
436                 if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))
437                         continue;
438                 switch (dev_id) {
439                 case ASUS_WMI_DEVID_THERMAL_CTRL:
440                 case ASUS_WMI_DEVID_PROCESSOR_STATE:
441                 case ASUS_WMI_DEVID_FAN_CTRL:
442                 case ASUS_WMI_DEVID_BRIGHTNESS:
443                         if (val == 0)
444                                 continue;
445                         break;
446                 default:
447                         if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0)
448                                 continue;
449                         break;
450                 }
451
452                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
453                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
454                     acpi_asus_wmi_sysctls[i].name,
455                     acpi_asus_wmi_sysctls[i].access,
456                     sc, i, acpi_asus_wmi_sysctl, "I",
457                     acpi_asus_wmi_sysctls[i].description);
458         }
459         ACPI_SERIAL_END(asus_wmi);
460
461         return (0);
462 }
463
464 static int
465 acpi_asus_wmi_detach(device_t dev)
466 {
467         struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
468         
469         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
470
471         if (sc->notify_guid)
472                 ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid);
473
474         return (0);
475 }
476
477 static int
478 acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS)
479 {
480         struct acpi_asus_wmi_softc      *sc;
481         int                     arg;
482         int                     oldarg;
483         int                     error = 0;
484         int                     function;
485         int                     dev_id;
486         
487         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
488
489         sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1;
490         function = oidp->oid_arg2;
491         dev_id = acpi_asus_wmi_sysctls[function].dev_id;
492
493         ACPI_SERIAL_BEGIN(asus_wmi);
494         arg = acpi_asus_wmi_sysctl_get(sc, dev_id);
495         oldarg = arg;
496         error = sysctl_handle_int(oidp, &arg, 0, req);
497         if (!error && req->newptr != NULL)
498                 error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg);
499         ACPI_SERIAL_END(asus_wmi);
500
501         return (error);
502 }
503
504 static int
505 acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id)
506 {
507         UINT32  val = 0;
508
509         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
510         ACPI_SERIAL_ASSERT(asus_wmi);
511
512         acpi_wpi_asus_get_devstate(sc, dev_id, &val);
513
514         switch(dev_id) {
515         case ASUS_WMI_DEVID_THERMAL_CTRL:
516                 val = (val - 2732 + 5) / 10;
517                 break;
518         case ASUS_WMI_DEVID_PROCESSOR_STATE:
519         case ASUS_WMI_DEVID_FAN_CTRL:
520                 break;
521         case ASUS_WMI_DEVID_BRIGHTNESS:
522                 val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK;
523                 break;
524         case ASUS_WMI_DEVID_KBD_BACKLIGHT:
525                 val &= 0x7;
526                 break;
527         default:
528                 if (val & ASUS_WMI_DSTS_UNKNOWN_BIT)
529                         val = -1;
530                 else
531                         val = !!(val & ASUS_WMI_DSTS_STATUS_BIT);
532                 break;
533         }
534
535         return (val);
536 }
537
538 static int
539 acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg)
540 {
541         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
542         ACPI_SERIAL_ASSERT(asus_wmi);
543
544         switch(dev_id) {
545         case ASUS_WMI_DEVID_KBD_BACKLIGHT:
546                 arg = min(0x7, arg);
547                 if (arg != 0)
548                         arg |= 0x80;
549                 break;
550         }
551
552         acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL);
553
554         return (0);
555 }
556
557 static __inline void
558 acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {
559         if (buf && buf->Pointer) {
560                 AcpiOsFree(buf->Pointer);
561         }
562 }
563
564 static void
565 acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
566 {
567         device_t dev = context;
568         ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
569         UINT32 val;
570         int code = 0;
571
572         struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
573         ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
574         ACPI_OBJECT *obj;
575         ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
576         obj = (ACPI_OBJECT*) response.Pointer;
577         if (obj && obj->Type == ACPI_TYPE_INTEGER) {
578                 code = obj->Integer.Value;
579                 acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT,
580                     code);
581         }
582         if (code && sc->handle_keys) {
583                 /* Keyboard backlight control. */
584                 if (code == 0xc4 || code == 0xc5) {
585                         acpi_wpi_asus_get_devstate(sc,
586                             ASUS_WMI_DEVID_KBD_BACKLIGHT, &val);
587                         val &= 0x7;
588                         if (code == 0xc4) {
589                                 if (val < 0x7)
590                                         val++;
591                         } else if (val > 0)
592                                 val--;
593                         if (val != 0)
594                                 val |= 0x80;
595                         acpi_wpi_asus_set_devstate(sc,
596                             ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL);
597                 }
598                 /* Touchpad control. */
599                 if (code == 0x6b) {
600                         acpi_wpi_asus_get_devstate(sc,
601                             ASUS_WMI_DEVID_TOUCHPAD, &val);
602                         val = !(val & 1);
603                         acpi_wpi_asus_set_devstate(sc,
604                             ASUS_WMI_DEVID_TOUCHPAD, val, NULL);
605                 }
606         }
607         acpi_asus_wmi_free_buffer(&response);
608 }
609
610 static int
611 acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
612     UINT32 arg0, UINT32 arg1, UINT32 *retval)
613 {
614         UINT32          params[2] = { arg0, arg1 };
615         UINT32          result;
616         ACPI_OBJECT     *obj;
617         ACPI_BUFFER     in = { sizeof(params), &params };
618         ACPI_BUFFER     out = { ACPI_ALLOCATE_BUFFER, NULL };
619         
620         if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev,
621             ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) {
622                 acpi_asus_wmi_free_buffer(&out);
623                 return (-EINVAL);
624         }
625         obj = out.Pointer;
626         if (obj && obj->Type == ACPI_TYPE_INTEGER)
627                 result = (UINT32) obj->Integer.Value;
628         else
629                 result = 0;
630         acpi_asus_wmi_free_buffer(&out);
631         if (retval)
632                 *retval = result;
633         return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0);
634 }
635
636 static int
637 acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
638     UINT32 dev_id, UINT32 *retval)
639 {
640
641         return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
642             sc->dsts_id, dev_id, 0, retval));
643 }
644
645 static int
646 acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
647     UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval)
648 {
649
650         return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
651             ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval));
652 }