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