]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/acpi_support/acpi_asus.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / acpi_support / acpi_asus.c
1 /*-
2  * Copyright (c) 2004, 2005 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 <contrib/dev/acpica/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 #define ACPI_ASUS_METHOD_CAMERA 4
59 #define ACPI_ASUS_METHOD_CARDRD 5
60 #define ACPI_ASUS_METHOD_WLAN   6
61
62 #define _COMPONENT      ACPI_OEM
63 ACPI_MODULE_NAME("ASUS")
64
65 struct acpi_asus_model {
66         char    *name;
67
68         char    *bled_set;
69         char    *dled_set;
70         char    *gled_set;
71         char    *mled_set;
72         char    *tled_set;
73         char    *wled_set;
74
75         char    *brn_get;
76         char    *brn_set;
77         char    *brn_up;
78         char    *brn_dn;
79
80         char    *lcd_get;
81         char    *lcd_set;
82
83         char    *disp_get;
84         char    *disp_set;
85
86         char    *cam_get;
87         char    *cam_set;
88
89         char    *crd_get;
90         char    *crd_set;
91
92         char    *wlan_get;
93         char    *wlan_set;
94
95         void    (*n_func)(ACPI_HANDLE, UINT32, void *);
96
97         char    *lcdd;
98         void    (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *);
99 };
100
101 struct acpi_asus_led {
102         struct acpi_asus_softc *sc;
103         struct cdev     *cdev;
104         int             busy;
105         int             state;
106         enum {
107                 ACPI_ASUS_LED_BLED,
108                 ACPI_ASUS_LED_DLED,
109                 ACPI_ASUS_LED_GLED,
110                 ACPI_ASUS_LED_MLED,
111                 ACPI_ASUS_LED_TLED,
112                 ACPI_ASUS_LED_WLED,
113         } type;
114 };
115
116 struct acpi_asus_softc {
117         device_t                dev;
118         ACPI_HANDLE             handle;
119         ACPI_HANDLE             lcdd_handle;
120
121         struct acpi_asus_model  *model;
122         struct sysctl_ctx_list  sysctl_ctx;
123         struct sysctl_oid       *sysctl_tree;
124
125         struct acpi_asus_led    s_bled;
126         struct acpi_asus_led    s_dled;
127         struct acpi_asus_led    s_gled;
128         struct acpi_asus_led    s_mled;
129         struct acpi_asus_led    s_tled;
130         struct acpi_asus_led    s_wled;
131
132         int                     s_brn;
133         int                     s_disp;
134         int                     s_lcd;
135         int                     s_cam;
136         int                     s_crd;
137         int                     s_wlan;
138 };
139
140 static void     acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify,
141     void *context);
142
143 /*
144  * We can identify Asus laptops from the string they return
145  * as a result of calling the ATK0100 'INIT' method.
146  */
147 static struct acpi_asus_model acpi_asus_models[] = {
148         {
149                 .name           = "xxN",
150                 .mled_set       = "MLED",
151                 .wled_set       = "WLED",
152                 .lcd_get        = "\\BKLT",
153                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
154                 .brn_get        = "GPLV",
155                 .brn_set        = "SPLV",
156                 .disp_get       = "\\ADVG",
157                 .disp_set       = "SDSP"
158         },
159         {
160                 .name           = "A1x",
161                 .mled_set       = "MLED",
162                 .lcd_get        = "\\BKLI",
163                 .lcd_set        = "\\_SB.PCI0.ISA.EC0._Q10",
164                 .brn_up         = "\\_SB.PCI0.ISA.EC0._Q0E",
165                 .brn_dn         = "\\_SB.PCI0.ISA.EC0._Q0F"
166         },
167         {
168                 .name           = "A2x",
169                 .mled_set       = "MLED",
170                 .wled_set       = "WLED",
171                 .lcd_get        = "\\BAOF",
172                 .lcd_set        = "\\Q10",
173                 .brn_get        = "GPLV",
174                 .brn_set        = "SPLV",
175                 .disp_get       = "\\INFB",
176                 .disp_set       = "SDSP"
177         },
178         {
179                 .name           = "A3N",
180                 .mled_set       = "MLED",
181                 .bled_set       = "BLED",
182                 .wled_set       = "WLED",
183                 .lcd_get        = NULL,
184                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
185                 .brn_set        = "SPLV",
186                 .brn_get        = "SDSP",
187                 .disp_set       = "SDSP",
188                 .disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD"
189         },
190         {
191                 .name           = "A4D",
192                 .mled_set       = "MLED",
193                 .brn_up         = "\\_SB_.PCI0.SBRG.EC0._Q0E",
194                 .brn_dn         = "\\_SB_.PCI0.SBRG.EC0._Q0F",
195                 .brn_get        = "GPLV",
196                 .brn_set        = "SPLV",
197 #ifdef notyet
198                 .disp_get       = "\\_SB_.PCI0.SBRG.EC0._Q10",
199                 .disp_set       = "\\_SB_.PCI0.SBRG.EC0._Q11"
200 #endif
201         },
202         {
203                 .name           = "A6V",
204                 .bled_set       = "BLED",
205                 .mled_set       = "MLED",
206                 .wled_set       = "WLED",
207                 .lcd_get        = NULL,
208                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
209                 .brn_get        = "GPLV",
210                 .brn_set        = "SPLV",
211                 .disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD",
212                 .disp_set       = "SDSP"
213         },
214         {
215                 .name           = "A8SR",
216                 .bled_set       = "BLED",
217                 .mled_set       = "MLED",
218                 .wled_set       = "WLED",
219                 .lcd_get        = NULL,
220                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
221                 .brn_get        = "GPLV",
222                 .brn_set        = "SPLV",
223                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
224                 .disp_set       = "SDSP",
225                 .lcdd           = "\\_SB.PCI0.P0P1.VGA.LCDD",
226                 .lcdd_n_func    = acpi_asus_lcdd_notify
227         },
228         {
229                 .name           = "D1x",
230                 .mled_set       = "MLED",
231                 .lcd_get        = "\\GP11",
232                 .lcd_set        = "\\Q0D",
233                 .brn_up         = "\\Q0C",
234                 .brn_dn         = "\\Q0B",
235                 .disp_get       = "\\INFB",
236                 .disp_set       = "SDSP"
237         },
238         {
239                 .name           = "G2K",
240                 .bled_set       = "BLED",
241                 .dled_set       = "DLED",
242                 .gled_set       = "GLED",
243                 .mled_set       = "MLED",
244                 .tled_set       = "TLED",
245                 .wled_set       = "WLED",
246                 .brn_get        = "GPLV",
247                 .brn_set        = "SPLV",
248                 .lcd_get        = "\\_SB.PCI0.SBRG.EC0.RPIN",
249                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
250                 .disp_get       = "\\_SB.PCI0.PCE2.VGA.GETD",
251                 .disp_set       = "SDSP",
252         },
253         {
254                 .name           = "L2D",
255                 .mled_set       = "MLED",
256                 .wled_set       = "WLED",
257                 .brn_up         = "\\Q0E",
258                 .brn_dn         = "\\Q0F",
259                 .lcd_get        = "\\SGP0",
260                 .lcd_set        = "\\Q10"
261         },
262         {
263                 .name           = "L3C",
264                 .mled_set       = "MLED",
265                 .wled_set       = "WLED",
266                 .brn_get        = "GPLV",
267                 .brn_set        = "SPLV",
268                 .lcd_get        = "\\GL32",
269                 .lcd_set        = "\\_SB.PCI0.PX40.ECD0._Q10"
270         },
271         {
272                 .name           = "L3D",
273                 .mled_set       = "MLED",
274                 .wled_set       = "WLED",
275                 .brn_get        = "GPLV",
276                 .brn_set        = "SPLV",
277                 .lcd_get        = "\\BKLG",
278                 .lcd_set        = "\\Q10"
279         },
280         {
281                 .name           = "L3H",
282                 .mled_set       = "MLED",
283                 .wled_set       = "WLED",
284                 .brn_get        = "GPLV",
285                 .brn_set        = "SPLV",
286                 .lcd_get        = "\\_SB.PCI0.PM.PBC",
287                 .lcd_set        = "EHK",
288                 .disp_get       = "\\_SB.INFB",
289                 .disp_set       = "SDSP"
290         },
291         {
292                 .name           = "L4R",
293                 .mled_set       = "MLED",
294                 .wled_set       = "WLED",
295                 .brn_get        = "GPLV",
296                 .brn_set        = "SPLV",
297                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
298                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
299                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
300                 .disp_set       = "SDSP"
301         },
302         {
303                 .name           = "L5x",
304                 .mled_set       = "MLED",
305                 .tled_set       = "TLED",
306                 .lcd_get        = "\\BAOF",
307                 .lcd_set        = "\\Q0D",
308                 .brn_get        = "GPLV",
309                 .brn_set        = "SPLV",
310                 .disp_get       = "\\INFB",
311                 .disp_set       = "SDSP"
312         },
313         {
314                 .name           = "L8L"
315                 /* Only has hotkeys, apparently */
316         },
317         {
318                 .name           = "M1A",
319                 .mled_set       = "MLED",
320                 .brn_up         = "\\_SB.PCI0.PX40.EC0.Q0E",
321                 .brn_dn         = "\\_SB.PCI0.PX40.EC0.Q0F",
322                 .lcd_get        = "\\PNOF",
323                 .lcd_set        = "\\_SB.PCI0.PX40.EC0.Q10"
324         },
325         {
326                 .name           = "M2E",
327                 .mled_set       = "MLED",
328                 .wled_set       = "WLED",
329                 .brn_get        = "GPLV",
330                 .brn_set        = "SPLV",
331                 .lcd_get        = "\\GP06",
332                 .lcd_set        = "\\Q10"
333         },
334         {
335                 .name           = "M6N",
336                 .mled_set       = "MLED",
337                 .wled_set       = "WLED",
338                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
339                 .lcd_get        = "\\_SB.BKLT",
340                 .brn_set        = "SPLV",
341                 .brn_get        = "GPLV",
342                 .disp_set       = "SDSP",
343                 .disp_get       = "\\SSTE"
344         },
345         {
346                 .name           = "M6R",
347                 .mled_set       = "MLED",
348                 .wled_set       = "WLED",
349                 .brn_get        = "GPLV",
350                 .brn_set        = "SPLV",
351                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
352                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
353                 .disp_get       = "\\SSTE",
354                 .disp_set       = "SDSP"
355         },
356         {
357                 .name           = "S1x",
358                 .mled_set       = "MLED",
359                 .wled_set       = "WLED",
360                 .lcd_get        = "\\PNOF",
361                 .lcd_set        = "\\_SB.PCI0.PX40.Q10",
362                 .brn_get        = "GPLV",
363                 .brn_set        = "SPLV"
364         },
365         {
366                 .name           = "S2x",
367                 .mled_set       = "MLED",
368                 .lcd_get        = "\\BKLI",
369                 .lcd_set        = "\\_SB.PCI0.ISA.EC0._Q10",
370                 .brn_up         = "\\_SB.PCI0.ISA.EC0._Q0B",
371                 .brn_dn         = "\\_SB.PCI0.ISA.EC0._Q0A"
372         },
373         {
374                 .name           = "V6V",
375                 .bled_set       = "BLED",
376                 .tled_set       = "TLED",
377                 .wled_set       = "WLED",
378                 .lcd_get        = "\\BKLT",
379                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
380                 .brn_get        = "GPLV",
381                 .brn_set        = "SPLV",
382                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
383                 .disp_set       = "SDSP"
384         },
385         {
386                 .name           = "W5A",
387                 .bled_set       = "BLED",
388                 .lcd_get        = "\\BKLT",
389                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
390                 .brn_get        = "GPLV",
391                 .brn_set        = "SPLV",
392                 .disp_get       = "\\_SB.PCI0.P0P2.VGA.GETD",
393                 .disp_set       = "SDSP"
394         },
395
396         { .name = NULL }
397 };
398
399 /*
400  * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
401  * but they can't be probed quite the same way as Asus laptops.
402  */
403 static struct acpi_asus_model acpi_samsung_models[] = {
404         {
405                 .name           = "P30",
406                 .wled_set       = "WLED",
407                 .brn_up         = "\\_SB.PCI0.LPCB.EC0._Q68",
408                 .brn_dn         = "\\_SB.PCI0.LPCB.EC0._Q69",
409                 .lcd_get        = "\\BKLT",
410                 .lcd_set        = "\\_SB.PCI0.LPCB.EC0._Q0E"
411         },
412
413         { .name = NULL }
414 };
415
416 static void     acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
417
418 /*
419  * EeePC have an Asus ASUS010 gadget interface,
420  * but they can't be probed quite the same way as Asus laptops.
421  */
422 static struct acpi_asus_model acpi_eeepc_models[] = {
423         {
424                 .name           = "EEE",
425                 .brn_get        = "\\_SB.ATKD.PBLG",
426                 .brn_set        = "\\_SB.ATKD.PBLS",
427                 .cam_get        = "\\_SB.ATKD.CAMG",
428                 .cam_set        = "\\_SB.ATKD.CAMS",
429                 .crd_set        = "\\_SB.ATKD.CRDS",
430                 .crd_get        = "\\_SB.ATKD.CRDG",
431                 .wlan_get       = "\\_SB.ATKD.WLDG",
432                 .wlan_set       = "\\_SB.ATKD.WLDS",
433                 .n_func         = acpi_asus_eeepc_notify
434         },
435
436         { .name = NULL }
437 };
438
439 static struct {
440         char    *name;
441         char    *description;
442         int     method;
443         int     flags;
444 } acpi_asus_sysctls[] = {
445         {
446                 .name           = "lcd_backlight",
447                 .method         = ACPI_ASUS_METHOD_LCD,
448                 .description    = "state of the lcd backlight",
449                 .flags          = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
450         },
451         {
452                 .name           = "lcd_brightness",
453                 .method         = ACPI_ASUS_METHOD_BRN,
454                 .description    = "brightness of the lcd panel",
455                 .flags          = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
456         },
457         {
458                 .name           = "video_output",
459                 .method         = ACPI_ASUS_METHOD_DISP,
460                 .description    = "display output state",
461                 .flags          = CTLTYPE_INT | CTLFLAG_RW
462         },
463         {
464                 .name           = "camera",
465                 .method         = ACPI_ASUS_METHOD_CAMERA,
466                 .description    = "internal camera state",  
467                 .flags          = CTLTYPE_INT | CTLFLAG_RW
468         },
469         {
470                 .name           = "cardreader",
471                 .method         = ACPI_ASUS_METHOD_CARDRD,
472                 .description    = "internal card reader state",
473                 .flags          = CTLTYPE_INT | CTLFLAG_RW
474         },
475         {
476                 .name           = "wlan",
477                 .method         = ACPI_ASUS_METHOD_WLAN,
478                 .description    = "wireless lan state",
479                 .flags          = CTLTYPE_INT | CTLFLAG_RW
480         },
481
482         { .name = NULL }
483 };
484
485 ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
486
487 /* Function prototypes */
488 static int      acpi_asus_probe(device_t dev);
489 static int      acpi_asus_attach(device_t dev);
490 static int      acpi_asus_detach(device_t dev);
491
492 static void     acpi_asus_led(struct acpi_asus_led *led, int state);
493 static void     acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
494
495 static int      acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
496 static int      acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
497 static int      acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
498 static int      acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
499
500 static void     acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
501
502 static device_method_t acpi_asus_methods[] = {
503         DEVMETHOD(device_probe,  acpi_asus_probe),
504         DEVMETHOD(device_attach, acpi_asus_attach),
505         DEVMETHOD(device_detach, acpi_asus_detach),
506
507         { 0, 0 }
508 };
509
510 static driver_t acpi_asus_driver = {
511         "acpi_asus",
512         acpi_asus_methods,
513         sizeof(struct acpi_asus_softc)
514 };
515
516 static devclass_t acpi_asus_devclass;
517
518 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
519 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
520
521 static int
522 acpi_asus_probe(device_t dev)
523 {
524         struct acpi_asus_model  *model;
525         struct acpi_asus_softc  *sc;
526         struct sbuf             *sb;
527         ACPI_BUFFER             Buf;
528         ACPI_OBJECT             Arg, *Obj;
529         ACPI_OBJECT_LIST        Args;
530         static char             *asus_ids[] = { "ATK0100", "ASUS010", NULL };
531         char *rstr;
532
533         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
534
535         if (acpi_disabled("asus"))
536                 return (ENXIO);
537         rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
538         if (rstr == NULL) {
539                 return (ENXIO);
540         }
541
542         sc = device_get_softc(dev);
543         sc->dev = dev;
544         sc->handle = acpi_get_handle(dev);
545
546         Arg.Type = ACPI_TYPE_INTEGER;
547         Arg.Integer.Value = 0;
548
549         Args.Count = 1;
550         Args.Pointer = &Arg;
551
552         Buf.Pointer = NULL;
553         Buf.Length = ACPI_ALLOCATE_BUFFER;
554
555         AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
556         Obj = Buf.Pointer;
557
558         /*
559          * The Samsung P30 returns a null-pointer from INIT, we
560          * can identify it from the 'ODEM' string in the DSDT.
561          */
562         if (Obj->String.Pointer == NULL) {
563                 ACPI_STATUS             status;
564                 ACPI_TABLE_HEADER       th;
565
566                 status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
567                 if (ACPI_FAILURE(status)) {
568                         device_printf(dev, "Unsupported (Samsung?) laptop\n");
569                         AcpiOsFree(Buf.Pointer);
570                         return (ENXIO);
571                 }
572
573                 if (strncmp("ODEM", th.OemTableId, 4) == 0) {
574                         sc->model = &acpi_samsung_models[0];
575                         device_set_desc(dev, "Samsung P30 Laptop Extras");
576                         AcpiOsFree(Buf.Pointer);
577                         return (0);
578                 }
579
580                 /* if EeePC */
581                 if (strncmp("ASUS010", rstr, 7) == 0) {
582                         sc->model = &acpi_eeepc_models[0];
583                         device_set_desc(dev, "ASUS EeePC");
584                         AcpiOsFree(Buf.Pointer);
585                         return (0);
586                 }
587         }
588
589         sb = sbuf_new_auto();
590         if (sb == NULL)
591                 return (ENOMEM);
592
593         /*
594          * Asus laptops are simply identified by name, easy!
595          */
596         for (model = acpi_asus_models; model->name != NULL; model++) {
597                 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
598
599 good:
600                         sbuf_printf(sb, "Asus %s Laptop Extras",
601                             Obj->String.Pointer);
602                         sbuf_finish(sb);
603
604                         sc->model = model;
605                         device_set_desc_copy(dev, sbuf_data(sb));
606
607                         sbuf_delete(sb);
608                         AcpiOsFree(Buf.Pointer);
609                         return (0);
610                 }
611                 
612                 /*
613                  * Some models look exactly the same as other models, but have
614                  * their own ids.  If we spot these, set them up with the same
615                  * details as the models they're like, possibly dealing with
616                  * small differences.
617                  *
618                  * XXX: there must be a prettier way to do this!
619                  */
620                 else if (strncmp(model->name, "xxN", 3) == 0 &&
621                     (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
622                      strncmp(Obj->String.Pointer, "S1N", 3) == 0))
623                         goto good;
624                 else if (strncmp(model->name, "A1x", 3) == 0 &&
625                     strncmp(Obj->String.Pointer, "A1", 2) == 0)
626                         goto good;
627                 else if (strncmp(model->name, "A2x", 3) == 0 &&
628                     strncmp(Obj->String.Pointer, "A2", 2) == 0)
629                         goto good;
630                 else if (strncmp(model->name, "D1x", 3) == 0 &&
631                     strncmp(Obj->String.Pointer, "D1", 2) == 0)
632                         goto good;
633                 else if (strncmp(model->name, "L3H", 3) == 0 &&
634                     strncmp(Obj->String.Pointer, "L2E", 3) == 0)
635                         goto good;
636                 else if (strncmp(model->name, "L5x", 3) == 0 &&
637                     strncmp(Obj->String.Pointer, "L5", 2) == 0)
638                         goto good;
639                 else if (strncmp(model->name, "M2E", 3) == 0 &&
640                     (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
641                      strncmp(Obj->String.Pointer, "L4E", 3) == 0))
642                         goto good;
643                 else if (strncmp(model->name, "S1x", 3) == 0 &&
644                     (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
645                      strncmp(Obj->String.Pointer, "S1", 2) == 0))
646                         goto good;
647                 else if (strncmp(model->name, "S2x", 3) == 0 &&
648                     (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
649                      strncmp(Obj->String.Pointer, "S2", 2) == 0))
650                         goto good;
651
652                 /* L2B is like L3C but has no lcd_get method */
653                 else if (strncmp(model->name, "L3C", 3) == 0 &&
654                     strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
655                         model->lcd_get = NULL;
656                         goto good;
657                 }
658
659                 /* A3G is like M6R but with a different lcd_get method */
660                 else if (strncmp(model->name, "M6R", 3) == 0 &&
661                     strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
662                         model->lcd_get = "\\BLFG";
663                         goto good;
664                 }
665
666                 /* M2N and W1N are like xxN with added WLED */
667                 else if (strncmp(model->name, "xxN", 3) == 0 &&
668                     (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
669                      strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
670                         model->wled_set = "WLED";
671                         goto good;
672                 }
673
674                 /* M5N and S5N are like xxN without MLED */
675                 else if (strncmp(model->name, "xxN", 3) == 0 &&
676                     (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
677                      strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
678                         model->mled_set = NULL;
679                         goto good;
680                 }
681         }
682
683         sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
684         sbuf_finish(sb);
685
686         device_printf(dev, sbuf_data(sb));
687
688         sbuf_delete(sb);
689         AcpiOsFree(Buf.Pointer);
690
691         return (ENXIO);
692 }
693
694 static int
695 acpi_asus_attach(device_t dev)
696 {
697         struct acpi_asus_softc  *sc;
698         struct acpi_softc       *acpi_sc;
699
700         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
701
702         sc = device_get_softc(dev);
703         acpi_sc = acpi_device_get_parent_softc(dev);
704
705         /* Build sysctl tree */
706         sysctl_ctx_init(&sc->sysctl_ctx);
707         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
708             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
709             OID_AUTO, "asus", CTLFLAG_RD, 0, "");
710
711         /* Hook up nodes */
712         for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
713                 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
714                         continue;
715
716                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
717                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
718                     acpi_asus_sysctls[i].name,
719                     acpi_asus_sysctls[i].flags,
720                     sc, i, acpi_asus_sysctl, "I",
721                     acpi_asus_sysctls[i].description);
722         }
723
724         /* Attach leds */
725         if (sc->model->bled_set) {
726                 sc->s_bled.busy = 0;
727                 sc->s_bled.sc = sc;
728                 sc->s_bled.type = ACPI_ASUS_LED_BLED;
729                 sc->s_bled.cdev =
730                     led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
731                         "bled", 1);
732         }
733
734         if (sc->model->dled_set) {
735                 sc->s_dled.busy = 0;
736                 sc->s_dled.sc = sc;
737                 sc->s_dled.type = ACPI_ASUS_LED_DLED;
738                 sc->s_dled.cdev =
739                     led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
740         }
741
742         if (sc->model->gled_set) {
743                 sc->s_gled.busy = 0;
744                 sc->s_gled.sc = sc;
745                 sc->s_gled.type = ACPI_ASUS_LED_GLED;
746                 sc->s_gled.cdev =
747                     led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
748         }
749
750         if (sc->model->mled_set) {
751                 sc->s_mled.busy = 0;
752                 sc->s_mled.sc = sc;
753                 sc->s_mled.type = ACPI_ASUS_LED_MLED;
754                 sc->s_mled.cdev =
755                     led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
756         }
757
758         if (sc->model->tled_set) {
759                 sc->s_tled.busy = 0;
760                 sc->s_tled.sc = sc;
761                 sc->s_tled.type = ACPI_ASUS_LED_TLED;
762                 sc->s_tled.cdev =
763                     led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
764                         "tled", 1);
765         }
766
767         if (sc->model->wled_set) {
768                 sc->s_wled.busy = 0;
769                 sc->s_wled.sc = sc;
770                 sc->s_wled.type = ACPI_ASUS_LED_WLED;
771                 sc->s_wled.cdev =
772                     led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
773                         "wled", 1);
774         }
775
776         /* Activate hotkeys */
777         AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
778
779         /* Handle notifies */
780         if (sc->model->n_func == NULL)
781                 sc->model->n_func = acpi_asus_notify;
782
783         AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
784             sc->model->n_func, dev);
785
786         /* Find and hook the 'LCDD' object */
787         if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
788                 ACPI_STATUS res;
789
790                 sc->lcdd_handle = NULL;
791                 res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
792                     NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
793                 if (ACPI_SUCCESS(res)) {
794                         AcpiInstallNotifyHandler((sc->lcdd_handle),
795                             ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
796                 } else {
797                         printf("%s: unable to find LCD device '%s'\n",
798                             __func__, sc->model->lcdd);
799                 }
800         }
801
802         return (0);
803 }
804
805 static int
806 acpi_asus_detach(device_t dev)
807 {
808         struct acpi_asus_softc  *sc;
809
810         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
811
812         sc = device_get_softc(dev);
813
814         /* Turn the lights off */
815         if (sc->model->bled_set)
816                 led_destroy(sc->s_bled.cdev);
817
818         if (sc->model->dled_set)
819                 led_destroy(sc->s_dled.cdev);
820
821         if (sc->model->gled_set)
822                 led_destroy(sc->s_gled.cdev);
823
824         if (sc->model->mled_set)
825                 led_destroy(sc->s_mled.cdev);
826
827         if (sc->model->tled_set)
828                 led_destroy(sc->s_tled.cdev);
829
830         if (sc->model->wled_set)
831                 led_destroy(sc->s_wled.cdev);
832
833         /* Remove notify handler */
834         AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
835             acpi_asus_notify);
836         
837         if (sc->lcdd_handle) {
838                 KASSERT(sc->model->lcdd_n_func != NULL,
839                     ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
840                 AcpiRemoveNotifyHandler((sc->lcdd_handle),
841                     ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
842         }
843
844         /* Free sysctl tree */
845         sysctl_ctx_free(&sc->sysctl_ctx);
846
847         return (0);
848 }
849
850 static void
851 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
852 {
853         struct acpi_asus_softc  *sc;
854         char                    *method;
855         int                     state;
856         
857         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
858
859         sc = led->sc;
860
861         switch (led->type) {
862         case ACPI_ASUS_LED_BLED:
863                 method = sc->model->bled_set;
864                 state = led->state;
865                 break;
866         case ACPI_ASUS_LED_DLED:
867                 method = sc->model->dled_set;
868                 state = led->state;
869                 break;
870         case ACPI_ASUS_LED_GLED:
871                 method = sc->model->gled_set;
872                 state = led->state + 1; /* 1: off, 2: on */
873                 break;
874         case ACPI_ASUS_LED_MLED:
875                 method = sc->model->mled_set;
876                 state = !led->state;    /* inverted */
877                 break;
878         case ACPI_ASUS_LED_TLED:
879                 method = sc->model->tled_set;
880                 state = led->state;
881                 break;
882         case ACPI_ASUS_LED_WLED:
883                 method = sc->model->wled_set;
884                 state = led->state;
885                 break;
886         default:
887                 printf("acpi_asus_led: invalid LED type %d\n",
888                     (int)led->type);
889                 return;
890         }
891
892         acpi_SetInteger(sc->handle, method, state);
893         led->busy = 0;
894 }
895         
896 static void
897 acpi_asus_led(struct acpi_asus_led *led, int state)
898 {
899
900         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
901
902         if (led->busy)
903                 return;
904
905         led->busy = 1;
906         led->state = state;
907
908         AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
909 }
910
911 static int
912 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
913 {
914         struct acpi_asus_softc  *sc;
915         int                     arg;
916         int                     error = 0;
917         int                     function;
918         int                     method;
919         
920         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
921
922         sc = (struct acpi_asus_softc *)oidp->oid_arg1;
923         function = oidp->oid_arg2;
924         method = acpi_asus_sysctls[function].method;
925
926         ACPI_SERIAL_BEGIN(asus);
927         arg = acpi_asus_sysctl_get(sc, method);
928         error = sysctl_handle_int(oidp, &arg, 0, req);
929
930         /* Sanity check */
931         if (error != 0 || req->newptr == NULL)
932                 goto out;
933
934         /* Update */
935         error = acpi_asus_sysctl_set(sc, method, arg);
936
937 out:
938         ACPI_SERIAL_END(asus);
939         return (error);
940 }
941
942 static int
943 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
944 {
945         int val = 0;
946
947         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
948         ACPI_SERIAL_ASSERT(asus);
949
950         switch (method) {
951         case ACPI_ASUS_METHOD_BRN:
952                 val = sc->s_brn;
953                 break;
954         case ACPI_ASUS_METHOD_DISP:
955                 val = sc->s_disp;
956                 break;
957         case ACPI_ASUS_METHOD_LCD:
958                 val = sc->s_lcd;
959                 break;
960         case ACPI_ASUS_METHOD_CAMERA:
961                 val = sc->s_cam;
962                 break;
963         case ACPI_ASUS_METHOD_CARDRD:
964                 val = sc->s_crd;
965                 break;
966         case ACPI_ASUS_METHOD_WLAN:
967                 val = sc->s_wlan;
968                 break;
969         }
970
971         return (val);
972 }
973
974 static int
975 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
976 {
977         ACPI_STATUS             status = AE_OK;
978         ACPI_OBJECT_LIST        acpiargs;
979         ACPI_OBJECT             acpiarg[1];
980
981         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
982         ACPI_SERIAL_ASSERT(asus);
983
984         acpiargs.Count = 1;
985         acpiargs.Pointer = acpiarg;
986         acpiarg[0].Type = ACPI_TYPE_INTEGER;
987         acpiarg[0].Integer.Value = arg;
988
989         switch (method) {
990         case ACPI_ASUS_METHOD_BRN:
991                 if (arg < 0 || arg > 15)
992                         return (EINVAL);
993
994                 if (sc->model->brn_set)
995                         status = acpi_SetInteger(sc->handle,
996                             sc->model->brn_set, arg);
997                 else {
998                         while (arg != 0) {
999                                 status = AcpiEvaluateObject(sc->handle,
1000                                     (arg > 0) ?  sc->model->brn_up :
1001                                     sc->model->brn_dn, NULL, NULL);
1002                                 (arg > 0) ? arg-- : arg++;
1003                         }
1004                 }
1005
1006                 if (ACPI_SUCCESS(status))
1007                         sc->s_brn = arg;
1008
1009                 break;
1010         case ACPI_ASUS_METHOD_DISP:
1011                 if (arg < 0 || arg > 7)
1012                         return (EINVAL);
1013
1014                 status = acpi_SetInteger(sc->handle,
1015                     sc->model->disp_set, arg);
1016
1017                 if (ACPI_SUCCESS(status))
1018                         sc->s_disp = arg;
1019
1020                 break;
1021         case ACPI_ASUS_METHOD_LCD:
1022                 if (arg < 0 || arg > 1)
1023                         return (EINVAL);
1024
1025                 if (strncmp(sc->model->name, "L3H", 3) != 0)
1026                         status = AcpiEvaluateObject(sc->handle,
1027                             sc->model->lcd_set, NULL, NULL);
1028                 else
1029                         status = acpi_SetInteger(sc->handle,
1030                             sc->model->lcd_set, 0x7);
1031
1032                 if (ACPI_SUCCESS(status))
1033                         sc->s_lcd = arg;
1034
1035                 break;
1036         case ACPI_ASUS_METHOD_CAMERA:
1037                 if (arg < 0 || arg > 1)
1038                         return (EINVAL);
1039
1040                 status = AcpiEvaluateObject(sc->handle,
1041                     sc->model->cam_set, &acpiargs, NULL);
1042
1043                 if (ACPI_SUCCESS(status))
1044                         sc->s_cam = arg;
1045                 break;
1046         case ACPI_ASUS_METHOD_CARDRD:
1047                 if (arg < 0 || arg > 1)
1048                         return (EINVAL);
1049
1050                 status = AcpiEvaluateObject(sc->handle,
1051                     sc->model->crd_set, &acpiargs, NULL);
1052
1053                 if (ACPI_SUCCESS(status))
1054                         sc->s_crd = arg;
1055                 break;
1056         case ACPI_ASUS_METHOD_WLAN:
1057                 if (arg < 0 || arg > 1)
1058                         return (EINVAL);
1059
1060                 status = AcpiEvaluateObject(sc->handle,
1061                     sc->model->wlan_set, &acpiargs, NULL);
1062
1063                 if (ACPI_SUCCESS(status))
1064                         sc->s_wlan = arg;
1065                 break;
1066         }
1067
1068         return (0);
1069 }
1070
1071 static int
1072 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
1073 {
1074         ACPI_STATUS     status;
1075
1076         switch (method) {
1077         case ACPI_ASUS_METHOD_BRN:
1078                 if (sc->model->brn_get) {
1079                         /* GPLV/SPLV models */
1080                         status = acpi_GetInteger(sc->handle,
1081                             sc->model->brn_get, &sc->s_brn);
1082                         if (ACPI_SUCCESS(status))
1083                                 return (TRUE);
1084                 } else if (sc->model->brn_up) {
1085                         /* Relative models */
1086                         status = AcpiEvaluateObject(sc->handle,
1087                             sc->model->brn_up, NULL, NULL);
1088                         if (ACPI_FAILURE(status))
1089                                 return (FALSE);
1090
1091                         status = AcpiEvaluateObject(sc->handle,
1092                             sc->model->brn_dn, NULL, NULL);
1093                         if (ACPI_FAILURE(status))
1094                                 return (FALSE);
1095
1096                         return (TRUE);
1097                 }
1098                 return (FALSE);
1099         case ACPI_ASUS_METHOD_DISP:
1100                 if (sc->model->disp_get) {
1101                         status = acpi_GetInteger(sc->handle,
1102                             sc->model->disp_get, &sc->s_disp);
1103                         if (ACPI_SUCCESS(status))
1104                                 return (TRUE);
1105                 }
1106                 return (FALSE);
1107         case ACPI_ASUS_METHOD_LCD:
1108                 if (sc->model->lcd_get) {
1109                         if (strncmp(sc->model->name, "G2K", 3) == 0) {
1110                                 ACPI_BUFFER             Buf;
1111                                 ACPI_OBJECT             Arg, Obj;
1112                                 ACPI_OBJECT_LIST        Args;
1113
1114                                 Arg.Type = ACPI_TYPE_INTEGER;
1115                                 Arg.Integer.Value = 0x11;
1116                                 Args.Count = 1;
1117                                 Args.Pointer = &Arg;
1118                                 Buf.Length = sizeof(Obj);
1119                                 Buf.Pointer = &Obj;
1120
1121                                 status = AcpiEvaluateObject(sc->handle,
1122                                     sc->model->lcd_get, &Args, &Buf);
1123                                 if (ACPI_SUCCESS(status) &&
1124                                     Obj.Type == ACPI_TYPE_INTEGER) {
1125                                         sc->s_lcd = Obj.Integer.Value;
1126                                         return (TRUE);
1127                                 }
1128                         } else if (strncmp(sc->model->name, "L3H", 3) == 0) {
1129                                 ACPI_BUFFER             Buf;
1130                                 ACPI_OBJECT             Arg[2], Obj;
1131                                 ACPI_OBJECT_LIST        Args;
1132
1133                                 /* L3H is a bit special */
1134                                 Arg[0].Type = ACPI_TYPE_INTEGER;
1135                                 Arg[0].Integer.Value = 0x02;
1136                                 Arg[1].Type = ACPI_TYPE_INTEGER;
1137                                 Arg[1].Integer.Value = 0x03;
1138
1139                                 Args.Count = 2;
1140                                 Args.Pointer = Arg;
1141
1142                                 Buf.Length = sizeof(Obj);
1143                                 Buf.Pointer = &Obj;
1144
1145                                 status = AcpiEvaluateObject(sc->handle,
1146                                     sc->model->lcd_get, &Args, &Buf);
1147                                 if (ACPI_SUCCESS(status) &&
1148                                     Obj.Type == ACPI_TYPE_INTEGER) {
1149                                         sc->s_lcd = Obj.Integer.Value >> 8;
1150                                         return (TRUE);
1151                                 }
1152                         } else {
1153                                 status = acpi_GetInteger(sc->handle,
1154                                     sc->model->lcd_get, &sc->s_lcd);
1155                                 if (ACPI_SUCCESS(status))
1156                                         return (TRUE);
1157                         }
1158                 }
1159                 return (FALSE);
1160         case ACPI_ASUS_METHOD_CAMERA:
1161                 if (sc->model->cam_get) {
1162                         status = acpi_GetInteger(sc->handle,
1163                             sc->model->cam_get, &sc->s_cam);
1164                         if (ACPI_SUCCESS(status))
1165                                 return (TRUE);
1166                 }
1167                 return (FALSE);
1168         case ACPI_ASUS_METHOD_CARDRD:
1169                 if (sc->model->crd_get) {
1170                         status = acpi_GetInteger(sc->handle,
1171                             sc->model->crd_get, &sc->s_crd);
1172                         if (ACPI_SUCCESS(status))
1173                                 return (TRUE);
1174                 }
1175                 return (FALSE);
1176         case ACPI_ASUS_METHOD_WLAN:
1177                 if (sc->model->wlan_get) {
1178                         status = acpi_GetInteger(sc->handle,
1179                             sc->model->wlan_get, &sc->s_wlan);
1180                         if (ACPI_SUCCESS(status))
1181                                 return (TRUE);
1182                 }
1183                 return (FALSE);
1184         }
1185         return (FALSE);
1186 }
1187
1188 static void
1189 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1190 {
1191         struct acpi_asus_softc  *sc;
1192         struct acpi_softc       *acpi_sc;
1193
1194         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1195
1196         sc = device_get_softc((device_t)context);
1197         acpi_sc = acpi_device_get_parent_softc(sc->dev);
1198
1199         ACPI_SERIAL_BEGIN(asus);
1200         if ((notify & ~0x10) <= 15) {
1201                 sc->s_brn = notify & ~0x10;
1202                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1203         } else if ((notify & ~0x20) <= 15) {
1204                 sc->s_brn = notify & ~0x20;
1205                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1206         } else if (notify == 0x33) {
1207                 sc->s_lcd = 1;
1208                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
1209         } else if (notify == 0x34) {
1210                 sc->s_lcd = 0;
1211                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
1212         } else if (notify == 0x86) {
1213                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1214                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1215         } else if (notify == 0x87) {
1216                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1217                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1218         } else {
1219                 /* Notify devd(8) */
1220                 acpi_UserNotify("ASUS", h, notify);
1221         }
1222         ACPI_SERIAL_END(asus);
1223 }
1224
1225 static void
1226 acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1227 {
1228         struct acpi_asus_softc  *sc;
1229         struct acpi_softc       *acpi_sc;
1230
1231         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1232
1233         sc = device_get_softc((device_t)context);
1234         acpi_sc = acpi_device_get_parent_softc(sc->dev);
1235
1236         ACPI_SERIAL_BEGIN(asus);
1237         switch (notify) {
1238         case 0x87:
1239                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1240                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1241                 break;
1242         case 0x86:
1243                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1244                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1245                 break;
1246         }
1247         ACPI_SERIAL_END(asus);
1248 }
1249
1250 static void
1251 acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1252 {
1253         struct acpi_asus_softc  *sc;
1254         struct acpi_softc       *acpi_sc;
1255
1256         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1257
1258         sc = device_get_softc((device_t)context);
1259         acpi_sc = acpi_device_get_parent_softc(sc->dev);
1260
1261         ACPI_SERIAL_BEGIN(asus);
1262         if ((notify & ~0x20) <= 15) {
1263                 sc->s_brn = notify & ~0x20;
1264                 ACPI_VPRINT(sc->dev, acpi_sc,
1265                     "Brightness increased/decreased\n");
1266         } else {
1267                 /* Notify devd(8) */
1268                 acpi_UserNotify("ASUS-Eee", h, notify);
1269         }
1270         ACPI_SERIAL_END(asus);
1271 }