]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/acpi_support/acpi_asus.c
Use a taskqueue for led-handling to prevent a potential panic.
[FreeBSD/FreeBSD.git] / sys / dev / acpi_support / acpi_asus.c
1 /*-
2  * Copyright (c) 2004 Philip Paeps <philip@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 /*
31  * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
32  * recent Asus (and Medion) laptops.  Inspired by the acpi4asus project which
33  * implements these features in the Linux kernel.
34  *
35  *   <http://sourceforge.net/projects/acpi4asus/>
36  *
37  * Currently should support most features, but could use some more testing.
38  * Particularly the display-switching stuff is a bit hairy.  If you have an
39  * Asus laptop which doesn't appear to be supported, or strange things happen
40  * when using this driver, please report to <acpi@FreeBSD.org>.
41  */
42
43 #include "opt_acpi.h"
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46 #include <sys/module.h>
47 #include <sys/bus.h>
48 #include <sys/sbuf.h>
49
50 #include "acpi.h"
51 #include <dev/acpica/acpivar.h>
52 #include <dev/led/led.h>
53
54 /* Methods */
55 #define ACPI_ASUS_METHOD_BRN    1
56 #define ACPI_ASUS_METHOD_DISP   2
57 #define ACPI_ASUS_METHOD_LCD    3
58
59 #define _COMPONENT      ACPI_OEM
60 ACPI_MODULE_NAME("ASUS")
61
62 struct acpi_asus_model {
63         char    *name;
64
65         char    *mled_set;
66         char    *tled_set;
67         char    *wled_set;
68
69         char    *brn_get;
70         char    *brn_set;
71         char    *brn_up;
72         char    *brn_dn;
73
74         char    *lcd_get;
75         char    *lcd_set;
76
77         char    *disp_get;
78         char    *disp_set;
79 };
80
81 struct acpi_asus_led {
82         struct acpi_asus_softc *sc;
83         struct cdev     *cdev;
84         int             busy;
85         int             state;
86         enum {
87                 ACPI_ASUS_LED_MLED,
88                 ACPI_ASUS_LED_TLED,
89                 ACPI_ASUS_LED_WLED,
90         } type;
91 };
92
93 struct acpi_asus_softc {
94         device_t                dev;
95         ACPI_HANDLE             handle;
96
97         struct acpi_asus_model  *model;
98         struct sysctl_ctx_list  sysctl_ctx;
99         struct sysctl_oid       *sysctl_tree;
100
101         struct acpi_asus_led    s_mled;
102         struct acpi_asus_led    s_tled;
103         struct acpi_asus_led    s_wled;
104
105         int                     s_brn;
106         int                     s_disp;
107         int                     s_lcd;
108 };
109
110 /*
111  * We can identify Asus laptops from the string they return
112  * as a result of calling the ATK0100 'INIT' method.
113  */
114 static struct acpi_asus_model acpi_asus_models[] = {
115         {
116                 .name           = "L2D",
117                 .mled_set       = "MLED",
118                 .wled_set       = "WLED",
119                 .brn_up         = "\\Q0E",
120                 .brn_dn         = "\\Q0F",
121                 .lcd_get        = "\\SGP0",
122                 .lcd_set        = "\\Q10"
123         },
124         {
125                 .name           = "L3C",
126                 .mled_set       = "MLED",
127                 .wled_set       = "WLED",
128                 .brn_get        = "GPLV",
129                 .brn_set        = "SPLV",
130                 .lcd_get        = "\\GL32",
131                 .lcd_set        = "\\_SB.PCI0.PX40.ECD0._Q10"
132         },
133         {
134                 .name           = "L3D",
135                 .mled_set       = "MLED",
136                 .wled_set       = "WLED",
137                 .brn_get        = "GPLV",
138                 .brn_set        = "SPLV",
139                 .lcd_get        = "\\BKLG",
140                 .lcd_set        = "\\Q10"
141         },
142         {
143                 .name           = "L3H",
144                 .mled_set       = "MLED",
145                 .wled_set       = "WLED",
146                 .brn_get        = "GPLV",
147                 .brn_set        = "SPLV",
148                 .lcd_get        = "\\_SB.PCI0.PM.PBC",
149                 .lcd_set        = "EHK",
150                 .disp_get       = "\\_SB.INFB",
151                 .disp_set       = "SDSP"
152         },
153         {
154                 .name           = "L4R",
155                 .mled_set       = "MLED",
156                 .wled_set       = "WLED",
157                 .brn_get        = "GPLV",
158                 .brn_set        = "SPLV",
159                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
160                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
161                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
162                 .disp_set       = "SDSP"
163         },
164         {
165                 .name           = "L8L"
166                 /* Only has hotkeys, apparantly */
167         },
168         {
169                 .name           = "M1A",
170                 .mled_set       = "MLED",
171                 .brn_up         = "\\_SB.PCI0.PX40.EC0.Q0E",
172                 .brn_dn         = "\\_SB.PCI0.PX40.EC0.Q0F",
173                 .lcd_get        = "\\PNOF",
174                 .lcd_set        = "\\_SB.PCI0.PX40.EC0.Q10"
175         },
176         {
177                 .name           = "M2E",
178                 .mled_set       = "MLED",
179                 .wled_set       = "WLED",
180                 .brn_get        = "GPLV",
181                 .brn_set        = "SPLV",
182                 .lcd_get        = "\\GP06",
183                 .lcd_set        = "\\Q10"
184         },
185         {
186                 .name           = "M6N",
187                 .mled_set       = "MLED",
188                 .wled_set       = "WLED",
189                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
190                 .lcd_get        = "\\_SB.BKLT",
191                 .brn_set        = "SPLV",
192                 .brn_get        = "GPLV",
193                 .disp_set       = "SDSP",
194                 .disp_get       = "\\SSTE"
195         },
196         {
197                 .name           = "M6R",
198                 .mled_set       = "MLED",
199                 .wled_set       = "WLED",
200                 .brn_get        = "GPLV",
201                 .brn_set        = "SPLV",
202                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
203                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
204                 .disp_get       = "\\SSTE",
205                 .disp_set       = "SDSP"
206         },
207
208         { .name = NULL }
209 };
210
211 /*
212  * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
213  * but they can't be probed quite the same way as Asus laptops.
214  */
215 static struct acpi_asus_model acpi_samsung_models[] = {
216         {
217                 .name           = "P30",
218                 .wled_set       = "WLED",
219                 .brn_up         = "\\_SB.PCI0.LPCB.EC0._Q68",
220                 .brn_dn         = "\\_SB.PCI0.LPCB.EC0._Q69",
221                 .lcd_get        = "\\BKLT",
222                 .lcd_set        = "\\_SB.PCI0.LPCB.EC0._Q0E"
223         },
224
225         { .name = NULL }
226 };
227
228 static struct {
229         char    *name;
230         char    *description;
231         int     method;
232 } acpi_asus_sysctls[] = {
233         {
234                 .name           = "lcd_backlight",
235                 .method         = ACPI_ASUS_METHOD_LCD,
236                 .description    = "state of the lcd backlight"
237         },
238         {
239                 .name           = "lcd_brightness",
240                 .method         = ACPI_ASUS_METHOD_BRN,
241                 .description    = "brightness of the lcd panel"
242         },
243         {
244                 .name           = "video_output",
245                 .method         = ACPI_ASUS_METHOD_DISP,
246                 .description    = "display output state"
247         },
248
249         { .name = NULL }
250 };
251
252 ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
253
254 /* Function prototypes */
255 static int      acpi_asus_probe(device_t dev);
256 static int      acpi_asus_attach(device_t dev);
257 static int      acpi_asus_detach(device_t dev);
258
259 static void     acpi_asus_led(struct acpi_asus_led *led, int state);
260 static void     acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
261
262 static int      acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
263 static int      acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
264 static int      acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
265 static int      acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
266
267 static void     acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
268
269 static device_method_t acpi_asus_methods[] = {
270         DEVMETHOD(device_probe,  acpi_asus_probe),
271         DEVMETHOD(device_attach, acpi_asus_attach),
272         DEVMETHOD(device_detach, acpi_asus_detach),
273
274         { 0, 0 }
275 };
276
277 static driver_t acpi_asus_driver = {
278         "acpi_asus",
279         acpi_asus_methods,
280         sizeof(struct acpi_asus_softc)
281 };
282
283 static devclass_t acpi_asus_devclass;
284
285 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
286 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
287
288 static int
289 acpi_asus_probe(device_t dev)
290 {
291         struct acpi_asus_model  *model;
292         struct acpi_asus_softc  *sc;
293         struct sbuf             *sb;
294         ACPI_BUFFER             Buf;
295         ACPI_OBJECT             Arg, *Obj;
296         ACPI_OBJECT_LIST        Args;
297         static char             *asus_ids[] = { "ATK0100", NULL };
298
299         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
300
301         if (acpi_disabled("asus") ||
302             ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids) == NULL)
303                 return (ENXIO);
304
305         sc = device_get_softc(dev);
306         sc->dev = dev;
307         sc->handle = acpi_get_handle(dev);
308
309         Arg.Type = ACPI_TYPE_INTEGER;
310         Arg.Integer.Value = 0;
311
312         Args.Count = 1;
313         Args.Pointer = &Arg;
314
315         Buf.Pointer = NULL;
316         Buf.Length = ACPI_ALLOCATE_BUFFER;
317
318         AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
319         Obj = Buf.Pointer;
320
321         /*
322          * The Samsung P30 returns a null-pointer from INIT, we
323          * can identify it from the 'ODEM' string in the DSDT.
324          */
325         if (Obj->String.Pointer == NULL) {
326                 ACPI_STATUS             status;
327                 ACPI_TABLE_HEADER       th;
328
329                 status = AcpiGetTableHeader(ACPI_TABLE_DSDT, 1, &th);
330                 if (ACPI_FAILURE(status)) {
331                         device_printf(dev, "Unsupported (Samsung?) laptop\n");
332                         AcpiOsFree(Buf.Pointer);
333                         return (ENXIO);
334                 }
335
336                 if (strncmp("ODEM", th.OemTableId, 4) == 0) {
337                         sc->model = &acpi_samsung_models[0];
338                         device_set_desc(dev, "Samsung P30 Laptop Extras");
339                         AcpiOsFree(Buf.Pointer);
340                         return (0);
341                 }
342         }
343
344         sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
345         if (sb == NULL)
346                 return (ENOMEM);
347
348         /*
349          * Asus laptops are simply identified by name, easy!
350          */
351         for (model = acpi_asus_models; model->name != NULL; model++)
352                 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
353                         sbuf_printf(sb, "Asus %s Laptop Extras", model->name);
354                         sbuf_finish(sb);
355
356                         sc->model = model;
357                         device_set_desc_copy(dev, sbuf_data(sb));
358
359                         sbuf_delete(sb);
360                         AcpiOsFree(Buf.Pointer);
361                         return (0);
362                 }
363
364         sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
365         sbuf_finish(sb);
366
367         device_printf(dev, sbuf_data(sb));
368
369         sbuf_delete(sb);
370         AcpiOsFree(Buf.Pointer);
371
372         return (ENXIO);
373 }
374
375 static int
376 acpi_asus_attach(device_t dev)
377 {
378         struct acpi_asus_softc  *sc;
379         struct acpi_softc       *acpi_sc;
380
381         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
382
383         sc = device_get_softc(dev);
384         acpi_sc = acpi_device_get_parent_softc(dev);
385
386         /* Build sysctl tree */
387         sysctl_ctx_init(&sc->sysctl_ctx);
388         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
389             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
390             OID_AUTO, "asus", CTLFLAG_RD, 0, "");
391
392         /* Hook up nodes */
393         for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
394                 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
395                         continue;
396
397                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
398                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
399                     acpi_asus_sysctls[i].name,
400                     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
401                     sc, i, acpi_asus_sysctl, "I",
402                     acpi_asus_sysctls[i].description);
403         }
404
405         /* Attach leds */
406         if (sc->model->mled_set) {
407                 sc->s_mled.busy = 0;
408                 sc->s_mled.sc = sc;
409                 sc->s_mled.type = ACPI_ASUS_LED_MLED;
410                 sc->s_mled.cdev =
411                     led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
412         }
413
414         if (sc->model->tled_set) {
415                 sc->s_tled.busy = 0;
416                 sc->s_tled.sc = sc;
417                 sc->s_tled.type = ACPI_ASUS_LED_TLED;
418                 sc->s_tled.cdev =
419                     led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled");
420         }
421
422         if (sc->model->wled_set) {
423                 sc->s_wled.busy = 0;
424                 sc->s_wled.sc = sc;
425                 sc->s_wled.type = ACPI_ASUS_LED_WLED;
426                 sc->s_wled.cdev =
427                     led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled");
428         }
429
430         /* Activate hotkeys */
431         AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
432
433         /* Handle notifies */
434         AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
435             acpi_asus_notify, dev);
436
437         return (0);
438 }
439
440 static int
441 acpi_asus_detach(device_t dev)
442 {
443         struct acpi_asus_softc  *sc;
444
445         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
446
447         sc = device_get_softc(dev);
448
449         /* Turn the lights off */
450         if (sc->model->mled_set)
451                 led_destroy(sc->s_mled.cdev);
452
453         if (sc->model->tled_set)
454                 led_destroy(sc->s_tled.cdev);
455
456         if (sc->model->wled_set)
457                 led_destroy(sc->s_wled.cdev);
458
459         /* Remove notify handler */
460         AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
461             acpi_asus_notify);
462
463         /* Free sysctl tree */
464         sysctl_ctx_free(&sc->sysctl_ctx);
465
466         return (0);
467 }
468
469 static void
470 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
471 {
472         struct acpi_asus_softc  *sc;
473         char                    *method;
474         int                     state;
475         
476         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
477
478         sc = led->sc;
479
480         switch (led->type) {
481         case ACPI_ASUS_LED_MLED:
482                 method = sc->model->mled_set;
483
484                 /* Note: inverted */
485                 state = !led->state;
486                 break;
487         case ACPI_ASUS_LED_TLED:
488                 method = sc->model->tled_set;
489                 state = led->state;
490                 break;
491         case ACPI_ASUS_LED_WLED:
492                 method = sc->model->wled_set;
493                 state = led->state;
494                 break;
495         default:
496                 printf("acpi_asus_led: invalid LED type %d\n",
497                     (int)led->type);
498                 return;
499         }
500
501         acpi_SetInteger(sc->handle, method, state);
502         led->busy = 0;
503 }
504         
505 static void
506 acpi_asus_led(struct acpi_asus_led *led, int state)
507 {
508
509         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
510
511         if (led->busy)
512                 return;
513
514         led->busy = 1;
515         led->state = state;
516
517         AcpiOsQueueForExecution(OSD_PRIORITY_LO,
518             (void *)acpi_asus_led_task, led);
519 }
520
521 static int
522 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
523 {
524         struct acpi_asus_softc  *sc;
525         int                     arg;
526         int                     error = 0;
527         int                     function;
528         int                     method;
529         
530         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
531
532         sc = (struct acpi_asus_softc *)oidp->oid_arg1;
533         function = oidp->oid_arg2;
534         method = acpi_asus_sysctls[function].method;
535
536         ACPI_SERIAL_BEGIN(asus);
537         arg = acpi_asus_sysctl_get(sc, method);
538         error = sysctl_handle_int(oidp, &arg, 0, req);
539
540         /* Sanity check */
541         if (error != 0 || req->newptr == NULL)
542                 goto out;
543
544         /* Update */
545         error = acpi_asus_sysctl_set(sc, method, arg);
546
547 out:
548         ACPI_SERIAL_END(asus);
549         return (error);
550 }
551
552 static int
553 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
554 {
555         int val = 0;
556
557         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
558         ACPI_SERIAL_ASSERT(asus);
559
560         switch (method) {
561         case ACPI_ASUS_METHOD_BRN:
562                 val = sc->s_brn;
563                 break;
564         case ACPI_ASUS_METHOD_DISP:
565                 val = sc->s_disp;
566                 break;
567         case ACPI_ASUS_METHOD_LCD:
568                 val = sc->s_lcd;
569                 break;
570         }
571
572         return (val);
573 }
574
575 static int
576 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
577 {
578         ACPI_STATUS     status = AE_OK;
579
580         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
581         ACPI_SERIAL_ASSERT(asus);
582
583         switch (method) {
584         case ACPI_ASUS_METHOD_BRN:
585                 if (arg < 0 || arg > 15)
586                         return (EINVAL);
587
588                 if (sc->model->brn_set)
589                         status = acpi_SetInteger(sc->handle,
590                             sc->model->brn_set, arg);
591                 else {
592                         while (arg != 0) {
593                                 status = AcpiEvaluateObject(sc->handle,
594                                     (arg > 0) ?  sc->model->brn_up :
595                                     sc->model->brn_dn, NULL, NULL);
596                                 (arg > 0) ? arg-- : arg++;
597                         }
598                 }
599
600                 if (ACPI_SUCCESS(status))
601                         sc->s_brn = arg;
602
603                 break;
604         case ACPI_ASUS_METHOD_DISP:
605                 if (arg < 0 || arg > 7)
606                         return (EINVAL);
607
608                 status = acpi_SetInteger(sc->handle,
609                     sc->model->disp_set, arg);
610
611                 if (ACPI_SUCCESS(status))
612                         sc->s_disp = arg;
613
614                 break;
615         case ACPI_ASUS_METHOD_LCD:
616                 if (arg < 0 || arg > 1)
617                         return (EINVAL);
618
619                 if (strncmp(sc->model->name, "L3H", 3) != 0)
620                         status = AcpiEvaluateObject(sc->handle,
621                             sc->model->lcd_set, NULL, NULL);
622                 else
623                         status = acpi_SetInteger(sc->handle,
624                             sc->model->lcd_set, 0x7);
625
626                 if (ACPI_SUCCESS(status))
627                         sc->s_lcd = arg;
628
629                 break;
630         }
631
632         return (0);
633 }
634
635 static int
636 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
637 {
638         ACPI_STATUS     status;
639
640         switch (method) {
641         case ACPI_ASUS_METHOD_BRN:
642                 if (sc->model->brn_get) {
643                         /* GPLV/SPLV models */
644                         status = acpi_GetInteger(sc->handle,
645                             sc->model->brn_get, &sc->s_brn);
646                         if (ACPI_SUCCESS(status))
647                                 return (TRUE);
648                 } else if (sc->model->brn_up) {
649                         /* Relative models */
650                         status = AcpiEvaluateObject(sc->handle,
651                             sc->model->brn_up, NULL, NULL);
652                         if (ACPI_FAILURE(status))
653                                 return (FALSE);
654
655                         status = AcpiEvaluateObject(sc->handle,
656                             sc->model->brn_dn, NULL, NULL);
657                         if (ACPI_FAILURE(status))
658                                 return (FALSE);
659
660                         return (TRUE);
661                 }
662                 return (FALSE);
663         case ACPI_ASUS_METHOD_DISP:
664                 if (sc->model->disp_get) {
665                         status = acpi_GetInteger(sc->handle,
666                             sc->model->disp_get, &sc->s_disp);
667                         if (ACPI_SUCCESS(status))
668                                 return (TRUE);
669                 }
670                 return (FALSE);
671         case ACPI_ASUS_METHOD_LCD:
672                 if (sc->model->lcd_get &&
673                     strncmp(sc->model->name, "L3H", 3) != 0) {
674                         status = acpi_GetInteger(sc->handle,
675                             sc->model->lcd_get, &sc->s_lcd);
676                         if (ACPI_SUCCESS(status))
677                                 return (TRUE);
678                 }
679                 else if (sc->model->lcd_get) {
680                         ACPI_BUFFER             Buf;
681                         ACPI_OBJECT             Arg[2], Obj;
682                         ACPI_OBJECT_LIST        Args;
683
684                         /* L3H is a bit special */
685                         Arg[0].Type = ACPI_TYPE_INTEGER;
686                         Arg[0].Integer.Value = 0x02;
687                         Arg[1].Type = ACPI_TYPE_INTEGER;
688                         Arg[1].Integer.Value = 0x03;
689
690                         Args.Count = 2;
691                         Args.Pointer = Arg;
692
693                         Buf.Length = sizeof(Obj);
694                         Buf.Pointer = &Obj;
695
696                         status = AcpiEvaluateObject(sc->handle,
697                             sc->model->lcd_get, &Args, &Buf);
698                         if (ACPI_SUCCESS(status) &&
699                             Obj.Type == ACPI_TYPE_INTEGER) {
700                                 sc->s_lcd = Obj.Integer.Value >> 8;
701                                 return (TRUE);
702                         }
703                 }
704                 return (FALSE);
705         }
706         return (FALSE);
707 }
708
709 static void
710 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
711 {
712         struct acpi_asus_softc  *sc;
713         struct acpi_softc       *acpi_sc;
714
715         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
716
717         sc = device_get_softc((device_t)context);
718         acpi_sc = acpi_device_get_parent_softc(sc->dev);
719
720         ACPI_SERIAL_BEGIN(asus);
721         if ((notify & ~0x10) <= 15) {
722                 sc->s_brn = notify & ~0x10;
723                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
724         } else if ((notify & ~0x20) <= 15) {
725                 sc->s_brn = notify & ~0x20;
726                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
727         } else if (notify == 0x33) {
728                 sc->s_lcd = 1;
729                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
730         } else if (notify == 0x34) {
731                 sc->s_lcd = 0;
732                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
733         } else {
734                 /* Notify devd(8) */
735                 acpi_UserNotify("ASUS", h, notify);
736         }
737         ACPI_SERIAL_END(asus);
738 }