kernel: FWIW, add FreeBSD's led(4) framework.
[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
543 static int
544 acpi_asus_probe(device_t dev)
545 {
546         struct acpi_asus_model  *model;
547         struct acpi_asus_softc  *sc;
548         struct sbuf             *sb;
549         ACPI_BUFFER             Buf;
550         ACPI_OBJECT             Arg, *Obj;
551         ACPI_OBJECT_LIST        Args;
552         static char             *asus_ids[] = { "ATK0100", "ASUS010", NULL };
553         char *rstr;
554
555         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
556
557         if (acpi_disabled("asus"))
558                 return (ENXIO);
559         ACPI_SERIAL_INIT(asus);
560         rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
561         if (rstr == NULL) {
562                 return (ENXIO);
563         }
564
565         sc = device_get_softc(dev);
566         sc->dev = dev;
567         sc->handle = acpi_get_handle(dev);
568
569         Arg.Type = ACPI_TYPE_INTEGER;
570         Arg.Integer.Value = 0;
571
572         Args.Count = 1;
573         Args.Pointer = &Arg;
574
575         Buf.Pointer = NULL;
576         Buf.Length = ACPI_ALLOCATE_BUFFER;
577
578         AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
579         Obj = Buf.Pointer;
580
581         /*
582          * The Samsung P30 returns a null-pointer from INIT, we
583          * can identify it from the 'ODEM' string in the DSDT.
584          */
585         if (Obj->String.Pointer == NULL) {
586                 ACPI_STATUS             status;
587                 ACPI_TABLE_HEADER       th;
588
589                 status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
590                 if (ACPI_FAILURE(status)) {
591                         device_printf(dev, "Unsupported (Samsung?) laptop\n");
592                         AcpiOsFree(Buf.Pointer);
593                         return (ENXIO);
594                 }
595
596                 if (strncmp("ODEM", th.OemTableId, 4) == 0) {
597                         sc->model = &acpi_samsung_models[0];
598                         device_set_desc(dev, "Samsung P30 Laptop Extras");
599                         AcpiOsFree(Buf.Pointer);
600                         return (0);
601                 }
602
603                 /* EeePC */
604                 if (strncmp("ASUS010", rstr, 7) == 0) {
605                         sc->model = &acpi_eeepc_models[0];
606                         device_set_desc(dev, "ASUS EeePC");
607                         AcpiOsFree(Buf.Pointer);
608                         return (0);
609                 }
610         }
611
612         sb = sbuf_new_auto();
613         if (sb == NULL)
614                 return (ENOMEM);
615
616         /*
617          * Asus laptops are simply identified by name, easy!
618          */
619         for (model = acpi_asus_models; model->name != NULL; model++) {
620                 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
621
622 good:
623                         sbuf_printf(sb, "Asus %s Laptop Extras",
624                             Obj->String.Pointer);
625                         sbuf_finish(sb);
626
627                         sc->model = model;
628                         device_set_desc_copy(dev, sbuf_data(sb));
629
630                         sbuf_delete(sb);
631                         AcpiOsFree(Buf.Pointer);
632                         return (0);
633                 }
634                 
635                 /*
636                  * Some models look exactly the same as other models, but have
637                  * their own ids.  If we spot these, set them up with the same
638                  * details as the models they're like, possibly dealing with
639                  * small differences.
640                  *
641                  * XXX: there must be a prettier way to do this!
642                  */
643                 else if (strncmp(model->name, "xxN", 3) == 0 &&
644                     (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
645                      strncmp(Obj->String.Pointer, "S1N", 3) == 0))
646                         goto good;
647                 else if (strncmp(model->name, "A1x", 3) == 0 &&
648                     strncmp(Obj->String.Pointer, "A1", 2) == 0)
649                         goto good;
650                 else if (strncmp(model->name, "A2x", 3) == 0 &&
651                     strncmp(Obj->String.Pointer, "A2", 2) == 0)
652                         goto good;
653                 else if (strncmp(model->name, "A3F", 3) == 0 &&
654                     strncmp(Obj->String.Pointer, "A6F", 3) == 0)
655                         goto good;
656                 else if (strncmp(model->name, "D1x", 3) == 0 &&
657                     strncmp(Obj->String.Pointer, "D1", 2) == 0)
658                         goto good;
659                 else if (strncmp(model->name, "L3H", 3) == 0 &&
660                     strncmp(Obj->String.Pointer, "L2E", 3) == 0)
661                         goto good;
662                 else if (strncmp(model->name, "L5x", 3) == 0 &&
663                     strncmp(Obj->String.Pointer, "L5", 2) == 0)
664                         goto good;
665                 else if (strncmp(model->name, "M2E", 3) == 0 &&
666                     (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
667                      strncmp(Obj->String.Pointer, "L4E", 3) == 0))
668                         goto good;
669                 else if (strncmp(model->name, "S1x", 3) == 0 &&
670                     (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
671                      strncmp(Obj->String.Pointer, "S1", 2) == 0))
672                         goto good;
673                 else if (strncmp(model->name, "S2x", 3) == 0 &&
674                     (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
675                      strncmp(Obj->String.Pointer, "S2", 2) == 0))
676                         goto good;
677
678                 /* L2B is like L3C but has no lcd_get method */
679                 else if (strncmp(model->name, "L3C", 3) == 0 &&
680                     strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
681                         model->lcd_get = NULL;
682                         goto good;
683                 }
684
685                 /* A3G is like M6R but with a different lcd_get method */
686                 else if (strncmp(model->name, "M6R", 3) == 0 &&
687                     strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
688                         model->lcd_get = "\\BLFG";
689                         goto good;
690                 }
691
692                 /* M2N and W1N are like xxN with added WLED */
693                 else if (strncmp(model->name, "xxN", 3) == 0 &&
694                     (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
695                      strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
696                         model->wled_set = "WLED";
697                         goto good;
698                 }
699
700                 /* M5N and S5N are like xxN without MLED */
701                 else if (strncmp(model->name, "xxN", 3) == 0 &&
702                     (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
703                      strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
704                         model->mled_set = NULL;
705                         goto good;
706                 }
707         }
708
709         sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
710         sbuf_finish(sb);
711
712         device_printf(dev, "%s", sbuf_data(sb));
713
714         sbuf_delete(sb);
715         AcpiOsFree(Buf.Pointer);
716
717         return (ENXIO);
718 }
719
720 static int
721 acpi_asus_attach(device_t dev)
722 {
723         struct acpi_asus_softc  *sc;
724         struct acpi_softc       *acpi_sc;
725
726         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
727
728         sc = device_get_softc(dev);
729         acpi_sc = acpi_device_get_parent_softc(dev);
730
731         /* Build sysctl tree */
732         sysctl_ctx_init(&sc->sysctl_ctx);
733         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
734             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
735             OID_AUTO, "asus", CTLFLAG_RD, 0, "");
736
737         /* Hook up nodes */
738         for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
739                 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
740                         continue;
741
742                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
743                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
744                     acpi_asus_sysctls[i].name,
745                     acpi_asus_sysctls[i].flags,
746                     sc, i, acpi_asus_sysctl, "I",
747                     acpi_asus_sysctls[i].description);
748         }
749
750         /* Attach leds */
751         if (sc->model->bled_set) {
752                 sc->s_bled.busy = 0;
753                 sc->s_bled.sc = sc;
754                 sc->s_bled.type = ACPI_ASUS_LED_BLED;
755                 sc->s_bled.cdev =
756                     led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
757                         "bled", 1);
758         }
759
760         if (sc->model->dled_set) {
761                 sc->s_dled.busy = 0;
762                 sc->s_dled.sc = sc;
763                 sc->s_dled.type = ACPI_ASUS_LED_DLED;
764                 sc->s_dled.cdev =
765                     led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
766         }
767
768         if (sc->model->gled_set) {
769                 sc->s_gled.busy = 0;
770                 sc->s_gled.sc = sc;
771                 sc->s_gled.type = ACPI_ASUS_LED_GLED;
772                 sc->s_gled.cdev =
773                     led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
774         }
775
776         if (sc->model->mled_set) {
777                 sc->s_mled.busy = 0;
778                 sc->s_mled.sc = sc;
779                 sc->s_mled.type = ACPI_ASUS_LED_MLED;
780                 sc->s_mled.cdev =
781                     led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
782         }
783
784         if (sc->model->tled_set) {
785                 sc->s_tled.busy = 0;
786                 sc->s_tled.sc = sc;
787                 sc->s_tled.type = ACPI_ASUS_LED_TLED;
788                 sc->s_tled.cdev =
789                     led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
790                         "tled", 1);
791         }
792
793         if (sc->model->wled_set) {
794                 sc->s_wled.busy = 0;
795                 sc->s_wled.sc = sc;
796                 sc->s_wled.type = ACPI_ASUS_LED_WLED;
797                 sc->s_wled.cdev =
798                     led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
799                         "wled", 1);
800         }
801
802         /* Activate hotkeys */
803         AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
804
805         /* Handle notifies */
806         if (sc->model->n_func == NULL)
807                 sc->model->n_func = acpi_asus_notify;
808
809         AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
810             sc->model->n_func, dev);
811
812         /* Find and hook the 'LCDD' object */
813         if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
814                 ACPI_STATUS res;
815
816                 sc->lcdd_handle = NULL;
817                 res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
818                     NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
819                 if (ACPI_SUCCESS(res)) {
820                         AcpiInstallNotifyHandler((sc->lcdd_handle),
821                             ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
822                 } else {
823                         kprintf("%s: unable to find LCD device '%s'\n",
824                             __func__, sc->model->lcdd);
825                 }
826         }
827
828         return (0);
829 }
830
831 static int
832 acpi_asus_detach(device_t dev)
833 {
834         struct acpi_asus_softc  *sc;
835
836         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
837
838         sc = device_get_softc(dev);
839         /* Turn the lights off */
840         if (sc->model->bled_set)
841                 led_destroy(sc->s_bled.cdev);
842
843         if (sc->model->dled_set)
844                 led_destroy(sc->s_dled.cdev);
845
846         if (sc->model->gled_set)
847                 led_destroy(sc->s_gled.cdev);
848
849         if (sc->model->mled_set)
850                 led_destroy(sc->s_mled.cdev);
851
852         if (sc->model->tled_set)
853                 led_destroy(sc->s_tled.cdev);
854
855         if (sc->model->wled_set)
856                 led_destroy(sc->s_wled.cdev);
857
858         /* Remove notify handler */
859         AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
860             acpi_asus_notify);
861         
862         if (sc->lcdd_handle) {
863                 KASSERT(sc->model->lcdd_n_func != NULL,
864                     ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
865                 AcpiRemoveNotifyHandler((sc->lcdd_handle),
866                     ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
867         }
868
869         /* Free sysctl tree */
870         sysctl_ctx_free(&sc->sysctl_ctx);
871
872         return (0);
873 }
874
875 static void
876 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
877 {
878         struct acpi_asus_softc  *sc;
879         char                    *method;
880         int                     state;
881         
882         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
883
884         sc = led->sc;
885
886         switch (led->type) {
887         case ACPI_ASUS_LED_BLED:
888                 method = sc->model->bled_set;
889                 state = led->state;
890                 break;
891         case ACPI_ASUS_LED_DLED:
892                 method = sc->model->dled_set;
893                 state = led->state;
894                 break;
895         case ACPI_ASUS_LED_GLED:
896                 method = sc->model->gled_set;
897                 state = led->state + 1; /* 1: off, 2: on */
898                 break;
899         case ACPI_ASUS_LED_MLED:
900                 method = sc->model->mled_set;
901                 state = !led->state;    /* inverted */
902                 break;
903         case ACPI_ASUS_LED_TLED:
904                 method = sc->model->tled_set;
905                 state = led->state;
906                 break;
907         case ACPI_ASUS_LED_WLED:
908                 method = sc->model->wled_set;
909                 state = led->state;
910                 break;
911         default:
912                 kprintf("acpi_asus_led: invalid LED type %d\n",
913                     (int)led->type);
914                 return;
915         }
916
917         acpi_SetInteger(sc->handle, method, state);
918         led->busy = 0;
919 }
920         
921 static void
922 acpi_asus_led(struct acpi_asus_led *led, int state)
923 {
924
925         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
926
927         if (led->busy)
928                 return;
929
930         led->busy = 1;
931         led->state = state;
932
933         AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
934 }
935
936 static int
937 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
938 {
939         struct acpi_asus_softc  *sc;
940         int                     arg;
941         int                     error = 0;
942         int                     function;
943         int                     method;
944         
945         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
946
947         sc = (struct acpi_asus_softc *)oidp->oid_arg1;
948         function = oidp->oid_arg2;
949         method = acpi_asus_sysctls[function].method;
950
951         ACPI_SERIAL_BEGIN(asus);
952         arg = acpi_asus_sysctl_get(sc, method);
953         error = sysctl_handle_int(oidp, &arg, 0, req);
954
955         /* Sanity check */
956         if (error != 0 || req->newptr == NULL)
957                 goto out;
958
959         /* Update */
960         error = acpi_asus_sysctl_set(sc, method, arg);
961
962 out:
963         ACPI_SERIAL_END(asus);
964         return (error);
965 }
966
967 static int
968 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
969 {
970         int val = 0;
971
972         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
973         ACPI_SERIAL_ASSERT(asus);
974
975         switch (method) {
976         case ACPI_ASUS_METHOD_BRN:
977                 val = sc->s_brn;
978                 break;
979         case ACPI_ASUS_METHOD_DISP:
980                 val = sc->s_disp;
981                 break;
982         case ACPI_ASUS_METHOD_LCD:
983                 val = sc->s_lcd;
984                 break;
985         case ACPI_ASUS_METHOD_CAMERA:
986                 val = sc->s_cam;
987                 break;
988         case ACPI_ASUS_METHOD_CARDRD:
989                 val = sc->s_crd;
990                 break;
991         case ACPI_ASUS_METHOD_WLAN:
992                 val = sc->s_wlan;
993                 break;
994         }
995
996         return (val);
997 }
998
999 static int
1000 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
1001 {
1002         ACPI_STATUS             status = AE_OK;
1003         ACPI_OBJECT_LIST        acpiargs;
1004         ACPI_OBJECT             acpiarg[1];
1005
1006         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1007         ACPI_SERIAL_ASSERT(asus);
1008
1009         acpiargs.Count = 1;
1010         acpiargs.Pointer = acpiarg;
1011         acpiarg[0].Type = ACPI_TYPE_INTEGER;
1012         acpiarg[0].Integer.Value = arg;
1013
1014         switch (method) {
1015         case ACPI_ASUS_METHOD_BRN:
1016                 if (arg < 0 || arg > 15)
1017                         return (EINVAL);
1018
1019                 if (sc->model->brn_set)
1020                         status = acpi_SetInteger(sc->handle,
1021                             sc->model->brn_set, arg);
1022                 else {
1023                         while (arg != 0) {
1024                                 status = AcpiEvaluateObject(sc->handle,
1025                                     (arg > 0) ?  sc->model->brn_up :
1026                                     sc->model->brn_dn, NULL, NULL);
1027                                 (arg > 0) ? arg-- : arg++;
1028                         }
1029                 }
1030
1031                 if (ACPI_SUCCESS(status))
1032                         sc->s_brn = arg;
1033
1034                 break;
1035         case ACPI_ASUS_METHOD_DISP:
1036                 if (arg < 0 || arg > 7)
1037                         return (EINVAL);
1038
1039                 status = acpi_SetInteger(sc->handle,
1040                     sc->model->disp_set, arg);
1041
1042                 if (ACPI_SUCCESS(status))
1043                         sc->s_disp = arg;
1044
1045                 break;
1046         case ACPI_ASUS_METHOD_LCD:
1047                 if (arg < 0 || arg > 1)
1048                         return (EINVAL);
1049
1050                 if (strncmp(sc->model->name, "L3H", 3) != 0)
1051                         status = AcpiEvaluateObject(sc->handle,
1052                             sc->model->lcd_set, NULL, NULL);
1053                 else
1054                         status = acpi_SetInteger(sc->handle,
1055                             sc->model->lcd_set, 0x7);
1056
1057                 if (ACPI_SUCCESS(status))
1058                         sc->s_lcd = arg;
1059
1060                 break;
1061         case ACPI_ASUS_METHOD_CAMERA:
1062                 if (arg < 0 || arg > 1)
1063                         return (EINVAL);
1064
1065                 status = AcpiEvaluateObject(sc->handle,
1066                     sc->model->cam_set, &acpiargs, NULL);
1067
1068                 if (ACPI_SUCCESS(status))
1069                         sc->s_cam = arg;
1070                 break;
1071         case ACPI_ASUS_METHOD_CARDRD:
1072                 if (arg < 0 || arg > 1)
1073                         return (EINVAL);
1074
1075                 status = AcpiEvaluateObject(sc->handle,
1076                     sc->model->crd_set, &acpiargs, NULL);
1077
1078                 if (ACPI_SUCCESS(status))
1079                         sc->s_crd = arg;
1080                 break;
1081         case ACPI_ASUS_METHOD_WLAN:
1082                 if (arg < 0 || arg > 1)
1083                         return (EINVAL);
1084
1085                 status = AcpiEvaluateObject(sc->handle,
1086                     sc->model->wlan_set, &acpiargs, NULL);
1087
1088                 if (ACPI_SUCCESS(status))
1089                         sc->s_wlan = arg;
1090                 break;
1091         }
1092
1093         return (0);
1094 }
1095
1096 static int
1097 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
1098 {
1099         ACPI_STATUS     status;
1100
1101         switch (method) {
1102         case ACPI_ASUS_METHOD_BRN:
1103                 if (sc->model->brn_get) {
1104                         /* GPLV/SPLV models */
1105                         status = acpi_GetInteger(sc->handle,
1106                             sc->model->brn_get, &sc->s_brn);
1107                         if (ACPI_SUCCESS(status))
1108                                 return (TRUE);
1109                 } else if (sc->model->brn_up) {
1110                         /* Relative models */
1111                         status = AcpiEvaluateObject(sc->handle,
1112                             sc->model->brn_up, NULL, NULL);
1113                         if (ACPI_FAILURE(status))
1114                                 return (FALSE);
1115
1116                         status = AcpiEvaluateObject(sc->handle,
1117                             sc->model->brn_dn, NULL, NULL);
1118                         if (ACPI_FAILURE(status))
1119                                 return (FALSE);
1120
1121                         return (TRUE);
1122                 }
1123                 return (FALSE);
1124         case ACPI_ASUS_METHOD_DISP:
1125                 if (sc->model->disp_get) {
1126                         status = acpi_GetInteger(sc->handle,
1127                             sc->model->disp_get, &sc->s_disp);
1128                         if (ACPI_SUCCESS(status))
1129                                 return (TRUE);
1130                 }
1131                 return (FALSE);
1132         case ACPI_ASUS_METHOD_LCD:
1133                 if (sc->model->lcd_get) {
1134                         if (strncmp(sc->model->name, "L3H", 3) == 0) {
1135                                 ACPI_BUFFER             Buf;
1136                                 ACPI_OBJECT             Arg[2], Obj;
1137                                 ACPI_OBJECT_LIST        Args;
1138
1139                                 /* L3H is a bit special */
1140                                 Arg[0].Type = ACPI_TYPE_INTEGER;
1141                                 Arg[0].Integer.Value = 0x02;
1142                                 Arg[1].Type = ACPI_TYPE_INTEGER;
1143                                 Arg[1].Integer.Value = 0x03;
1144
1145                                 Args.Count = 2;
1146                                 Args.Pointer = Arg;
1147
1148                                 Buf.Length = sizeof(Obj);
1149                                 Buf.Pointer = &Obj;
1150
1151                                 status = AcpiEvaluateObject(sc->handle,
1152                                     sc->model->lcd_get, &Args, &Buf);
1153                                 if (ACPI_SUCCESS(status) &&
1154                                     Obj.Type == ACPI_TYPE_INTEGER) {
1155                                         sc->s_lcd = Obj.Integer.Value >> 8;
1156                                         return (TRUE);
1157                                 }
1158                         } else {
1159                                 status = acpi_GetInteger(sc->handle,
1160                                     sc->model->lcd_get, &sc->s_lcd);
1161                                 if (ACPI_SUCCESS(status))
1162                                         return (TRUE);
1163                         }
1164                 }
1165                 return (FALSE);
1166         case ACPI_ASUS_METHOD_CAMERA:
1167                 if (sc->model->cam_get) {
1168                         status = acpi_GetInteger(sc->handle,
1169                             sc->model->cam_get, &sc->s_cam);
1170                         if (ACPI_SUCCESS(status))
1171                                 return (TRUE);
1172                 }
1173                 return (FALSE);
1174         case ACPI_ASUS_METHOD_CARDRD:
1175                 if (sc->model->crd_get) {
1176                         status = acpi_GetInteger(sc->handle,
1177                             sc->model->crd_get, &sc->s_crd);
1178                         if (ACPI_SUCCESS(status))
1179                                 return (TRUE);
1180                 }
1181                 return (FALSE);
1182         case ACPI_ASUS_METHOD_WLAN:
1183                 if (sc->model->wlan_get) {
1184                         status = acpi_GetInteger(sc->handle,
1185                             sc->model->wlan_get, &sc->s_wlan);
1186                         if (ACPI_SUCCESS(status))
1187                                 return (TRUE);
1188                 }
1189                 return (FALSE);
1190         }
1191         return (FALSE);
1192 }
1193
1194 static void
1195 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1196 {
1197         struct acpi_asus_softc  *sc;
1198         struct acpi_softc       *acpi_sc;
1199
1200         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1201
1202         sc = device_get_softc((device_t)context);
1203         acpi_sc = acpi_device_get_parent_softc(sc->dev);
1204
1205         ACPI_SERIAL_BEGIN(asus);
1206         if ((notify & ~0x10) <= 15) {
1207                 sc->s_brn = notify & ~0x10;
1208                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1209         } else if ((notify & ~0x20) <= 15) {
1210                 sc->s_brn = notify & ~0x20;
1211                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1212         } else if (notify == 0x33) {
1213                 sc->s_lcd = 1;
1214                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
1215         } else if (notify == 0x34) {
1216                 sc->s_lcd = 0;
1217                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
1218         } else if (notify == 0x86) {
1219                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1220                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1221         } else if (notify == 0x87) {
1222                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1223                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1224         } else {
1225                 /* Notify devd(8) */
1226                 acpi_UserNotify("ASUS", h, notify);
1227         }
1228         ACPI_SERIAL_END(asus);
1229 }
1230
1231 static void
1232 acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1233 {
1234         struct acpi_asus_softc  *sc;
1235         struct acpi_softc       *acpi_sc;
1236
1237         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1238
1239         sc = device_get_softc((device_t)context);
1240         acpi_sc = acpi_device_get_parent_softc(sc->dev);
1241
1242         ACPI_SERIAL_BEGIN(asus);
1243         switch (notify) {
1244         case 0x87:
1245                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1246                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1247                 break;
1248         case 0x86:
1249                 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1250                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1251                 break;
1252         }
1253         ACPI_SERIAL_END(asus);
1254 }
1255
1256 static void
1257 acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1258 {
1259         struct acpi_asus_softc  *sc;
1260         struct acpi_softc       *acpi_sc;
1261
1262         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1263
1264         sc = device_get_softc((device_t)context);
1265         acpi_sc = acpi_device_get_parent_softc(sc->dev);
1266
1267         ACPI_SERIAL_BEGIN(asus);
1268         if ((notify & ~0x20) <= 15) {
1269                 sc->s_brn = notify & ~0x20;
1270                 ACPI_VPRINT(sc->dev, acpi_sc,
1271                     "Brightness increased/decreased\n");
1272         } else {
1273                 /* Notify devd(8) */
1274                 acpi_UserNotify("ASUS-Eee", h, notify);
1275         }
1276         ACPI_SERIAL_END(asus);
1277 }