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