The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/acpica/acpi_asus.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2004, 2005 Philip Paeps <philip@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 /*
   31  * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
   32  * recent Asus (and Medion) laptops.  Inspired by the acpi4asus project which
   33  * implements these features in the Linux kernel.
   34  *
   35  *   <http://sourceforge.net/projects/acpi4asus/>
   36  *
   37  * Currently should support most features, but could use some more testing.
   38  * Particularly the display-switching stuff is a bit hairy.  If you have an
   39  * Asus laptop which doesn't appear to be supported, or strange things happen
   40  * when using this driver, please report to <acpi@FreeBSD.org>.
   41  */
   42 
   43 #include "opt_acpi.h"
   44 #include <sys/param.h>
   45 #include <sys/kernel.h>
   46 #include <sys/module.h>
   47 #include <sys/bus.h>
   48 #include <sys/sbuf.h>
   49 
   50 #include "acpi.h"
   51 #include <dev/acpica/acpivar.h>
   52 #include <dev/led/led.h>
   53 
   54 /* Methods */
   55 #define ACPI_ASUS_METHOD_BRN    1
   56 #define ACPI_ASUS_METHOD_DISP   2
   57 #define ACPI_ASUS_METHOD_LCD    3
   58 
   59 #define _COMPONENT      ACPI_OEM
   60 ACPI_MODULE_NAME("ASUS")
   61 
   62 struct acpi_asus_model {
   63         char    *name;
   64 
   65         char    *bled_set;
   66         char    *mled_set;
   67         char    *tled_set;
   68         char    *wled_set;
   69 
   70         char    *brn_get;
   71         char    *brn_set;
   72         char    *brn_up;
   73         char    *brn_dn;
   74 
   75         char    *lcd_get;
   76         char    *lcd_set;
   77 
   78         char    *disp_get;
   79         char    *disp_set;
   80 };
   81 
   82 struct acpi_asus_led {
   83         struct acpi_asus_softc *sc;
   84         struct cdev     *cdev;
   85         int             busy;
   86         int             state;
   87         enum {
   88                 ACPI_ASUS_LED_BLED,
   89                 ACPI_ASUS_LED_MLED,
   90                 ACPI_ASUS_LED_TLED,
   91                 ACPI_ASUS_LED_WLED,
   92         } type;
   93 };
   94 
   95 struct acpi_asus_softc {
   96         device_t                dev;
   97         ACPI_HANDLE             handle;
   98 
   99         struct acpi_asus_model  *model;
  100         struct sysctl_ctx_list  sysctl_ctx;
  101         struct sysctl_oid       *sysctl_tree;
  102 
  103         struct acpi_asus_led    s_bled;
  104         struct acpi_asus_led    s_mled;
  105         struct acpi_asus_led    s_tled;
  106         struct acpi_asus_led    s_wled;
  107 
  108         int                     s_brn;
  109         int                     s_disp;
  110         int                     s_lcd;
  111 };
  112 
  113 /*
  114  * We can identify Asus laptops from the string they return
  115  * as a result of calling the ATK0100 'INIT' method.
  116  */
  117 static struct acpi_asus_model acpi_asus_models[] = {
  118         {
  119                 .name           = "xxN",
  120                 .mled_set       = "MLED",
  121                 .wled_set       = "WLED",
  122                 .lcd_get        = "\\BKLT",
  123                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  124                 .brn_get        = "GPLV",
  125                 .brn_set        = "SPLV",
  126                 .disp_get       = "\\ADVG",
  127                 .disp_set       = "SDSP"
  128         },
  129         {
  130                 .name           = "A1x",
  131                 .mled_set       = "MLED",
  132                 .lcd_get        = "\\BKLI",
  133                 .lcd_set        = "\\_SB.PCI0.ISA.EC0._Q10",
  134                 .brn_up         = "\\_SB.PCI0.ISA.EC0._Q0E",
  135                 .brn_dn         = "\\_SB.PCI0.ISA.EC0._Q0F"
  136         },
  137         {
  138                 .name           = "A2x",
  139                 .mled_set       = "MLED",
  140                 .wled_set       = "WLED",
  141                 .lcd_get        = "\\BAOF",
  142                 .lcd_set        = "\\Q10",
  143                 .brn_get        = "GPLV",
  144                 .brn_set        = "SPLV",
  145                 .disp_get       = "\\INFB",
  146                 .disp_set       = "SDSP"
  147         },
  148         {
  149                 .name           = "A4D",
  150                 .mled_set       = "MLED",
  151                 .brn_up         = "\\_SB_.PCI0.SBRG.EC0._Q0E",
  152                 .brn_dn         = "\\_SB_.PCI0.SBRG.EC0._Q0F",
  153                 .brn_get        = "GPLV",
  154                 .brn_set        = "SPLV",
  155 #ifdef notyet
  156                 .disp_get       = "\\_SB_.PCI0.SBRG.EC0._Q10",
  157                 .disp_set       = "\\_SB_.PCI0.SBRG.EC0._Q11"
  158 #endif
  159         },
  160         {
  161                 .name           = "A6V",
  162                 .bled_set       = "BLED",
  163                 .mled_set       = "MLED",
  164                 .wled_set       = "WLED",
  165                 .lcd_get        = NULL,
  166                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  167                 .brn_get        = "GPLV",
  168                 .brn_set        = "SPLV",
  169                 .disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD",
  170                 .disp_set       = "SDSP"
  171         },
  172         {
  173                 .name           = "D1x",
  174                 .mled_set       = "MLED",
  175                 .lcd_get        = "\\GP11",
  176                 .lcd_set        = "\\Q0D",
  177                 .brn_up         = "\\Q0C",
  178                 .brn_dn         = "\\Q0B",
  179                 .disp_get       = "\\INFB",
  180                 .disp_set       = "SDSP"
  181         },
  182         {
  183                 .name           = "L2D",
  184                 .mled_set       = "MLED",
  185                 .wled_set       = "WLED",
  186                 .brn_up         = "\\Q0E",
  187                 .brn_dn         = "\\Q0F",
  188                 .lcd_get        = "\\SGP0",
  189                 .lcd_set        = "\\Q10"
  190         },
  191         {
  192                 .name           = "L3C",
  193                 .mled_set       = "MLED",
  194                 .wled_set       = "WLED",
  195                 .brn_get        = "GPLV",
  196                 .brn_set        = "SPLV",
  197                 .lcd_get        = "\\GL32",
  198                 .lcd_set        = "\\_SB.PCI0.PX40.ECD0._Q10"
  199         },
  200         {
  201                 .name           = "L3D",
  202                 .mled_set       = "MLED",
  203                 .wled_set       = "WLED",
  204                 .brn_get        = "GPLV",
  205                 .brn_set        = "SPLV",
  206                 .lcd_get        = "\\BKLG",
  207                 .lcd_set        = "\\Q10"
  208         },
  209         {
  210                 .name           = "L3H",
  211                 .mled_set       = "MLED",
  212                 .wled_set       = "WLED",
  213                 .brn_get        = "GPLV",
  214                 .brn_set        = "SPLV",
  215                 .lcd_get        = "\\_SB.PCI0.PM.PBC",
  216                 .lcd_set        = "EHK",
  217                 .disp_get       = "\\_SB.INFB",
  218                 .disp_set       = "SDSP"
  219         },
  220         {
  221                 .name           = "L4R",
  222                 .mled_set       = "MLED",
  223                 .wled_set       = "WLED",
  224                 .brn_get        = "GPLV",
  225                 .brn_set        = "SPLV",
  226                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
  227                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  228                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
  229                 .disp_set       = "SDSP"
  230         },
  231         {
  232                 .name           = "L5x",
  233                 .mled_set       = "MLED",
  234                 .tled_set       = "TLED",
  235                 .lcd_get        = "\\BAOF",
  236                 .lcd_set        = "\\Q0D",
  237                 .brn_get        = "GPLV",
  238                 .brn_set        = "SPLV",
  239                 .disp_get       = "\\INFB",
  240                 .disp_set       = "SDSP"
  241         },
  242         {
  243                 .name           = "L8L"
  244                 /* Only has hotkeys, apparantly */
  245         },
  246         {
  247                 .name           = "M1A",
  248                 .mled_set       = "MLED",
  249                 .brn_up         = "\\_SB.PCI0.PX40.EC0.Q0E",
  250                 .brn_dn         = "\\_SB.PCI0.PX40.EC0.Q0F",
  251                 .lcd_get        = "\\PNOF",
  252                 .lcd_set        = "\\_SB.PCI0.PX40.EC0.Q10"
  253         },
  254         {
  255                 .name           = "M2E",
  256                 .mled_set       = "MLED",
  257                 .wled_set       = "WLED",
  258                 .brn_get        = "GPLV",
  259                 .brn_set        = "SPLV",
  260                 .lcd_get        = "\\GP06",
  261                 .lcd_set        = "\\Q10"
  262         },
  263         {
  264                 .name           = "M6N",
  265                 .mled_set       = "MLED",
  266                 .wled_set       = "WLED",
  267                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  268                 .lcd_get        = "\\_SB.BKLT",
  269                 .brn_set        = "SPLV",
  270                 .brn_get        = "GPLV",
  271                 .disp_set       = "SDSP",
  272                 .disp_get       = "\\SSTE"
  273         },
  274         {
  275                 .name           = "M6R",
  276                 .mled_set       = "MLED",
  277                 .wled_set       = "WLED",
  278                 .brn_get        = "GPLV",
  279                 .brn_set        = "SPLV",
  280                 .lcd_get        = "\\_SB.PCI0.SBSM.SEO4",
  281                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  282                 .disp_get       = "\\SSTE",
  283                 .disp_set       = "SDSP"
  284         },
  285         {
  286                 .name           = "S1x",
  287                 .mled_set       = "MLED",
  288                 .wled_set       = "WLED",
  289                 .lcd_get        = "\\PNOF",
  290                 .lcd_set        = "\\_SB.PCI0.PX40.Q10",
  291                 .brn_get        = "GPLV",
  292                 .brn_set        = "SPLV"
  293         },
  294         {
  295                 .name           = "S2x",
  296                 .mled_set       = "MLED",
  297                 .lcd_get        = "\\BKLI",
  298                 .lcd_set        = "\\_SB.PCI0.ISA.EC0._Q10",
  299                 .brn_up         = "\\_SB.PCI0.ISA.EC0._Q0B",
  300                 .brn_dn         = "\\_SB.PCI0.ISA.EC0._Q0A"
  301         },
  302         {
  303                 .name           = "V6V",
  304                 .bled_set       = "BLED",
  305                 .tled_set       = "TLED",
  306                 .wled_set       = "WLED",
  307                 .lcd_get        = "\\BKLT",
  308                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  309                 .brn_get        = "GPLV",
  310                 .brn_set        = "SPLV",
  311                 .disp_get       = "\\_SB.PCI0.P0P1.VGA.GETD",
  312                 .disp_set       = "SDSP"
  313         },
  314         {
  315                 .name           = "W5A",
  316                 .bled_set       = "BLED",
  317                 .lcd_get        = "\\BKLT",
  318                 .lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
  319                 .brn_get        = "GPLV",
  320                 .brn_set        = "SPLV",
  321                 .disp_get       = "\\_SB.PCI0.P0P2.VGA.GETD",
  322                 .disp_set       = "SDSP"
  323         },
  324 
  325         { .name = NULL }
  326 };
  327 
  328 /*
  329  * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
  330  * but they can't be probed quite the same way as Asus laptops.
  331  */
  332 static struct acpi_asus_model acpi_samsung_models[] = {
  333         {
  334                 .name           = "P30",
  335                 .wled_set       = "WLED",
  336                 .brn_up         = "\\_SB.PCI0.LPCB.EC0._Q68",
  337                 .brn_dn         = "\\_SB.PCI0.LPCB.EC0._Q69",
  338                 .lcd_get        = "\\BKLT",
  339                 .lcd_set        = "\\_SB.PCI0.LPCB.EC0._Q0E"
  340         },
  341 
  342         { .name = NULL }
  343 };
  344 
  345 static struct {
  346         char    *name;
  347         char    *description;
  348         int     method;
  349 } acpi_asus_sysctls[] = {
  350         {
  351                 .name           = "lcd_backlight",
  352                 .method         = ACPI_ASUS_METHOD_LCD,
  353                 .description    = "state of the lcd backlight"
  354         },
  355         {
  356                 .name           = "lcd_brightness",
  357                 .method         = ACPI_ASUS_METHOD_BRN,
  358                 .description    = "brightness of the lcd panel"
  359         },
  360         {
  361                 .name           = "video_output",
  362                 .method         = ACPI_ASUS_METHOD_DISP,
  363                 .description    = "display output state"
  364         },
  365 
  366         { .name = NULL }
  367 };
  368 
  369 ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
  370 
  371 /* Function prototypes */
  372 static int      acpi_asus_probe(device_t dev);
  373 static int      acpi_asus_attach(device_t dev);
  374 static int      acpi_asus_detach(device_t dev);
  375 
  376 static void     acpi_asus_led(struct acpi_asus_led *led, int state);
  377 static void     acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
  378 
  379 static int      acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
  380 static int      acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
  381 static int      acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
  382 static int      acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
  383 
  384 static void     acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
  385 
  386 static device_method_t acpi_asus_methods[] = {
  387         DEVMETHOD(device_probe,  acpi_asus_probe),
  388         DEVMETHOD(device_attach, acpi_asus_attach),
  389         DEVMETHOD(device_detach, acpi_asus_detach),
  390 
  391         { 0, 0 }
  392 };
  393 
  394 static driver_t acpi_asus_driver = {
  395         "acpi_asus",
  396         acpi_asus_methods,
  397         sizeof(struct acpi_asus_softc)
  398 };
  399 
  400 static devclass_t acpi_asus_devclass;
  401 
  402 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
  403 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
  404 
  405 static int
  406 acpi_asus_probe(device_t dev)
  407 {
  408         struct acpi_asus_model  *model;
  409         struct acpi_asus_softc  *sc;
  410         struct sbuf             *sb;
  411         ACPI_BUFFER             Buf;
  412         ACPI_OBJECT             Arg, *Obj;
  413         ACPI_OBJECT_LIST        Args;
  414         static char             *asus_ids[] = { "ATK0100", NULL };
  415 
  416         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  417 
  418         if (acpi_disabled("asus") ||
  419             ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids) == NULL)
  420                 return (ENXIO);
  421 
  422         sc = device_get_softc(dev);
  423         sc->dev = dev;
  424         sc->handle = acpi_get_handle(dev);
  425 
  426         Arg.Type = ACPI_TYPE_INTEGER;
  427         Arg.Integer.Value = 0;
  428 
  429         Args.Count = 1;
  430         Args.Pointer = &Arg;
  431 
  432         Buf.Pointer = NULL;
  433         Buf.Length = ACPI_ALLOCATE_BUFFER;
  434 
  435         AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
  436         Obj = Buf.Pointer;
  437 
  438         /*
  439          * The Samsung P30 returns a null-pointer from INIT, we
  440          * can identify it from the 'ODEM' string in the DSDT.
  441          */
  442         if (Obj->String.Pointer == NULL) {
  443                 ACPI_STATUS             status;
  444                 ACPI_TABLE_HEADER       th;
  445 
  446                 status = AcpiGetTableHeader(ACPI_TABLE_DSDT, 1, &th);
  447                 if (ACPI_FAILURE(status)) {
  448                         device_printf(dev, "Unsupported (Samsung?) laptop\n");
  449                         AcpiOsFree(Buf.Pointer);
  450                         return (ENXIO);
  451                 }
  452 
  453                 if (strncmp("ODEM", th.OemTableId, 4) == 0) {
  454                         sc->model = &acpi_samsung_models[0];
  455                         device_set_desc(dev, "Samsung P30 Laptop Extras");
  456                         AcpiOsFree(Buf.Pointer);
  457                         return (0);
  458                 }
  459         }
  460 
  461         sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
  462         if (sb == NULL)
  463                 return (ENOMEM);
  464 
  465         /*
  466          * Asus laptops are simply identified by name, easy!
  467          */
  468         for (model = acpi_asus_models; model->name != NULL; model++) {
  469                 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
  470 
  471 good:
  472                         sbuf_printf(sb, "Asus %s Laptop Extras",
  473                             Obj->String.Pointer);
  474                         sbuf_finish(sb);
  475 
  476                         sc->model = model;
  477                         device_set_desc_copy(dev, sbuf_data(sb));
  478 
  479                         sbuf_delete(sb);
  480                         AcpiOsFree(Buf.Pointer);
  481                         return (0);
  482                 }
  483                 
  484                 /*
  485                  * Some models look exactly the same as other models, but have
  486                  * their own ids.  If we spot these, set them up with the same
  487                  * details as the models they're like, possibly dealing with
  488                  * small differences.
  489                  *
  490                  * XXX: there must be a prettier way to do this!
  491                  */
  492                 else if (strncmp(model->name, "xxN", 3) == 0 &&
  493                     (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
  494                      strncmp(Obj->String.Pointer, "S1N", 3) == 0))
  495                         goto good;
  496                 else if (strncmp(model->name, "A1x", 3) == 0 &&
  497                     strncmp(Obj->String.Pointer, "A1", 2) == 0)
  498                         goto good;
  499                 else if (strncmp(model->name, "A2x", 3) == 0 &&
  500                     strncmp(Obj->String.Pointer, "A2", 2) == 0)
  501                         goto good;
  502                 else if (strncmp(model->name, "D1x", 3) == 0 &&
  503                     strncmp(Obj->String.Pointer, "D1", 2) == 0)
  504                         goto good;
  505                 else if (strncmp(model->name, "L3H", 3) == 0 &&
  506                     strncmp(Obj->String.Pointer, "L2E", 3) == 0)
  507                         goto good;
  508                 else if (strncmp(model->name, "L5x", 3) == 0 &&
  509                     strncmp(Obj->String.Pointer, "L5", 2) == 0)
  510                         goto good;
  511                 else if (strncmp(model->name, "M2E", 3) == 0 &&
  512                     (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
  513                      strncmp(Obj->String.Pointer, "L4E", 3) == 0))
  514                         goto good;
  515                 else if (strncmp(model->name, "S1x", 3) == 0 &&
  516                     (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
  517                      strncmp(Obj->String.Pointer, "S1", 2) == 0))
  518                         goto good;
  519                 else if (strncmp(model->name, "S2x", 3) == 0 &&
  520                     (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
  521                      strncmp(Obj->String.Pointer, "S2", 2) == 0))
  522                         goto good;
  523 
  524                 /* L2B is like L3C but has no lcd_get method */
  525                 else if (strncmp(model->name, "L3C", 3) == 0 &&
  526                     strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
  527                         model->lcd_get = NULL;
  528                         goto good;
  529                 }
  530 
  531                 /* A3G is like M6R but with a different lcd_get method */
  532                 else if (strncmp(model->name, "M6R", 3) == 0 &&
  533                     strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
  534                         model->lcd_get = "\\BLFG";
  535                         goto good;
  536                 }
  537 
  538                 /* M2N and W1N are like xxN with added WLED */
  539                 else if (strncmp(model->name, "xxN", 3) == 0 &&
  540                     (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
  541                      strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
  542                         model->wled_set = "WLED";
  543                         goto good;
  544                 }
  545 
  546                 /* M5N and S5N are like xxN without MLED */
  547                 else if (strncmp(model->name, "xxN", 3) == 0 &&
  548                     (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
  549                      strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
  550                         model->mled_set = NULL;
  551                         goto good;
  552                 }
  553         }
  554 
  555         sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
  556         sbuf_finish(sb);
  557 
  558         device_printf(dev, sbuf_data(sb));
  559 
  560         sbuf_delete(sb);
  561         AcpiOsFree(Buf.Pointer);
  562 
  563         return (ENXIO);
  564 }
  565 
  566 static int
  567 acpi_asus_attach(device_t dev)
  568 {
  569         struct acpi_asus_softc  *sc;
  570         struct acpi_softc       *acpi_sc;
  571 
  572         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  573 
  574         sc = device_get_softc(dev);
  575         acpi_sc = acpi_device_get_parent_softc(dev);
  576 
  577         /* Build sysctl tree */
  578         sysctl_ctx_init(&sc->sysctl_ctx);
  579         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
  580             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
  581             OID_AUTO, "asus", CTLFLAG_RD, 0, "");
  582 
  583         /* Hook up nodes */
  584         for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
  585                 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
  586                         continue;
  587 
  588                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
  589                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  590                     acpi_asus_sysctls[i].name,
  591                     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
  592                     sc, i, acpi_asus_sysctl, "I",
  593                     acpi_asus_sysctls[i].description);
  594         }
  595 
  596         /* Attach leds */
  597         if (sc->model->bled_set) {
  598                 sc->s_bled.busy = 0;
  599                 sc->s_bled.sc = sc;
  600                 sc->s_bled.type = ACPI_ASUS_LED_BLED;
  601                 sc->s_bled.cdev =
  602                     led_create((led_t *)acpi_asus_led, &sc->s_bled, "bled");
  603         }
  604 
  605         if (sc->model->mled_set) {
  606                 sc->s_mled.busy = 0;
  607                 sc->s_mled.sc = sc;
  608                 sc->s_mled.type = ACPI_ASUS_LED_MLED;
  609                 sc->s_mled.cdev =
  610                     led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
  611         }
  612 
  613         if (sc->model->tled_set) {
  614                 sc->s_tled.busy = 0;
  615                 sc->s_tled.sc = sc;
  616                 sc->s_tled.type = ACPI_ASUS_LED_TLED;
  617                 sc->s_tled.cdev =
  618                     led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled");
  619         }
  620 
  621         if (sc->model->wled_set) {
  622                 sc->s_wled.busy = 0;
  623                 sc->s_wled.sc = sc;
  624                 sc->s_wled.type = ACPI_ASUS_LED_WLED;
  625                 sc->s_wled.cdev =
  626                     led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled");
  627         }
  628 
  629         /* Activate hotkeys */
  630         AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
  631 
  632         /* Handle notifies */
  633         AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
  634             acpi_asus_notify, dev);
  635 
  636         return (0);
  637 }
  638 
  639 static int
  640 acpi_asus_detach(device_t dev)
  641 {
  642         struct acpi_asus_softc  *sc;
  643 
  644         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  645 
  646         sc = device_get_softc(dev);
  647 
  648         /* Turn the lights off */
  649         if (sc->model->bled_set)
  650                 led_destroy(sc->s_bled.cdev);
  651 
  652         if (sc->model->mled_set)
  653                 led_destroy(sc->s_mled.cdev);
  654 
  655         if (sc->model->tled_set)
  656                 led_destroy(sc->s_tled.cdev);
  657 
  658         if (sc->model->wled_set)
  659                 led_destroy(sc->s_wled.cdev);
  660 
  661         /* Remove notify handler */
  662         AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
  663             acpi_asus_notify);
  664 
  665         /* Free sysctl tree */
  666         sysctl_ctx_free(&sc->sysctl_ctx);
  667 
  668         return (0);
  669 }
  670 
  671 static void
  672 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
  673 {
  674         struct acpi_asus_softc  *sc;
  675         char                    *method;
  676         int                     state;
  677         
  678         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  679 
  680         sc = led->sc;
  681 
  682         switch (led->type) {
  683         case ACPI_ASUS_LED_BLED:
  684                 method = sc->model->bled_set;
  685                 state = led->state;
  686                 break;
  687         case ACPI_ASUS_LED_MLED:
  688                 method = sc->model->mled_set;
  689 
  690                 /* Note: inverted */
  691                 state = !led->state;
  692                 break;
  693         case ACPI_ASUS_LED_TLED:
  694                 method = sc->model->tled_set;
  695                 state = led->state;
  696                 break;
  697         case ACPI_ASUS_LED_WLED:
  698                 method = sc->model->wled_set;
  699                 state = led->state;
  700                 break;
  701         default:
  702                 printf("acpi_asus_led: invalid LED type %d\n",
  703                     (int)led->type);
  704                 return;
  705         }
  706 
  707         acpi_SetInteger(sc->handle, method, state);
  708         led->busy = 0;
  709 }
  710 
  711 static void
  712 acpi_asus_led(struct acpi_asus_led *led, int state)
  713 {
  714 
  715         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  716 
  717         if (led->busy)
  718                 return;
  719 
  720         led->busy = 1;
  721         led->state = state;
  722 
  723         AcpiOsQueueForExecution(OSD_PRIORITY_LO,
  724             (void *)acpi_asus_led_task, led);
  725 }
  726 
  727 static int
  728 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
  729 {
  730         struct acpi_asus_softc  *sc;
  731         int                     arg;
  732         int                     error = 0;
  733         int                     function;
  734         int                     method;
  735         
  736         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  737 
  738         sc = (struct acpi_asus_softc *)oidp->oid_arg1;
  739         function = oidp->oid_arg2;
  740         method = acpi_asus_sysctls[function].method;
  741 
  742         ACPI_SERIAL_BEGIN(asus);
  743         arg = acpi_asus_sysctl_get(sc, method);
  744         error = sysctl_handle_int(oidp, &arg, 0, req);
  745 
  746         /* Sanity check */
  747         if (error != 0 || req->newptr == NULL)
  748                 goto out;
  749 
  750         /* Update */
  751         error = acpi_asus_sysctl_set(sc, method, arg);
  752 
  753 out:
  754         ACPI_SERIAL_END(asus);
  755         return (error);
  756 }
  757 
  758 static int
  759 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
  760 {
  761         int val = 0;
  762 
  763         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  764         ACPI_SERIAL_ASSERT(asus);
  765 
  766         switch (method) {
  767         case ACPI_ASUS_METHOD_BRN:
  768                 val = sc->s_brn;
  769                 break;
  770         case ACPI_ASUS_METHOD_DISP:
  771                 val = sc->s_disp;
  772                 break;
  773         case ACPI_ASUS_METHOD_LCD:
  774                 val = sc->s_lcd;
  775                 break;
  776         }
  777 
  778         return (val);
  779 }
  780 
  781 static int
  782 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
  783 {
  784         ACPI_STATUS     status = AE_OK;
  785 
  786         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  787         ACPI_SERIAL_ASSERT(asus);
  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         ACPI_SERIAL_BEGIN(asus);
  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         ACPI_SERIAL_END(asus);
  944 }

Cache object: 77a542f4f243f69007af3c1214df917f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.