acpi_asus.c revision 143937
1128561Sphilip/*- 2128561Sphilip * Copyright (c) 2004 Philip Paeps <philip@FreeBSD.org> 3128561Sphilip * All rights reserved. 4128561Sphilip * 5128561Sphilip * Redistribution and use in source and binary forms, with or without 6128561Sphilip * modification, are permitted provided that the following conditions 7128561Sphilip * are met: 8128561Sphilip * 1. Redistributions of source code must retain the above copyright 9128561Sphilip * notice, this list of conditions and the following disclaimer. 10128561Sphilip * 2. Redistributions in binary form must reproduce the above copyright 11128561Sphilip * notice, this list of conditions and the following disclaimer in the 12128561Sphilip * documentation and/or other materials provided with the distribution. 13128561Sphilip * 14128561Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15128561Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16128561Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17128561Sphilip * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18128561Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19128561Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20128561Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21128561Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22128561Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23128561Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24128561Sphilip * SUCH DAMAGE. 25128561Sphilip */ 26128561Sphilip 27128561Sphilip#include <sys/cdefs.h> 28128561Sphilip__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_asus.c 143937 2005-03-21 18:11:50Z philip $"); 29128561Sphilip 30128561Sphilip/* 31128561Sphilip * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on 32133095Sphilip * recent Asus (and Medion) laptops. Inspired by the acpi4asus project which 33128561Sphilip * implements these features in the Linux kernel. 34128561Sphilip * 35128561Sphilip * <http://sourceforge.net/projects/acpi4asus/> 36128561Sphilip * 37128561Sphilip * Currently should support most features, but could use some more testing. 38128561Sphilip * Particularly the display-switching stuff is a bit hairy. If you have an 39128561Sphilip * Asus laptop which doesn't appear to be supported, or strange things happen 40128561Sphilip * when using this driver, please report to <acpi@FreeBSD.org>. 41128561Sphilip */ 42128561Sphilip 43128561Sphilip#include "opt_acpi.h" 44128561Sphilip#include <sys/param.h> 45128561Sphilip#include <sys/kernel.h> 46129882Sphk#include <sys/module.h> 47128561Sphilip#include <sys/bus.h> 48128561Sphilip#include <sys/sbuf.h> 49128561Sphilip 50128561Sphilip#include "acpi.h" 51128561Sphilip#include <dev/acpica/acpivar.h> 52128561Sphilip#include <dev/led/led.h> 53128561Sphilip 54143894Sphilip/* Methods */ 55143894Sphilip#define ACPI_ASUS_METHOD_BRN 1 56143894Sphilip#define ACPI_ASUS_METHOD_DISP 2 57143894Sphilip#define ACPI_ASUS_METHOD_LCD 3 58143894Sphilip 59138825Snjl#define _COMPONENT ACPI_OEM 60128561SphilipACPI_MODULE_NAME("ASUS") 61128561Sphilip 62128561Sphilipstruct acpi_asus_model { 63128561Sphilip char *name; 64128561Sphilip 65128561Sphilip char *mled_set; 66128561Sphilip char *tled_set; 67128561Sphilip char *wled_set; 68128561Sphilip 69128561Sphilip char *brn_get; 70128561Sphilip char *brn_set; 71128561Sphilip char *brn_up; 72128561Sphilip char *brn_dn; 73128561Sphilip 74128561Sphilip char *lcd_get; 75128561Sphilip char *lcd_set; 76128561Sphilip 77128561Sphilip char *disp_get; 78128561Sphilip char *disp_set; 79128561Sphilip}; 80128561Sphilip 81133095Sphilipstruct acpi_asus_led { 82133095Sphilip struct cdev *cdev; 83133095Sphilip device_t dev; 84133095Sphilip enum { 85133095Sphilip ACPI_ASUS_LED_MLED, 86133095Sphilip ACPI_ASUS_LED_TLED, 87133095Sphilip ACPI_ASUS_LED_WLED, 88133095Sphilip } type; 89133095Sphilip}; 90133095Sphilip 91128561Sphilipstruct acpi_asus_softc { 92128561Sphilip device_t dev; 93128561Sphilip ACPI_HANDLE handle; 94128561Sphilip 95128561Sphilip struct acpi_asus_model *model; 96128561Sphilip struct sysctl_ctx_list sysctl_ctx; 97128561Sphilip struct sysctl_oid *sysctl_tree; 98128561Sphilip 99133095Sphilip struct acpi_asus_led s_mled; 100133095Sphilip struct acpi_asus_led s_tled; 101133095Sphilip struct acpi_asus_led s_wled; 102128561Sphilip 103128561Sphilip int s_brn; 104128561Sphilip int s_disp; 105128561Sphilip int s_lcd; 106128561Sphilip}; 107128561Sphilip 108137245Sphilip/* 109137245Sphilip * We can identify Asus laptops from the string they return 110137245Sphilip * as a result of calling the ATK0100 'INIT' method. 111137245Sphilip */ 112128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = { 113128561Sphilip { 114128561Sphilip .name = "L2D", 115128561Sphilip .mled_set = "MLED", 116128561Sphilip .wled_set = "WLED", 117128561Sphilip .brn_up = "\\Q0E", 118128561Sphilip .brn_dn = "\\Q0F", 119128561Sphilip .lcd_get = "\\SGP0", 120128561Sphilip .lcd_set = "\\Q10" 121128561Sphilip }, 122128561Sphilip { 123128561Sphilip .name = "L3C", 124128561Sphilip .mled_set = "MLED", 125128561Sphilip .wled_set = "WLED", 126128561Sphilip .brn_get = "GPLV", 127128561Sphilip .brn_set = "SPLV", 128128561Sphilip .lcd_get = "\\GL32", 129128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 130128561Sphilip }, 131128561Sphilip { 132128561Sphilip .name = "L3D", 133128561Sphilip .mled_set = "MLED", 134128561Sphilip .wled_set = "WLED", 135128561Sphilip .brn_get = "GPLV", 136128561Sphilip .brn_set = "SPLV", 137128561Sphilip .lcd_get = "\\BKLG", 138128561Sphilip .lcd_set = "\\Q10" 139128561Sphilip }, 140128561Sphilip { 141128561Sphilip .name = "L3H", 142128561Sphilip .mled_set = "MLED", 143128561Sphilip .wled_set = "WLED", 144128561Sphilip .brn_get = "GPLV", 145128561Sphilip .brn_set = "SPLV", 146128561Sphilip .lcd_get = "\\_SB.PCI0.PM.PBC", 147128561Sphilip .lcd_set = "EHK", 148128561Sphilip .disp_get = "\\_SB.INFB", 149128561Sphilip .disp_set = "SDSP" 150128561Sphilip }, 151128561Sphilip { 152137388Sphilip .name = "L4R", 153137388Sphilip .mled_set = "MLED", 154137388Sphilip .wled_set = "WLED", 155137388Sphilip .brn_get = "GPLV", 156137388Sphilip .brn_set = "SPLV", 157137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 158137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 159137388Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 160137388Sphilip .disp_set = "SDSP" 161137388Sphilip }, 162137388Sphilip { 163128561Sphilip .name = "L8L" 164128561Sphilip /* Only has hotkeys, apparantly */ 165128561Sphilip }, 166128561Sphilip { 167128561Sphilip .name = "M1A", 168128561Sphilip .mled_set = "MLED", 169128561Sphilip .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 170128561Sphilip .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 171128561Sphilip .lcd_get = "\\PNOF", 172128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 173128561Sphilip }, 174128561Sphilip { 175128561Sphilip .name = "M2E", 176128561Sphilip .mled_set = "MLED", 177128561Sphilip .wled_set = "WLED", 178128561Sphilip .brn_get = "GPLV", 179128561Sphilip .brn_set = "SPLV", 180128561Sphilip .lcd_get = "\\GP06", 181128561Sphilip .lcd_set = "\\Q10" 182128561Sphilip }, 183128561Sphilip { 184137127Sphilip .name = "M6N", 185137127Sphilip .mled_set = "MLED", 186137127Sphilip .wled_set = "WLED", 187137127Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 188137127Sphilip .lcd_get = "\\_SB.BKLT", 189137127Sphilip .brn_set = "SPLV", 190137127Sphilip .brn_get = "GPLV", 191137127Sphilip .disp_set = "SDSP", 192137127Sphilip .disp_get = "\\SSTE" 193137127Sphilip }, 194137388Sphilip { 195137388Sphilip .name = "M6R", 196137388Sphilip .mled_set = "MLED", 197137388Sphilip .wled_set = "WLED", 198137388Sphilip .brn_get = "GPLV", 199137388Sphilip .brn_set = "SPLV", 200137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 201137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 202137388Sphilip .disp_get = "\\SSTE", 203137388Sphilip .disp_set = "SDSP" 204137388Sphilip }, 205137245Sphilip 206137245Sphilip { .name = NULL } 207137245Sphilip}; 208137245Sphilip 209137245Sphilip/* 210137245Sphilip * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 211137245Sphilip * but they can't be probed quite the same way as Asus laptops. 212137245Sphilip */ 213137245Sphilipstatic struct acpi_asus_model acpi_samsung_models[] = { 214137127Sphilip { 215128561Sphilip .name = "P30", 216128561Sphilip .wled_set = "WLED", 217128561Sphilip .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 218128561Sphilip .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 219128561Sphilip .lcd_get = "\\BKLT", 220128561Sphilip .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 221128561Sphilip }, 222128561Sphilip 223128561Sphilip { .name = NULL } 224128561Sphilip}; 225128561Sphilip 226143894Sphilipstatic struct { 227143894Sphilip char *name; 228143894Sphilip char *description; 229143894Sphilip int method; 230143894Sphilip} acpi_asus_sysctls[] = { 231143894Sphilip { 232143894Sphilip .name = "lcd_backlight", 233143894Sphilip .method = ACPI_ASUS_METHOD_LCD, 234143894Sphilip .description = "state of the lcd backlight" 235143894Sphilip }, 236143894Sphilip { 237143894Sphilip .name = "lcd_brightness", 238143894Sphilip .method = ACPI_ASUS_METHOD_BRN, 239143894Sphilip .description = "brightness of the lcd panel" 240143894Sphilip }, 241143894Sphilip { 242143894Sphilip .name = "video_output", 243143894Sphilip .method = ACPI_ASUS_METHOD_DISP, 244143894Sphilip .description = "display output state" 245143894Sphilip }, 246143894Sphilip 247143894Sphilip { .name = NULL } 248143894Sphilip}; 249143894Sphilip 250133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 251133628Snjl 252128561Sphilip/* Function prototypes */ 253128561Sphilipstatic int acpi_asus_probe(device_t dev); 254128561Sphilipstatic int acpi_asus_attach(device_t dev); 255128561Sphilipstatic int acpi_asus_detach(device_t dev); 256128561Sphilip 257133095Sphilipstatic void acpi_asus_led(struct acpi_asus_led *led, int state); 258128561Sphilip 259143894Sphilipstatic int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 260143894Sphilipstatic int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 261143894Sphilipstatic int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 262143894Sphilipstatic int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 263128561Sphilip 264128561Sphilipstatic void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 265128561Sphilip 266128561Sphilipstatic device_method_t acpi_asus_methods[] = { 267137631Sphilip DEVMETHOD(device_probe, acpi_asus_probe), 268128561Sphilip DEVMETHOD(device_attach, acpi_asus_attach), 269128561Sphilip DEVMETHOD(device_detach, acpi_asus_detach), 270128561Sphilip 271128561Sphilip { 0, 0 } 272128561Sphilip}; 273128561Sphilip 274128561Sphilipstatic driver_t acpi_asus_driver = { 275128561Sphilip "acpi_asus", 276128561Sphilip acpi_asus_methods, 277128561Sphilip sizeof(struct acpi_asus_softc) 278128561Sphilip}; 279128561Sphilip 280128561Sphilipstatic devclass_t acpi_asus_devclass; 281128561Sphilip 282128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 283128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 284128561Sphilip 285128561Sphilipstatic int 286128561Sphilipacpi_asus_probe(device_t dev) 287128561Sphilip{ 288128561Sphilip struct acpi_asus_model *model; 289128561Sphilip struct acpi_asus_softc *sc; 290128561Sphilip struct sbuf *sb; 291128561Sphilip ACPI_BUFFER Buf; 292128561Sphilip ACPI_OBJECT Arg, *Obj; 293128561Sphilip ACPI_OBJECT_LIST Args; 294137631Sphilip static char *asus_ids[] = { "ATK0100", NULL }; 295128561Sphilip 296128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 297128561Sphilip 298137632Sphilip if (acpi_disabled("asus") || 299137632Sphilip ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids) == NULL) 300137632Sphilip return (ENXIO); 301137631Sphilip 302137632Sphilip sc = device_get_softc(dev); 303137632Sphilip sc->dev = dev; 304137632Sphilip sc->handle = acpi_get_handle(dev); 305128561Sphilip 306137632Sphilip Arg.Type = ACPI_TYPE_INTEGER; 307137632Sphilip Arg.Integer.Value = 0; 308128561Sphilip 309137632Sphilip Args.Count = 1; 310137632Sphilip Args.Pointer = &Arg; 311137631Sphilip 312137632Sphilip Buf.Pointer = NULL; 313137632Sphilip Buf.Length = ACPI_ALLOCATE_BUFFER; 314128561Sphilip 315137632Sphilip AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 316137632Sphilip Obj = Buf.Pointer; 317137245Sphilip 318137632Sphilip /* 319137632Sphilip * The Samsung P30 returns a null-pointer from INIT, we 320137632Sphilip * can identify it from the 'ODEM' string in the DSDT. 321137632Sphilip */ 322137632Sphilip if (Obj->String.Pointer == NULL) { 323137632Sphilip ACPI_STATUS status; 324137632Sphilip ACPI_TABLE_HEADER th; 325137245Sphilip 326137632Sphilip status = AcpiGetTableHeader(ACPI_TABLE_DSDT, 1, &th); 327137632Sphilip if (ACPI_FAILURE(status)) { 328137632Sphilip device_printf(dev, "Unsupported (Samsung?) laptop\n"); 329137632Sphilip AcpiOsFree(Buf.Pointer); 330137632Sphilip return (ENXIO); 331137245Sphilip } 332137245Sphilip 333137632Sphilip if (strncmp("ODEM", th.OemTableId, 4) == 0) { 334137632Sphilip sc->model = &acpi_samsung_models[0]; 335137632Sphilip device_set_desc(dev, "Samsung P30 Laptop Extras"); 336137632Sphilip AcpiOsFree(Buf.Pointer); 337137632Sphilip return (0); 338137632Sphilip } 339137632Sphilip } 340137245Sphilip 341137632Sphilip sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 342137632Sphilip if (sb == NULL) 343137632Sphilip return (ENOMEM); 344128561Sphilip 345137632Sphilip /* 346137632Sphilip * Asus laptops are simply identified by name, easy! 347137632Sphilip */ 348137632Sphilip for (model = acpi_asus_models; model->name != NULL; model++) 349137632Sphilip if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 350137632Sphilip sbuf_printf(sb, "Asus %s Laptop Extras", model->name); 351137632Sphilip sbuf_finish(sb); 352128561Sphilip 353137632Sphilip sc->model = model; 354137632Sphilip device_set_desc(dev, sbuf_data(sb)); 355128561Sphilip 356137632Sphilip sbuf_delete(sb); 357137632Sphilip AcpiOsFree(Buf.Pointer); 358137632Sphilip return (0); 359137632Sphilip } 360128561Sphilip 361137632Sphilip sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 362137632Sphilip sbuf_finish(sb); 363128561Sphilip 364137632Sphilip device_printf(dev, sbuf_data(sb)); 365137632Sphilip 366137632Sphilip sbuf_delete(sb); 367137632Sphilip AcpiOsFree(Buf.Pointer); 368137632Sphilip 369128561Sphilip return (ENXIO); 370128561Sphilip} 371128561Sphilip 372128561Sphilipstatic int 373128561Sphilipacpi_asus_attach(device_t dev) 374128561Sphilip{ 375128561Sphilip struct acpi_asus_softc *sc; 376128561Sphilip struct acpi_softc *acpi_sc; 377128561Sphilip 378128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 379128561Sphilip 380128561Sphilip sc = device_get_softc(dev); 381128561Sphilip acpi_sc = acpi_device_get_parent_softc(dev); 382128561Sphilip 383128561Sphilip /* Build sysctl tree */ 384128561Sphilip sysctl_ctx_init(&sc->sysctl_ctx); 385128561Sphilip sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 386128561Sphilip SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 387128561Sphilip OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 388128561Sphilip 389143894Sphilip /* Hook up nodes */ 390143894Sphilip for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 391143894Sphilip if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 392143894Sphilip continue; 393143894Sphilip 394143894Sphilip SYSCTL_ADD_PROC(&sc->sysctl_ctx, 395143894Sphilip SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 396143894Sphilip acpi_asus_sysctls[i].name, 397143894Sphilip CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 398143894Sphilip sc, i, acpi_asus_sysctl, "I", 399143894Sphilip acpi_asus_sysctls[i].description); 400143894Sphilip } 401143894Sphilip 402128561Sphilip /* Attach leds */ 403133095Sphilip if (sc->model->mled_set) { 404133095Sphilip sc->s_mled.dev = dev; 405133095Sphilip sc->s_mled.type = ACPI_ASUS_LED_MLED; 406133095Sphilip sc->s_mled.cdev = 407133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 408133095Sphilip } 409128561Sphilip 410133095Sphilip if (sc->model->tled_set) { 411133095Sphilip sc->s_tled.dev = dev; 412133095Sphilip sc->s_tled.type = ACPI_ASUS_LED_TLED; 413133095Sphilip sc->s_tled.cdev = 414133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled"); 415133095Sphilip } 416128561Sphilip 417133095Sphilip if (sc->model->wled_set) { 418133095Sphilip sc->s_wled.dev = dev; 419133095Sphilip sc->s_wled.type = ACPI_ASUS_LED_WLED; 420133095Sphilip sc->s_wled.cdev = 421133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled"); 422133095Sphilip } 423128561Sphilip 424128561Sphilip /* Activate hotkeys */ 425128561Sphilip AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 426128561Sphilip 427128561Sphilip /* Handle notifies */ 428132610Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 429132610Snjl acpi_asus_notify, dev); 430137631Sphilip 431128561Sphilip return (0); 432128561Sphilip} 433128561Sphilip 434128561Sphilipstatic int 435128561Sphilipacpi_asus_detach(device_t dev) 436128561Sphilip{ 437128561Sphilip struct acpi_asus_softc *sc; 438137631Sphilip 439128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 440128561Sphilip 441128561Sphilip sc = device_get_softc(dev); 442128561Sphilip 443128561Sphilip /* Turn the lights off */ 444128561Sphilip if (sc->model->mled_set) 445133095Sphilip led_destroy(sc->s_mled.cdev); 446128561Sphilip 447128561Sphilip if (sc->model->tled_set) 448133095Sphilip led_destroy(sc->s_tled.cdev); 449128561Sphilip 450128561Sphilip if (sc->model->wled_set) 451133095Sphilip led_destroy(sc->s_wled.cdev); 452128561Sphilip 453128561Sphilip /* Remove notify handler */ 454132610Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 455132610Snjl acpi_asus_notify); 456128561Sphilip 457128561Sphilip /* Free sysctl tree */ 458128561Sphilip sysctl_ctx_free(&sc->sysctl_ctx); 459128561Sphilip 460128561Sphilip return (0); 461128561Sphilip} 462128561Sphilip 463128561Sphilipstatic void 464133095Sphilipacpi_asus_led(struct acpi_asus_led *led, int state) 465128561Sphilip{ 466128561Sphilip struct acpi_asus_softc *sc; 467133095Sphilip char *method; 468128561Sphilip 469128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 470128561Sphilip 471133095Sphilip sc = device_get_softc(led->dev); 472128561Sphilip 473133095Sphilip switch (led->type) { 474143894Sphilip case ACPI_ASUS_LED_MLED: 475143894Sphilip method = sc->model->mled_set; 476128561Sphilip 477143894Sphilip /* Note: inverted */ 478143894Sphilip state = !state; 479143894Sphilip break; 480143894Sphilip case ACPI_ASUS_LED_TLED: 481143894Sphilip method = sc->model->tled_set; 482143894Sphilip break; 483143894Sphilip case ACPI_ASUS_LED_WLED: 484143894Sphilip method = sc->model->wled_set; 485143894Sphilip break; 486143894Sphilip default: 487143894Sphilip printf("acpi_asus_led: invalid LED type %d\n", 488143894Sphilip (int)led->type); 489143894Sphilip return; 490133095Sphilip } 491128561Sphilip 492133095Sphilip acpi_SetInteger(sc->handle, method, state); 493128561Sphilip} 494128561Sphilip 495128561Sphilipstatic int 496143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 497128561Sphilip{ 498128561Sphilip struct acpi_asus_softc *sc; 499143894Sphilip int arg; 500143894Sphilip int error = 0; 501143894Sphilip int function; 502143894Sphilip int method; 503143894Sphilip 504128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 505128561Sphilip 506128561Sphilip sc = (struct acpi_asus_softc *)oidp->oid_arg1; 507143894Sphilip function = oidp->oid_arg2; 508143894Sphilip method = acpi_asus_sysctls[function].method; 509143894Sphilip 510133628Snjl ACPI_SERIAL_BEGIN(asus); 511143894Sphilip arg = acpi_asus_sysctl_get(sc, method); 512143894Sphilip error = sysctl_handle_int(oidp, &arg, 0, req); 513128561Sphilip 514128561Sphilip /* Sanity check */ 515143894Sphilip if (error != 0 || req->newptr == NULL) 516133092Snjl goto out; 517128561Sphilip 518143894Sphilip /* Update */ 519143894Sphilip error = acpi_asus_sysctl_set(sc, method, arg); 520128561Sphilip 521143894Sphilipout: 522143894Sphilip ACPI_SERIAL_END(asus); 523143894Sphilip return (error); 524143894Sphilip} 525128561Sphilip 526143894Sphilipstatic int 527143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 528143894Sphilip{ 529143894Sphilip int val = 0; 530128561Sphilip 531143894Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 532143894Sphilip ACPI_SERIAL_ASSERT(asus); 533143894Sphilip 534143894Sphilip switch (method) { 535143894Sphilip case ACPI_ASUS_METHOD_BRN: 536143894Sphilip val = sc->s_brn; 537143894Sphilip break; 538143894Sphilip case ACPI_ASUS_METHOD_DISP: 539143894Sphilip val = sc->s_disp; 540143894Sphilip break; 541143894Sphilip case ACPI_ASUS_METHOD_LCD: 542143894Sphilip val = sc->s_lcd; 543143894Sphilip break; 544128561Sphilip } 545128561Sphilip 546143894Sphilip return (val); 547128561Sphilip} 548128561Sphilip 549128561Sphilipstatic int 550143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 551128561Sphilip{ 552143937Sphilip ACPI_STATUS status = AE_OK; 553128561Sphilip 554128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 555143894Sphilip ACPI_SERIAL_ASSERT(asus); 556128561Sphilip 557143894Sphilip switch (method) { 558143894Sphilip case ACPI_ASUS_METHOD_BRN: 559143894Sphilip if (arg < 0 || arg > 15) 560143894Sphilip return (EINVAL); 561128561Sphilip 562143894Sphilip if (sc->model->brn_set) 563143894Sphilip status = acpi_SetInteger(sc->handle, 564143894Sphilip sc->model->brn_set, arg); 565143894Sphilip else { 566143894Sphilip while (arg != 0) { 567143894Sphilip status = AcpiEvaluateObject(sc->handle, 568143894Sphilip (arg > 0) ? sc->model->brn_up : 569143894Sphilip sc->model->brn_dn, NULL, NULL); 570143894Sphilip (arg > 0) ? arg-- : arg++; 571143894Sphilip } 572143894Sphilip } 573128561Sphilip 574143894Sphilip if (ACPI_SUCCESS(status)) 575143894Sphilip sc->s_brn = arg; 576128561Sphilip 577143894Sphilip break; 578143894Sphilip case ACPI_ASUS_METHOD_DISP: 579143894Sphilip if (arg < 0 || arg > 7) 580143894Sphilip return (EINVAL); 581128561Sphilip 582143894Sphilip status = acpi_SetInteger(sc->handle, 583143894Sphilip sc->model->disp_set, arg); 584128561Sphilip 585143894Sphilip if (ACPI_SUCCESS(status)) 586143894Sphilip sc->s_disp = arg; 587128561Sphilip 588143894Sphilip break; 589143894Sphilip case ACPI_ASUS_METHOD_LCD: 590143894Sphilip if (arg < 0 || arg > 1) 591143894Sphilip return (EINVAL); 592143894Sphilip 593143894Sphilip if (strncmp(sc->model->name, "L3H", 3) != 0) 594143894Sphilip status = AcpiEvaluateObject(sc->handle, 595143894Sphilip sc->model->lcd_set, NULL, NULL); 596143894Sphilip else 597143894Sphilip status = acpi_SetInteger(sc->handle, 598143894Sphilip sc->model->lcd_set, 0x7); 599143894Sphilip 600143894Sphilip if (ACPI_SUCCESS(status)) 601143894Sphilip sc->s_lcd = arg; 602143894Sphilip 603143894Sphilip break; 604143894Sphilip } 605143894Sphilip 606143894Sphilip return (0); 607128561Sphilip} 608128561Sphilip 609128561Sphilipstatic int 610143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 611128561Sphilip{ 612143894Sphilip ACPI_STATUS status; 613128561Sphilip 614143894Sphilip switch (method) { 615143894Sphilip case ACPI_ASUS_METHOD_BRN: 616143894Sphilip if (sc->model->brn_get) { 617143894Sphilip /* GPLV/SPLV models */ 618143894Sphilip status = acpi_GetInteger(sc->handle, 619143894Sphilip sc->model->brn_get, &sc->s_brn); 620143894Sphilip if (ACPI_SUCCESS(status)) 621143894Sphilip return (TRUE); 622143894Sphilip } else if (sc->model->brn_up) { 623143894Sphilip /* Relative models */ 624143894Sphilip status = AcpiEvaluateObject(sc->handle, 625143894Sphilip sc->model->brn_up, NULL, NULL); 626143894Sphilip if (ACPI_FAILURE(status)) 627143894Sphilip return (FALSE); 628128561Sphilip 629143894Sphilip status = AcpiEvaluateObject(sc->handle, 630143894Sphilip sc->model->brn_dn, NULL, NULL); 631143894Sphilip if (ACPI_FAILURE(status)) 632143894Sphilip return (FALSE); 633128561Sphilip 634143894Sphilip return (TRUE); 635143894Sphilip } 636143894Sphilip return (FALSE); 637143894Sphilip case ACPI_ASUS_METHOD_DISP: 638143894Sphilip if (sc->model->disp_get) { 639143894Sphilip status = acpi_GetInteger(sc->handle, 640143894Sphilip sc->model->disp_get, &sc->s_disp); 641143894Sphilip if (ACPI_SUCCESS(status)) 642143894Sphilip return (TRUE); 643143894Sphilip } 644143894Sphilip return (FALSE); 645143894Sphilip case ACPI_ASUS_METHOD_LCD: 646143894Sphilip if (sc->model->lcd_get && 647143894Sphilip strncmp(sc->model->name, "L3H", 3) != 0) { 648143894Sphilip status = acpi_GetInteger(sc->handle, 649143894Sphilip sc->model->lcd_get, &sc->s_lcd); 650143894Sphilip if (ACPI_SUCCESS(status)) 651143894Sphilip return (TRUE); 652143894Sphilip } 653143894Sphilip else if (sc->model->lcd_get) { 654143894Sphilip ACPI_BUFFER Buf; 655143894Sphilip ACPI_OBJECT Arg[2], Obj; 656143894Sphilip ACPI_OBJECT_LIST Args; 657128561Sphilip 658143894Sphilip /* L3H is a bit special */ 659143894Sphilip Arg[0].Type = ACPI_TYPE_INTEGER; 660143894Sphilip Arg[0].Integer.Value = 0x02; 661143894Sphilip Arg[1].Type = ACPI_TYPE_INTEGER; 662143894Sphilip Arg[1].Integer.Value = 0x03; 663128561Sphilip 664143894Sphilip Args.Count = 2; 665143894Sphilip Args.Pointer = Arg; 666128561Sphilip 667143894Sphilip Buf.Length = sizeof(Obj); 668143894Sphilip Buf.Pointer = &Obj; 669128561Sphilip 670143894Sphilip status = AcpiEvaluateObject(sc->handle, 671143894Sphilip sc->model->lcd_get, &Args, &Buf); 672143894Sphilip if (ACPI_SUCCESS(status) && 673143894Sphilip Obj.Type == ACPI_TYPE_INTEGER) { 674143894Sphilip sc->s_lcd = Obj.Integer.Value >> 8; 675143894Sphilip return (TRUE); 676143894Sphilip } 677143894Sphilip } 678143894Sphilip return (FALSE); 679143894Sphilip } 680143894Sphilip return (FALSE); 681128561Sphilip} 682128561Sphilip 683128561Sphilipstatic void 684128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 685128561Sphilip{ 686128561Sphilip struct acpi_asus_softc *sc; 687128561Sphilip struct acpi_softc *acpi_sc; 688128561Sphilip 689128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 690128561Sphilip 691128561Sphilip sc = device_get_softc((device_t)context); 692128561Sphilip acpi_sc = acpi_device_get_parent_softc(sc->dev); 693128561Sphilip 694133628Snjl ACPI_SERIAL_BEGIN(asus); 695128561Sphilip if ((notify & ~0x10) <= 15) { 696132610Snjl sc->s_brn = notify & ~0x10; 697128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 698128561Sphilip } else if ((notify & ~0x20) <= 15) { 699132610Snjl sc->s_brn = notify & ~0x20; 700128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 701128561Sphilip } else if (notify == 0x33) { 702128561Sphilip sc->s_lcd = 1; 703128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 704128561Sphilip } else if (notify == 0x34) { 705128561Sphilip sc->s_lcd = 0; 706128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 707128561Sphilip } else { 708128561Sphilip /* Notify devd(8) */ 709128561Sphilip acpi_UserNotify("ASUS", h, notify); 710128561Sphilip } 711133628Snjl ACPI_SERIAL_END(asus); 712128561Sphilip} 713