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