Merge branch 'vendor/OPENSSH'
[dragonfly.git] / sys / dev / acpica5 / 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.24.2.4.2.1 2008/10/02 02:57:24 kensmith Exp $
27  */
28
29 #include <sys/cdefs.h>
30
31 /*
32  * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
33  * recent Asus (and Medion) laptops.  Inspired by the acpi4asus project which
34  * implements these features in the Linux kernel.
35  *
36  *   <http://sourceforge.net/projects/acpi4asus/>
37  *
38  * Currently should support most features, but could use some more testing.
39  * Particularly the display-switching stuff is a bit hairy.  If you have an
40  * Asus laptop which doesn't appear to be supported, or strange things happen
41  * when using this driver, please report to <acpi@FreeBSD.org>.
42  */
43
44 #include "opt_acpi.h"
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/bus.h>
48 #include <machine/cpufunc.h>
49 #include <sys/module.h>
50 #include <sys/sensors.h>
51 #include <sys/sysctl.h>
52 #include <sys/types.h>
53 #include <sys/lock.h>
54 #include <sys/sbuf.h>
55 #include <sys/thread2.h>
56 #include <machine/clock.h>
57
58 #include "acpi.h"
59 #include "acpivar.h"
60 #include "acpi_if.h"
61
62 /* Methods */
63 #define ACPI_ASUS_METHOD_BRN    1
64 #define ACPI_ASUS_METHOD_DISP   2
65 #define ACPI_ASUS_METHOD_LCD    3
66
67 #define _COMPONENT      ACPI_OEM
68 ACPI_MODULE_NAME("ASUS")
69
70 struct acpi_asus_model {
71         char    *name;
72
73         char    *bled_set;
74         char    *mled_set;
75         char    *tled_set;
76         char    *wled_set;
77
78         char    *brn_get;
79         char    *brn_set;
80         char    *brn_up;
81         char    *brn_dn;
82
83         char    *lcd_get;
84         char    *lcd_set;
85
86         char    *disp_get;
87         char    *disp_set;
88 };
89
90 struct acpi_asus_led {
91         struct acpi_asus_softc *sc;
92         struct cdev     *cdev;
93         int             busy;
94         int             state;
95         enum {
96                 ACPI_ASUS_LED_BLED,
97                 ACPI_ASUS_LED_MLED,
98                 ACPI_ASUS_LED_TLED,
99                 ACPI_ASUS_LED_WLED,
100         } type;
101 };
102
103 struct acpi_asus_softc {
104         device_t                dev;
105         ACPI_HANDLE             handle;
106
107         struct acpi_asus_model  *model;
108         struct sysctl_ctx_list  sysctl_ctx;
109         struct sysctl_oid       *sysctl_tree;
110
111         struct acpi_asus_led    s_bled;
112         struct acpi_asus_led    s_mled;
113         struct acpi_asus_led    s_tled;
114         struct acpi_asus_led    s_wled;
115
116         int                     s_brn;
117         int                     s_disp;
118         int                     s_lcd;
119 };
120
121 /*
122  * We can identify Asus laptops from the string they return
123  * as a result of calling the ATK0100 'INIT' method.
124  */
125 static struct acpi_asus_model acpi_asus_models[] = {
126         {
127                 .name           = "xxN",
128                 .mled_set       = "MLED",
129                 .wled_set       = "WLED",
130                 .lcd_get        = "\\BKLT",
131                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
132                 .brn_get        = "GPLV",
133                 .brn_set        = "SPLV",
134                 .disp_get       = "\\ADVG",
135                 .disp_set       = "SDSP"
136         },
137         {
138                 .name           = "A1x",
139                 .mled_set       = "MLED",
140                 .lcd_get        = "\\BKLI",
141                 .lcd_set        = "\\_SB.PCI0.ISA.EC0._Q10",
142                 .brn_up         = "\\_SB.PCI0.ISA.EC0._Q0E",
143                 .brn_dn         = "\\_SB.PCI0.ISA.EC0._Q0F"
144         },
145         {
146                 .name           = "A2x",
147                 .mled_set       = "MLED",
148                 .wled_set       = "WLED",
149                 .lcd_get        = "\\BAOF",
150                 .lcd_set        = "\\Q10",
151                 .brn_get        = "GPLV",
152                 .brn_set        = "SPLV",
153                 .disp_get       = "\\INFB",
154                 .disp_set       = "SDSP"
155         },
156         {
157                 .name           = "A3N",
158                 .mled_set       = "MLED",
159                 .bled_set       = "BLED",
160                 .wled_set       = "WLED",
161                 .lcd_get        = NULL,
162                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
163                 .brn_set        = "SPLV",
164                 .brn_get        = "SDSP",
165                 .disp_set       = "SDSP",
166                 .disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD"
167         },
168         {
169                 .name           = "A4D",
170                 .mled_set       = "MLED",
171                 .brn_up         = "\\_SB_.PCI0.SBRG.EC0._Q0E",
172                 .brn_dn         = "\\_SB_.PCI0.SBRG.EC0._Q0F",
173                 .brn_get        = "GPLV",
174                 .brn_set        = "SPLV",
175 #ifdef notyet
176                 .disp_get       = "\\_SB_.PCI0.SBRG.EC0._Q10",
177                 .disp_set       = "\\_SB_.PCI0.SBRG.EC0._Q11"
178 #endif
179         },
180         {
181                 .name           = "A6V",
182                 .bled_set       = "BLED",
183                 .mled_set       = "MLED",
184                 .wled_set       = "WLED",
185                 .lcd_get        = NULL,
186                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
187                 .brn_get        = "GPLV",
188                 .brn_set        = "SPLV",
189                 .disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD",
190                 .disp_set       = "SDSP"
191         },
192         {
193                 .name           = "D1x",
194                 .mled_set       = "MLED",
195                 .lcd_get        = "\\GP11",
196                 .lcd_set        = "\\Q0D",
197                 .brn_up         = "\\Q0C",
198                 .brn_dn         = "\\Q0B",
199                 .disp_get       = "\\INFB",
200                 .disp_set       = "SDSP"
201         },
202         {
203                 .name           = "L2D",
204                 .mled_set       = "MLED",
205                 .wled_set       = "WLED",
206                 .brn_up         = "\\Q0E",
207                 .brn_dn         = "\\Q0F",
208                 .lcd_get        = "\\SGP0",
209                 .lcd_set        = "\\Q10"
210         },
211         {
212                 .name           = "L3C",
213                 .mled_set       = "MLED",
214                 .wled_set       = "WLED",
215                 .brn_get        = "GPLV",
216                 .brn_set        = "SPLV",
217                 .lcd_get        = "\\GL32",
218                 .lcd_set        = "\\_SB.PCI0.PX40.ECD0._Q10"
219         },
220         {
221                 .name           = "L3D",
222                 .mled_set       = "MLED",
223                 .wled_set       = "WLED",
224                 .brn_get        = "GPLV",
225                 .brn_set        = "SPLV",
226                 .lcd_get        = "\\BKLG",
227                 .lcd_set        = "\\Q10"
228         },
229         {
230                 .name           = "L3H",
231                 .mled_set       = "MLED",
232                 .wled_set       = "WLED",
233                 .brn_get        = "GPLV",
234                 .brn_set        = "SPLV",
235                 .lcd_get        = "\\_SB.PCI0.PM.PBC",
236                 .lcd_set        = "EHK",
237                 .disp_get       = "\\_SB.INFB",
238                 .disp_set       = "SDSP"
239         },
240         {
241                 .name           = "L4R",
242                 .mled_set       = "MLED",
243                 .wled_set       = "WLED",
244                 .brn_get        = "GPLV",
245                 .brn_set        = "SPLV",
246                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
247                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
248                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
249                 .disp_set       = "SDSP"
250         },
251         {
252                 .name           = "L5x",
253                 .mled_set       = "MLED",
254                 .tled_set       = "TLED",
255                 .lcd_get        = "\\BAOF",
256                 .lcd_set        = "\\Q0D",
257                 .brn_get        = "GPLV",
258                 .brn_set        = "SPLV",
259                 .disp_get       = "\\INFB",
260                 .disp_set       = "SDSP"
261         },
262         {
263                 .name           = "L8L"
264                 /* Only has hotkeys, apparantly */
265         },
266         {
267                 .name           = "M1A",
268                 .mled_set       = "MLED",
269                 .brn_up         = "\\_SB.PCI0.PX40.EC0.Q0E",
270                 .brn_dn         = "\\_SB.PCI0.PX40.EC0.Q0F",
271                 .lcd_get        = "\\PNOF",
272                 .lcd_set        = "\\_SB.PCI0.PX40.EC0.Q10"
273         },
274         {
275                 .name           = "M2E",
276                 .mled_set       = "MLED",
277                 .wled_set       = "WLED",
278                 .brn_get        = "GPLV",
279                 .brn_set        = "SPLV",
280                 .lcd_get        = "\\GP06",
281                 .lcd_set        = "\\Q10"
282         },
283         {
284                 .name           = "M6N",
285                 .mled_set       = "MLED",
286                 .wled_set       = "WLED",
287                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
288                 .lcd_get        = "\\_SB.BKLT",
289                 .brn_set        = "SPLV",
290                 .brn_get        = "GPLV",
291                 .disp_set       = "SDSP",
292                 .disp_get       = "\\SSTE"
293         },
294         {
295                 .name           = "M6R",
296                 .mled_set       = "MLED",
297                 .wled_set       = "WLED",
298                 .brn_get        = "GPLV",
299                 .brn_set        = "SPLV",
300                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
301                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
302                 .disp_get       = "\\SSTE",
303                 .disp_set       = "SDSP"
304         },
305         {
306                 .name           = "S1x",
307                 .mled_set       = "MLED",
308                 .wled_set       = "WLED",
309                 .lcd_get        = "\\PNOF",
310                 .lcd_set        = "\\_SB.PCI0.PX40.Q10",
311                 .brn_get        = "GPLV",
312                 .brn_set        = "SPLV"
313         },
314         {
315                 .name           = "S2x",
316                 .mled_set       = "MLED",
317                 .lcd_get        = "\\BKLI",
318                 .lcd_set        = "\\_SB.PCI0.ISA.EC0._Q10",
319                 .brn_up         = "\\_SB.PCI0.ISA.EC0._Q0B",
320                 .brn_dn         = "\\_SB.PCI0.ISA.EC0._Q0A"
321         },
322         {
323                 .name           = "V6V",
324                 .bled_set       = "BLED",
325                 .tled_set       = "TLED",
326                 .wled_set       = "WLED",
327                 .lcd_get        = "\\BKLT",
328                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
329                 .brn_get        = "GPLV",
330                 .brn_set        = "SPLV",
331                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
332                 .disp_set       = "SDSP"
333         },
334         {
335                 .name           = "W5A",
336                 .bled_set       = "BLED",
337                 .lcd_get        = "\\BKLT",
338                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
339                 .brn_get        = "GPLV",
340                 .brn_set        = "SPLV",
341                 .disp_get       = "\\_SB.PCI0.P0P2.VGA.GETD",
342                 .disp_set       = "SDSP"
343         },
344
345         { .name = NULL }
346 };
347
348 /*
349  * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
350  * but they can't be probed quite the same way as Asus laptops.
351  */
352 static struct acpi_asus_model acpi_samsung_models[] = {
353         {
354                 .name           = "P30",
355                 .wled_set       = "WLED",
356                 .brn_up         = "\\_SB.PCI0.LPCB.EC0._Q68",
357                 .brn_dn         = "\\_SB.PCI0.LPCB.EC0._Q69",
358                 .lcd_get        = "\\BKLT",
359                 .lcd_set        = "\\_SB.PCI0.LPCB.EC0._Q0E"
360         },
361
362         { .name = NULL }
363 };
364
365 /*
366  * EeePC have an Asus ASUS010 gadget interface,
367  * but they can't be probed quite the same way as Asus laptops.
368  */
369 static struct acpi_asus_model acpi_eeepc_models[] = {
370         {
371                 .name           = "EEE",
372                 .brn_get        = "\\_SB.ATKD.PBLG",
373                 .brn_set        = "\\_SB.ATKD.PBLS"
374         },
375
376         { .name = NULL }
377 };
378
379 static struct {
380         char    *name;
381         char    *description;
382         int     method;
383 } acpi_asus_sysctls[] = {
384         {
385                 .name           = "lcd_backlight",
386                 .method         = ACPI_ASUS_METHOD_LCD,
387                 .description    = "state of the lcd backlight"
388         },
389         {
390                 .name           = "lcd_brightness",
391                 .method         = ACPI_ASUS_METHOD_BRN,
392                 .description    = "brightness of the lcd panel"
393         },
394         {
395                 .name           = "video_output",
396                 .method         = ACPI_ASUS_METHOD_DISP,
397                 .description    = "display output state"
398         },
399
400         { .name = NULL }
401 };
402
403 static int      acpi_asus_probe(device_t dev);
404 static int      acpi_asus_attach(device_t dev);
405 static int      acpi_asus_detach(device_t dev);
406
407 static struct lock asuslock;
408
409 static int      acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
410 static int      acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
411 static int      acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
412 static int      acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
413
414 static void     acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
415
416 static device_method_t acpi_asus_methods[] = {
417         /* Device interface */
418         DEVMETHOD(device_probe, acpi_asus_probe),
419         DEVMETHOD(device_attach, acpi_asus_attach),
420         DEVMETHOD(device_detach, acpi_asus_detach),
421         {0, 0}
422 };
423
424 static driver_t acpi_asus_driver = {
425         "acpi_asus",
426         acpi_asus_methods,
427         sizeof(struct acpi_asus_softc),
428 };
429
430 static devclass_t acpi_asus_devclass;
431
432 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver,
433     acpi_asus_devclass, 0, 0);
434 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
435
436 static char             *asus_ids[] = { "ATK0100", "ASUS010", NULL };
437
438 static int
439 acpi_asus_probe(device_t dev)
440 {
441         struct acpi_asus_model  *model;
442         struct acpi_asus_softc  *sc;
443         struct sbuf             *sb;
444         ACPI_BUFFER             Buf;
445         ACPI_OBJECT             Arg, *Obj;
446         ACPI_OBJECT_LIST        Args;
447         char *rstr;
448
449         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
450
451         if (acpi_disabled("asus")) return (ENXIO);
452         rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
453         if (rstr == NULL) {
454                 return (ENXIO);
455         }
456
457
458         sc = device_get_softc(dev);
459         sc->dev = dev;
460         sc->handle = acpi_get_handle(dev);
461
462
463         Arg.Type = ACPI_TYPE_INTEGER;
464         Arg.Integer.Value = 0;
465
466         Args.Count = 1;
467         Args.Pointer = &Arg;
468
469         Buf.Pointer = NULL;
470         Buf.Length = ACPI_ALLOCATE_BUFFER;
471
472         AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
473         Obj = Buf.Pointer;
474
475         /*
476          * The Samsung P30 returns a null-pointer from INIT, we
477          * can identify it from the 'ODEM' string in the DSDT.
478          */
479         if (Obj->String.Pointer == NULL) {
480                 ACPI_STATUS             status;
481                 ACPI_TABLE_HEADER       th;
482
483                 status = AcpiGetTableHeader(ACPI_SIG_DSDT, 1, &th);
484                 if (ACPI_FAILURE(status)) {
485                         device_printf(dev, "Unsupported (Samsung?) laptop\n");
486                         AcpiOsFree(Buf.Pointer);
487                         return (ENXIO);
488                 }
489
490                 if (strncmp("ODEM", th.OemTableId, 4) == 0) {
491                         sc->model = &acpi_samsung_models[0];
492                         device_set_desc(dev, "Samsung P30 Laptop Extras");
493                         AcpiOsFree(Buf.Pointer);
494                         return (0);
495                 }
496
497                 /* if EeePC */
498                 if (strncmp("ASUS010", rstr, 7) == 0) {
499                         sc->model = &acpi_eeepc_models[0];
500                         device_set_desc(dev, "ASUS EeePC");
501                         AcpiOsFree(Buf.Pointer);
502                         return (0);
503                 }
504         }
505
506         sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
507         if (sb == NULL)
508                 return (ENOMEM);
509
510         /*
511          * Asus laptops are simply identified by name, easy!
512          */
513         for (model = acpi_asus_models; model->name != NULL; model++) {
514                 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
515
516 good:
517                         sbuf_printf(sb, "Asus %s Laptop Extras",
518                             Obj->String.Pointer);
519                         sbuf_finish(sb);
520
521                         sc->model = model;
522                         device_set_desc_copy(dev, sbuf_data(sb));
523
524                         sbuf_delete(sb);
525                         AcpiOsFree(Buf.Pointer);
526                         return (0);
527                 }
528
529                 /*
530                  * Some models look exactly the same as other models, but have
531                  * their own ids.  If we spot these, set them up with the same
532                  * details as the models they're like, possibly dealing with
533                  * small differences.
534                  *
535                  * XXX: there must be a prettier way to do this!
536                  */
537                 else if (strncmp(model->name, "xxN", 3) == 0 &&
538                     (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
539                      strncmp(Obj->String.Pointer, "S1N", 3) == 0))
540                         goto good;
541                 else if (strncmp(model->name, "A1x", 3) == 0 &&
542                     strncmp(Obj->String.Pointer, "A1", 2) == 0)
543                         goto good;
544                 else if (strncmp(model->name, "A2x", 3) == 0 &&
545                     strncmp(Obj->String.Pointer, "A2", 2) == 0)
546                         goto good;
547                 else if (strncmp(model->name, "D1x", 3) == 0 &&
548                     strncmp(Obj->String.Pointer, "D1", 2) == 0)
549                         goto good;
550                 else if (strncmp(model->name, "L3H", 3) == 0 &&
551                     strncmp(Obj->String.Pointer, "L2E", 3) == 0)
552                         goto good;
553                 else if (strncmp(model->name, "L5x", 3) == 0 &&
554                     strncmp(Obj->String.Pointer, "L5", 2) == 0)
555                         goto good;
556                 else if (strncmp(model->name, "M2E", 3) == 0 &&
557                     (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
558                      strncmp(Obj->String.Pointer, "L4E", 3) == 0))
559                         goto good;
560                 else if (strncmp(model->name, "S1x", 3) == 0 &&
561                     (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
562                      strncmp(Obj->String.Pointer, "S1", 2) == 0))
563                         goto good;
564                 else if (strncmp(model->name, "S2x", 3) == 0 &&
565                     (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
566                      strncmp(Obj->String.Pointer, "S2", 2) == 0))
567                         goto good;
568
569                 /* L2B is like L3C but has no lcd_get method */
570                 else if (strncmp(model->name, "L3C", 3) == 0 &&
571                     strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
572                         model->lcd_get = NULL;
573                         goto good;
574                 }
575
576                 /* A3G is like M6R but with a different lcd_get method */
577                 else if (strncmp(model->name, "M6R", 3) == 0 &&
578                     strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
579                         model->lcd_get = "\\BLFG";
580                         goto good;
581                 }
582
583                 /* M2N and W1N are like xxN with added WLED */
584                 else if (strncmp(model->name, "xxN", 3) == 0 &&
585                     (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
586                      strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
587                         model->wled_set = "WLED";
588                         goto good;
589                 }
590
591                 /* M5N and S5N are like xxN without MLED */
592                 else if (strncmp(model->name, "xxN", 3) == 0 &&
593                     (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
594                      strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
595                         model->mled_set = NULL;
596                         goto good;
597                 }
598         }
599
600         sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
601         sbuf_finish(sb);
602
603         device_printf(dev, sbuf_data(sb));
604
605         sbuf_delete(sb);
606         AcpiOsFree(Buf.Pointer);
607
608         return (ENXIO);
609 }
610
611 static int
612 acpi_asus_detach(device_t dev)
613 {
614         struct acpi_asus_softc  *sc;
615
616         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
617
618         sc = device_get_softc(dev);
619
620
621         /* Turn the lights off */
622         /* We don't have LED support, sadly... :( */
623 /*      if (sc->model->bled_set)
624                 led_destroy(sc->s_bled.cdev);
625
626         if (sc->model->mled_set)
627                 led_destroy(sc->s_mled.cdev);
628
629         if (sc->model->tled_set)
630                 led_destroy(sc->s_tled.cdev);
631
632         if (sc->model->wled_set)
633                 led_destroy(sc->s_wled.cdev);
634 */
635         /* Remove notify handler */
636         AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
637             acpi_asus_notify);
638
639         /* Free sysctl tree */
640         sysctl_ctx_free(&sc->sysctl_ctx);
641
642         return (0);
643 }
644
645 static int
646 acpi_asus_attach(device_t dev)
647 {
648         struct acpi_asus_softc  *sc;
649         struct acpi_softc       *acpi_sc;
650
651         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
652
653         sc = device_get_softc(dev);
654         acpi_sc = acpi_device_get_parent_softc(dev);
655
656         /* Build sysctl tree */
657         sysctl_ctx_init(&sc->sysctl_ctx);
658         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
659             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
660             OID_AUTO, "asus", CTLFLAG_RD, 0, "");
661
662         /* Hook up nodes */
663         for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
664                 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
665                         continue;
666
667                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
668                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
669                     acpi_asus_sysctls[i].name,
670                     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
671                     sc, i, acpi_asus_sysctl, "I",
672                     acpi_asus_sysctls[i].description);
673         }
674
675         /* Attach leds */
676
677    /*Currently we don't have LEDs control. Just comment this for now...*/
678 /*
679         if (sc->model->bled_set) {
680                 sc->s_bled.busy = 0;
681                 sc->s_bled.sc = sc;
682                 sc->s_bled.type = ACPI_ASUS_LED_BLED;
683                 sc->s_bled.cdev =
684                     led_create((led_t *)acpi_asus_led, &sc->s_bled, "bled");
685         }
686
687         if (sc->model->mled_set) {
688                 sc->s_mled.busy = 0;
689                 sc->s_mled.sc = sc;
690                 sc->s_mled.type = ACPI_ASUS_LED_MLED;
691                 sc->s_mled.cdev =
692                     led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
693         }
694
695         if (sc->model->tled_set) {
696                 sc->s_tled.busy = 0;
697                 sc->s_tled.sc = sc;
698                 sc->s_tled.type = ACPI_ASUS_LED_TLED;
699                 sc->s_tled.cdev =
700                     led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled");
701         }
702
703         if (sc->model->wled_set) {
704                 sc->s_wled.busy = 0;
705                 sc->s_wled.sc = sc;
706                 sc->s_wled.type = ACPI_ASUS_LED_WLED;
707                 sc->s_wled.cdev =
708                     led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled");
709         }
710 */
711
712         /* Activate hotkeys */
713         AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
714
715         /* Handle notifies */
716         AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
717             acpi_asus_notify, dev);
718
719     lockinit(&asuslock, "asus", 0, 0);
720
721         return (0);
722 }
723
724 static int
725 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
726 {
727         struct acpi_asus_softc  *sc;
728         int                     arg;
729         int                     error = 0;
730         int                     function;
731         int                     method;
732
733         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
734
735         sc = (struct acpi_asus_softc *)oidp->oid_arg1;
736         function = oidp->oid_arg2;
737         method = acpi_asus_sysctls[function].method;
738
739         lockmgr(&asuslock, LK_EXCLUSIVE);
740         arg = acpi_asus_sysctl_get(sc, method);
741         error = sysctl_handle_int(oidp, &arg, 0, req);
742
743         /* Sanity check */
744         if (error != 0 || req->newptr == NULL)
745                 goto out;
746
747         /* Update */
748         error = acpi_asus_sysctl_set(sc, method, arg);
749
750 out:
751         lockmgr(&asuslock, LK_RELEASE);
752         return (error);
753 }
754
755 static int
756 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
757 {
758         int val = 0;
759
760         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
761         KKASSERT(lockstatus(&asuslock, NULL)!=0); /* was ACPI_SERIAL_ASSERT(asus); may be I lost something? */
762
763         switch (method) {
764         case ACPI_ASUS_METHOD_BRN:
765                 val = sc->s_brn;
766                 break;
767         case ACPI_ASUS_METHOD_DISP:
768                 val = sc->s_disp;
769                 break;
770         case ACPI_ASUS_METHOD_LCD:
771                 val = sc->s_lcd;
772                 break;
773         }
774
775         return (val);
776 }
777
778 static int
779 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
780 {
781         ACPI_STATUS     status = AE_OK;
782
783         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
784
785
786         KKASSERT(lockstatus(&asuslock, NULL)!=0); /* was ACPI_SERIAL_ASSERT(asus); another miss? */
787
788         switch (method) {
789         case ACPI_ASUS_METHOD_BRN:
790                 if (arg < 0 || arg > 15)
791                         return (EINVAL);
792
793                 if (sc->model->brn_set)
794                         status = acpi_SetInteger(sc->handle,
795                             sc->model->brn_set, arg);
796                 else {
797                         while (arg != 0) {
798                                 status = AcpiEvaluateObject(sc->handle,
799                                     (arg > 0) ?  sc->model->brn_up :
800                                     sc->model->brn_dn, NULL, NULL);
801                                 (arg > 0) ? arg-- : arg++;
802                         }
803                 }
804
805                 if (ACPI_SUCCESS(status))
806                         sc->s_brn = arg;
807
808                 break;
809         case ACPI_ASUS_METHOD_DISP:
810                 if (arg < 0 || arg > 7)
811                         return (EINVAL);
812
813                 status = acpi_SetInteger(sc->handle,
814                     sc->model->disp_set, arg);
815
816                 if (ACPI_SUCCESS(status))
817                         sc->s_disp = arg;
818
819                 break;
820         case ACPI_ASUS_METHOD_LCD:
821                 if (arg < 0 || arg > 1)
822                         return (EINVAL);
823
824                 if (strncmp(sc->model->name, "L3H", 3) != 0)
825                         status = AcpiEvaluateObject(sc->handle,
826                             sc->model->lcd_set, NULL, NULL);
827                 else
828                         status = acpi_SetInteger(sc->handle,
829                             sc->model->lcd_set, 0x7);
830
831                 if (ACPI_SUCCESS(status))
832                         sc->s_lcd = arg;
833
834                 break;
835         }
836
837         return (0);
838 }
839
840 static int
841 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
842 {
843         ACPI_STATUS     status;
844
845         switch (method) {
846         case ACPI_ASUS_METHOD_BRN:
847                 if (sc->model->brn_get) {
848                         /* GPLV/SPLV models */
849                         status = acpi_GetInteger(sc->handle,
850                             sc->model->brn_get, &sc->s_brn);
851                         if (ACPI_SUCCESS(status))
852                                 return (TRUE);
853                 } else if (sc->model->brn_up) {
854                         /* Relative models */
855                         status = AcpiEvaluateObject(sc->handle,
856                             sc->model->brn_up, NULL, NULL);
857                         if (ACPI_FAILURE(status))
858                                 return (FALSE);
859
860                         status = AcpiEvaluateObject(sc->handle,
861                             sc->model->brn_dn, NULL, NULL);
862                         if (ACPI_FAILURE(status))
863                                 return (FALSE);
864
865                         return (TRUE);
866                 }
867                 return (FALSE);
868         case ACPI_ASUS_METHOD_DISP:
869                 if (sc->model->disp_get) {
870                         status = acpi_GetInteger(sc->handle,
871                             sc->model->disp_get, &sc->s_disp);
872                         if (ACPI_SUCCESS(status))
873                                 return (TRUE);
874                 }
875                 return (FALSE);
876         case ACPI_ASUS_METHOD_LCD:
877                 if (sc->model->lcd_get &&
878                     strncmp(sc->model->name, "L3H", 3) != 0) {
879                         status = acpi_GetInteger(sc->handle,
880                             sc->model->lcd_get, &sc->s_lcd);
881                         if (ACPI_SUCCESS(status))
882                                 return (TRUE);
883                 }
884                 else if (sc->model->lcd_get) {
885                         ACPI_BUFFER             Buf;
886                         ACPI_OBJECT             Arg[2], Obj;
887                         ACPI_OBJECT_LIST        Args;
888
889                         /* L3H is a bit special */
890                         Arg[0].Type = ACPI_TYPE_INTEGER;
891                         Arg[0].Integer.Value = 0x02;
892                         Arg[1].Type = ACPI_TYPE_INTEGER;
893                         Arg[1].Integer.Value = 0x03;
894
895                         Args.Count = 2;
896                         Args.Pointer = Arg;
897
898                         Buf.Length = sizeof(Obj);
899                         Buf.Pointer = &Obj;
900
901                         status = AcpiEvaluateObject(sc->handle,
902                             sc->model->lcd_get, &Args, &Buf);
903                         if (ACPI_SUCCESS(status) &&
904                             Obj.Type == ACPI_TYPE_INTEGER) {
905                                 sc->s_lcd = Obj.Integer.Value >> 8;
906                                 return (TRUE);
907                         }
908                 }
909                 return (FALSE);
910         }
911         return (FALSE);
912 }
913
914 static void
915 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
916 {
917         struct acpi_asus_softc  *sc;
918         struct acpi_softc       *acpi_sc;
919
920         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
921
922         sc = device_get_softc((device_t)context);
923         acpi_sc = acpi_device_get_parent_softc(sc->dev);
924
925         lockmgr(&asuslock, LK_EXCLUSIVE);
926         if ((notify & ~0x10) <= 15) {
927                 sc->s_brn = notify & ~0x10;
928                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
929         } else if ((notify & ~0x20) <= 15) {
930                 sc->s_brn = notify & ~0x20;
931                 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
932         } else if (notify == 0x33) {
933                 sc->s_lcd = 1;
934                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
935         } else if (notify == 0x34) {
936                 sc->s_lcd = 0;
937                 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
938         } else {
939                 /* Notify devd(8) */
940                 acpi_UserNotify("ASUS", h, notify);
941         }
942         lockmgr(&asuslock, LK_RELEASE);
943 }