acpi_asus.c revision 180075
1128561Sphilip/*- 2146024Sphilip * Copyright (c) 2004, 2005 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 180075 2008-06-28 08:36:47Z remko $"); 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 50150003Sobrien#include <contrib/dev/acpica/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 58180062Srpaulo#define ACPI_ASUS_METHOD_CAMERA 4 59180075Sremko#define ACPI_ASUS_METHOD_CARDRD 5 60143894Sphilip 61138825Snjl#define _COMPONENT ACPI_OEM 62128561SphilipACPI_MODULE_NAME("ASUS") 63128561Sphilip 64128561Sphilipstruct acpi_asus_model { 65128561Sphilip char *name; 66128561Sphilip 67146022Sphilip char *bled_set; 68178069Sjkim char *dled_set; 69178069Sjkim char *gled_set; 70128561Sphilip char *mled_set; 71128561Sphilip char *tled_set; 72128561Sphilip char *wled_set; 73128561Sphilip 74128561Sphilip char *brn_get; 75128561Sphilip char *brn_set; 76128561Sphilip char *brn_up; 77128561Sphilip char *brn_dn; 78128561Sphilip 79128561Sphilip char *lcd_get; 80128561Sphilip char *lcd_set; 81128561Sphilip 82128561Sphilip char *disp_get; 83128561Sphilip char *disp_set; 84180062Srpaulo 85180062Srpaulo char *cam_get; 86180062Srpaulo char *cam_set; 87180062Srpaulo 88180062Srpaulo char *crd_get; 89180062Srpaulo char *crd_set; 90180062Srpaulo 91180062Srpaulo void (*n_func)(ACPI_HANDLE, UINT32, void *); 92128561Sphilip}; 93128561Sphilip 94133095Sphilipstruct acpi_asus_led { 95144339Sphilip struct acpi_asus_softc *sc; 96133095Sphilip struct cdev *cdev; 97144339Sphilip int busy; 98144339Sphilip int state; 99133095Sphilip enum { 100146022Sphilip ACPI_ASUS_LED_BLED, 101178069Sjkim ACPI_ASUS_LED_DLED, 102178069Sjkim ACPI_ASUS_LED_GLED, 103133095Sphilip ACPI_ASUS_LED_MLED, 104133095Sphilip ACPI_ASUS_LED_TLED, 105133095Sphilip ACPI_ASUS_LED_WLED, 106133095Sphilip } type; 107133095Sphilip}; 108133095Sphilip 109128561Sphilipstruct acpi_asus_softc { 110128561Sphilip device_t dev; 111128561Sphilip ACPI_HANDLE handle; 112128561Sphilip 113128561Sphilip struct acpi_asus_model *model; 114128561Sphilip struct sysctl_ctx_list sysctl_ctx; 115128561Sphilip struct sysctl_oid *sysctl_tree; 116128561Sphilip 117146022Sphilip struct acpi_asus_led s_bled; 118178069Sjkim struct acpi_asus_led s_dled; 119178069Sjkim struct acpi_asus_led s_gled; 120133095Sphilip struct acpi_asus_led s_mled; 121133095Sphilip struct acpi_asus_led s_tled; 122133095Sphilip struct acpi_asus_led s_wled; 123128561Sphilip 124128561Sphilip int s_brn; 125128561Sphilip int s_disp; 126128561Sphilip int s_lcd; 127180062Srpaulo int s_cam; 128180062Srpaulo int s_crd; 129128561Sphilip}; 130128561Sphilip 131137245Sphilip/* 132137245Sphilip * We can identify Asus laptops from the string they return 133137245Sphilip * as a result of calling the ATK0100 'INIT' method. 134137245Sphilip */ 135128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = { 136128561Sphilip { 137146024Sphilip .name = "xxN", 138146024Sphilip .mled_set = "MLED", 139146024Sphilip .wled_set = "WLED", 140146024Sphilip .lcd_get = "\\BKLT", 141146024Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 142146024Sphilip .brn_get = "GPLV", 143146024Sphilip .brn_set = "SPLV", 144146024Sphilip .disp_get = "\\ADVG", 145146024Sphilip .disp_set = "SDSP" 146146024Sphilip }, 147146024Sphilip { 148146024Sphilip .name = "A1x", 149146024Sphilip .mled_set = "MLED", 150146024Sphilip .lcd_get = "\\BKLI", 151146024Sphilip .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 152146024Sphilip .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E", 153146024Sphilip .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F" 154146024Sphilip }, 155146024Sphilip { 156146024Sphilip .name = "A2x", 157146024Sphilip .mled_set = "MLED", 158146024Sphilip .wled_set = "WLED", 159146024Sphilip .lcd_get = "\\BAOF", 160146024Sphilip .lcd_set = "\\Q10", 161146024Sphilip .brn_get = "GPLV", 162146024Sphilip .brn_set = "SPLV", 163146024Sphilip .disp_get = "\\INFB", 164146024Sphilip .disp_set = "SDSP" 165146024Sphilip }, 166146024Sphilip { 167170216Sphilip .name = "A3N", 168170216Sphilip .mled_set = "MLED", 169170216Sphilip .bled_set = "BLED", 170170216Sphilip .wled_set = "WLED", 171170216Sphilip .lcd_get = NULL, 172170216Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 173170216Sphilip .brn_set = "SPLV", 174170216Sphilip .brn_get = "SDSP", 175170216Sphilip .disp_set = "SDSP", 176170216Sphilip .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD" 177170216Sphilip }, 178170216Sphilip { 179155022Sphilip .name = "A4D", 180155022Sphilip .mled_set = "MLED", 181155022Sphilip .brn_up = "\\_SB_.PCI0.SBRG.EC0._Q0E", 182155022Sphilip .brn_dn = "\\_SB_.PCI0.SBRG.EC0._Q0F", 183155022Sphilip .brn_get = "GPLV", 184155022Sphilip .brn_set = "SPLV", 185155022Sphilip#ifdef notyet 186155022Sphilip .disp_get = "\\_SB_.PCI0.SBRG.EC0._Q10", 187155022Sphilip .disp_set = "\\_SB_.PCI0.SBRG.EC0._Q11" 188155022Sphilip#endif 189155022Sphilip }, 190155022Sphilip { 191155021Sphilip .name = "A6V", 192155021Sphilip .bled_set = "BLED", 193155021Sphilip .mled_set = "MLED", 194155021Sphilip .wled_set = "WLED", 195155021Sphilip .lcd_get = NULL, 196155021Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 197155021Sphilip .brn_get = "GPLV", 198155021Sphilip .brn_set = "SPLV", 199155021Sphilip .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", 200155021Sphilip .disp_set = "SDSP" 201155021Sphilip }, 202155021Sphilip { 203146024Sphilip .name = "D1x", 204146024Sphilip .mled_set = "MLED", 205146024Sphilip .lcd_get = "\\GP11", 206146024Sphilip .lcd_set = "\\Q0D", 207146024Sphilip .brn_up = "\\Q0C", 208146024Sphilip .brn_dn = "\\Q0B", 209146024Sphilip .disp_get = "\\INFB", 210146024Sphilip .disp_set = "SDSP" 211146024Sphilip }, 212146024Sphilip { 213178069Sjkim .name = "G2K", 214178069Sjkim .bled_set = "BLED", 215178069Sjkim .dled_set = "DLED", 216178069Sjkim .gled_set = "GLED", 217178069Sjkim .mled_set = "MLED", 218178069Sjkim .tled_set = "TLED", 219178069Sjkim .wled_set = "WLED", 220178069Sjkim .brn_get = "GPLV", 221178069Sjkim .brn_set = "SPLV", 222178069Sjkim .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN", 223178069Sjkim .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 224178069Sjkim .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD", 225178069Sjkim .disp_set = "SDSP", 226178069Sjkim }, 227178069Sjkim { 228128561Sphilip .name = "L2D", 229128561Sphilip .mled_set = "MLED", 230128561Sphilip .wled_set = "WLED", 231128561Sphilip .brn_up = "\\Q0E", 232128561Sphilip .brn_dn = "\\Q0F", 233128561Sphilip .lcd_get = "\\SGP0", 234128561Sphilip .lcd_set = "\\Q10" 235128561Sphilip }, 236128561Sphilip { 237128561Sphilip .name = "L3C", 238128561Sphilip .mled_set = "MLED", 239128561Sphilip .wled_set = "WLED", 240128561Sphilip .brn_get = "GPLV", 241128561Sphilip .brn_set = "SPLV", 242128561Sphilip .lcd_get = "\\GL32", 243128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 244128561Sphilip }, 245128561Sphilip { 246128561Sphilip .name = "L3D", 247128561Sphilip .mled_set = "MLED", 248128561Sphilip .wled_set = "WLED", 249128561Sphilip .brn_get = "GPLV", 250128561Sphilip .brn_set = "SPLV", 251128561Sphilip .lcd_get = "\\BKLG", 252128561Sphilip .lcd_set = "\\Q10" 253128561Sphilip }, 254128561Sphilip { 255128561Sphilip .name = "L3H", 256128561Sphilip .mled_set = "MLED", 257128561Sphilip .wled_set = "WLED", 258128561Sphilip .brn_get = "GPLV", 259128561Sphilip .brn_set = "SPLV", 260128561Sphilip .lcd_get = "\\_SB.PCI0.PM.PBC", 261128561Sphilip .lcd_set = "EHK", 262128561Sphilip .disp_get = "\\_SB.INFB", 263128561Sphilip .disp_set = "SDSP" 264128561Sphilip }, 265128561Sphilip { 266137388Sphilip .name = "L4R", 267137388Sphilip .mled_set = "MLED", 268137388Sphilip .wled_set = "WLED", 269137388Sphilip .brn_get = "GPLV", 270137388Sphilip .brn_set = "SPLV", 271137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 272137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 273137388Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 274137388Sphilip .disp_set = "SDSP" 275137388Sphilip }, 276137388Sphilip { 277146024Sphilip .name = "L5x", 278146024Sphilip .mled_set = "MLED", 279146024Sphilip .tled_set = "TLED", 280146024Sphilip .lcd_get = "\\BAOF", 281146024Sphilip .lcd_set = "\\Q0D", 282146024Sphilip .brn_get = "GPLV", 283146024Sphilip .brn_set = "SPLV", 284146024Sphilip .disp_get = "\\INFB", 285146024Sphilip .disp_set = "SDSP" 286146024Sphilip }, 287146024Sphilip { 288128561Sphilip .name = "L8L" 289128561Sphilip /* Only has hotkeys, apparantly */ 290128561Sphilip }, 291128561Sphilip { 292128561Sphilip .name = "M1A", 293128561Sphilip .mled_set = "MLED", 294128561Sphilip .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 295128561Sphilip .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 296128561Sphilip .lcd_get = "\\PNOF", 297128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 298128561Sphilip }, 299128561Sphilip { 300128561Sphilip .name = "M2E", 301128561Sphilip .mled_set = "MLED", 302128561Sphilip .wled_set = "WLED", 303128561Sphilip .brn_get = "GPLV", 304128561Sphilip .brn_set = "SPLV", 305128561Sphilip .lcd_get = "\\GP06", 306128561Sphilip .lcd_set = "\\Q10" 307128561Sphilip }, 308128561Sphilip { 309137127Sphilip .name = "M6N", 310137127Sphilip .mled_set = "MLED", 311137127Sphilip .wled_set = "WLED", 312137127Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 313137127Sphilip .lcd_get = "\\_SB.BKLT", 314137127Sphilip .brn_set = "SPLV", 315137127Sphilip .brn_get = "GPLV", 316137127Sphilip .disp_set = "SDSP", 317137127Sphilip .disp_get = "\\SSTE" 318137127Sphilip }, 319137388Sphilip { 320137388Sphilip .name = "M6R", 321137388Sphilip .mled_set = "MLED", 322137388Sphilip .wled_set = "WLED", 323137388Sphilip .brn_get = "GPLV", 324137388Sphilip .brn_set = "SPLV", 325137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 326137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 327137388Sphilip .disp_get = "\\SSTE", 328137388Sphilip .disp_set = "SDSP" 329137388Sphilip }, 330146022Sphilip { 331146024Sphilip .name = "S1x", 332146024Sphilip .mled_set = "MLED", 333146022Sphilip .wled_set = "WLED", 334146024Sphilip .lcd_get = "\\PNOF", 335146024Sphilip .lcd_set = "\\_SB.PCI0.PX40.Q10", 336146022Sphilip .brn_get = "GPLV", 337146024Sphilip .brn_set = "SPLV" 338146022Sphilip }, 339146022Sphilip { 340146024Sphilip .name = "S2x", 341146022Sphilip .mled_set = "MLED", 342146024Sphilip .lcd_get = "\\BKLI", 343146024Sphilip .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 344146024Sphilip .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B", 345146024Sphilip .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A" 346146024Sphilip }, 347146024Sphilip { 348146024Sphilip .name = "V6V", 349146024Sphilip .bled_set = "BLED", 350146024Sphilip .tled_set = "TLED", 351146022Sphilip .wled_set = "WLED", 352146022Sphilip .lcd_get = "\\BKLT", 353146022Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 354146022Sphilip .brn_get = "GPLV", 355146022Sphilip .brn_set = "SPLV", 356146024Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 357146022Sphilip .disp_set = "SDSP" 358146022Sphilip }, 359157605Sphilip { 360157605Sphilip .name = "W5A", 361157605Sphilip .bled_set = "BLED", 362157605Sphilip .lcd_get = "\\BKLT", 363157605Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 364157605Sphilip .brn_get = "GPLV", 365157605Sphilip .brn_set = "SPLV", 366157605Sphilip .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", 367157605Sphilip .disp_set = "SDSP" 368157605Sphilip }, 369137245Sphilip 370137245Sphilip { .name = NULL } 371137245Sphilip}; 372137245Sphilip 373137245Sphilip/* 374137245Sphilip * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 375137245Sphilip * but they can't be probed quite the same way as Asus laptops. 376137245Sphilip */ 377137245Sphilipstatic struct acpi_asus_model acpi_samsung_models[] = { 378137127Sphilip { 379128561Sphilip .name = "P30", 380128561Sphilip .wled_set = "WLED", 381128561Sphilip .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 382128561Sphilip .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 383128561Sphilip .lcd_get = "\\BKLT", 384128561Sphilip .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 385128561Sphilip }, 386128561Sphilip 387128561Sphilip { .name = NULL } 388128561Sphilip}; 389128561Sphilip 390180062Srpaulostatic void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context); 391180062Srpaulo 392178178Srpaulo/* 393178178Srpaulo * EeePC have an Asus ASUS010 gadget interface, 394178178Srpaulo * but they can't be probed quite the same way as Asus laptops. 395178178Srpaulo */ 396178178Srpaulostatic struct acpi_asus_model acpi_eeepc_models[] = { 397178178Srpaulo { 398178178Srpaulo .name = "EEE", 399178178Srpaulo .brn_get = "\\_SB.ATKD.PBLG", 400180062Srpaulo .brn_set = "\\_SB.ATKD.PBLS", 401180062Srpaulo .cam_get = "\\_SB.ATKD.CAMG", 402180062Srpaulo .cam_set = "\\_SB.ATKD.CAMS", 403180062Srpaulo .crd_set = "\\_SB.ATKD.CRDS", 404180062Srpaulo .crd_get = "\\_SB.ATKD.CRDG", 405180062Srpaulo .n_func = acpi_asus_eeepc_notify 406178178Srpaulo }, 407178178Srpaulo 408178178Srpaulo { .name = NULL } 409178178Srpaulo}; 410178178Srpaulo 411143894Sphilipstatic struct { 412143894Sphilip char *name; 413143894Sphilip char *description; 414143894Sphilip int method; 415180062Srpaulo int flags; 416143894Sphilip} acpi_asus_sysctls[] = { 417143894Sphilip { 418143894Sphilip .name = "lcd_backlight", 419143894Sphilip .method = ACPI_ASUS_METHOD_LCD, 420180062Srpaulo .description = "state of the lcd backlight", 421180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY 422143894Sphilip }, 423143894Sphilip { 424143894Sphilip .name = "lcd_brightness", 425143894Sphilip .method = ACPI_ASUS_METHOD_BRN, 426180062Srpaulo .description = "brightness of the lcd panel", 427180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY 428143894Sphilip }, 429143894Sphilip { 430143894Sphilip .name = "video_output", 431143894Sphilip .method = ACPI_ASUS_METHOD_DISP, 432180062Srpaulo .description = "display output state", 433180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 434143894Sphilip }, 435180062Srpaulo { 436180062Srpaulo .name = "camera", 437180062Srpaulo .method = ACPI_ASUS_METHOD_CAMERA, 438180062Srpaulo .description = "internal camera state", 439180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 440180062Srpaulo }, 441180062Srpaulo { 442180062Srpaulo .name = "cardreader", 443180062Srpaulo .method = ACPI_ASUS_METHOD_CARDRD, 444180062Srpaulo .description = "internal card reader state", 445180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 446180062Srpaulo }, 447143894Sphilip 448143894Sphilip { .name = NULL } 449143894Sphilip}; 450143894Sphilip 451133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 452133628Snjl 453128561Sphilip/* Function prototypes */ 454128561Sphilipstatic int acpi_asus_probe(device_t dev); 455128561Sphilipstatic int acpi_asus_attach(device_t dev); 456128561Sphilipstatic int acpi_asus_detach(device_t dev); 457128561Sphilip 458133095Sphilipstatic void acpi_asus_led(struct acpi_asus_led *led, int state); 459144339Sphilipstatic void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 460128561Sphilip 461143894Sphilipstatic int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 462143894Sphilipstatic int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 463143894Sphilipstatic int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 464143894Sphilipstatic int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 465128561Sphilip 466128561Sphilipstatic void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 467128561Sphilip 468128561Sphilipstatic device_method_t acpi_asus_methods[] = { 469137631Sphilip DEVMETHOD(device_probe, acpi_asus_probe), 470128561Sphilip DEVMETHOD(device_attach, acpi_asus_attach), 471128561Sphilip DEVMETHOD(device_detach, acpi_asus_detach), 472128561Sphilip 473128561Sphilip { 0, 0 } 474128561Sphilip}; 475128561Sphilip 476128561Sphilipstatic driver_t acpi_asus_driver = { 477128561Sphilip "acpi_asus", 478128561Sphilip acpi_asus_methods, 479128561Sphilip sizeof(struct acpi_asus_softc) 480128561Sphilip}; 481128561Sphilip 482128561Sphilipstatic devclass_t acpi_asus_devclass; 483128561Sphilip 484128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 485128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 486128561Sphilip 487128561Sphilipstatic int 488128561Sphilipacpi_asus_probe(device_t dev) 489128561Sphilip{ 490128561Sphilip struct acpi_asus_model *model; 491128561Sphilip struct acpi_asus_softc *sc; 492128561Sphilip struct sbuf *sb; 493128561Sphilip ACPI_BUFFER Buf; 494128561Sphilip ACPI_OBJECT Arg, *Obj; 495128561Sphilip ACPI_OBJECT_LIST Args; 496178178Srpaulo static char *asus_ids[] = { "ATK0100", "ASUS010", NULL }; 497178178Srpaulo char *rstr; 498128561Sphilip 499128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 500128561Sphilip 501178178Srpaulo if (acpi_disabled("asus")) 502137632Sphilip return (ENXIO); 503178178Srpaulo rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids); 504178178Srpaulo if (rstr == NULL) { 505178178Srpaulo return (ENXIO); 506178178Srpaulo } 507137631Sphilip 508137632Sphilip sc = device_get_softc(dev); 509137632Sphilip sc->dev = dev; 510137632Sphilip sc->handle = acpi_get_handle(dev); 511128561Sphilip 512137632Sphilip Arg.Type = ACPI_TYPE_INTEGER; 513137632Sphilip Arg.Integer.Value = 0; 514128561Sphilip 515137632Sphilip Args.Count = 1; 516137632Sphilip Args.Pointer = &Arg; 517137631Sphilip 518137632Sphilip Buf.Pointer = NULL; 519137632Sphilip Buf.Length = ACPI_ALLOCATE_BUFFER; 520128561Sphilip 521137632Sphilip AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 522137632Sphilip Obj = Buf.Pointer; 523137245Sphilip 524137632Sphilip /* 525137632Sphilip * The Samsung P30 returns a null-pointer from INIT, we 526137632Sphilip * can identify it from the 'ODEM' string in the DSDT. 527137632Sphilip */ 528137632Sphilip if (Obj->String.Pointer == NULL) { 529137632Sphilip ACPI_STATUS status; 530137632Sphilip ACPI_TABLE_HEADER th; 531137245Sphilip 532167814Sjkim status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th); 533137632Sphilip if (ACPI_FAILURE(status)) { 534137632Sphilip device_printf(dev, "Unsupported (Samsung?) laptop\n"); 535137632Sphilip AcpiOsFree(Buf.Pointer); 536137632Sphilip return (ENXIO); 537137245Sphilip } 538137245Sphilip 539137632Sphilip if (strncmp("ODEM", th.OemTableId, 4) == 0) { 540137632Sphilip sc->model = &acpi_samsung_models[0]; 541137632Sphilip device_set_desc(dev, "Samsung P30 Laptop Extras"); 542137632Sphilip AcpiOsFree(Buf.Pointer); 543137632Sphilip return (0); 544137632Sphilip } 545178178Srpaulo 546178178Srpaulo /* if EeePC */ 547178231Srpaulo if (strncmp("ASUS010", rstr, 7) == 0) { 548178178Srpaulo sc->model = &acpi_eeepc_models[0]; 549178178Srpaulo device_set_desc(dev, "ASUS EeePC"); 550178178Srpaulo AcpiOsFree(Buf.Pointer); 551178178Srpaulo return (0); 552178178Srpaulo } 553137632Sphilip } 554137245Sphilip 555137632Sphilip sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 556137632Sphilip if (sb == NULL) 557137632Sphilip return (ENOMEM); 558128561Sphilip 559137632Sphilip /* 560137632Sphilip * Asus laptops are simply identified by name, easy! 561137632Sphilip */ 562146024Sphilip for (model = acpi_asus_models; model->name != NULL; model++) { 563137632Sphilip if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 564146024Sphilip 565146024Sphilipgood: 566146024Sphilip sbuf_printf(sb, "Asus %s Laptop Extras", 567146024Sphilip Obj->String.Pointer); 568137632Sphilip sbuf_finish(sb); 569128561Sphilip 570137632Sphilip sc->model = model; 571144076Spjd device_set_desc_copy(dev, sbuf_data(sb)); 572128561Sphilip 573137632Sphilip sbuf_delete(sb); 574137632Sphilip AcpiOsFree(Buf.Pointer); 575137632Sphilip return (0); 576137632Sphilip } 577146024Sphilip 578146024Sphilip /* 579146024Sphilip * Some models look exactly the same as other models, but have 580146024Sphilip * their own ids. If we spot these, set them up with the same 581146024Sphilip * details as the models they're like, possibly dealing with 582146024Sphilip * small differences. 583146024Sphilip * 584146024Sphilip * XXX: there must be a prettier way to do this! 585146024Sphilip */ 586146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 587146024Sphilip (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || 588146024Sphilip strncmp(Obj->String.Pointer, "S1N", 3) == 0)) 589146024Sphilip goto good; 590146024Sphilip else if (strncmp(model->name, "A1x", 3) == 0 && 591146024Sphilip strncmp(Obj->String.Pointer, "A1", 2) == 0) 592146024Sphilip goto good; 593146024Sphilip else if (strncmp(model->name, "A2x", 3) == 0 && 594146024Sphilip strncmp(Obj->String.Pointer, "A2", 2) == 0) 595146024Sphilip goto good; 596146024Sphilip else if (strncmp(model->name, "D1x", 3) == 0 && 597146024Sphilip strncmp(Obj->String.Pointer, "D1", 2) == 0) 598146024Sphilip goto good; 599146024Sphilip else if (strncmp(model->name, "L3H", 3) == 0 && 600146024Sphilip strncmp(Obj->String.Pointer, "L2E", 3) == 0) 601146024Sphilip goto good; 602146024Sphilip else if (strncmp(model->name, "L5x", 3) == 0 && 603146024Sphilip strncmp(Obj->String.Pointer, "L5", 2) == 0) 604146024Sphilip goto good; 605146024Sphilip else if (strncmp(model->name, "M2E", 3) == 0 && 606146024Sphilip (strncmp(Obj->String.Pointer, "M2", 2) == 0 || 607146024Sphilip strncmp(Obj->String.Pointer, "L4E", 3) == 0)) 608146024Sphilip goto good; 609146024Sphilip else if (strncmp(model->name, "S1x", 3) == 0 && 610146024Sphilip (strncmp(Obj->String.Pointer, "L8", 2) == 0 || 611146024Sphilip strncmp(Obj->String.Pointer, "S1", 2) == 0)) 612146024Sphilip goto good; 613146024Sphilip else if (strncmp(model->name, "S2x", 3) == 0 && 614146024Sphilip (strncmp(Obj->String.Pointer, "J1", 2) == 0 || 615146024Sphilip strncmp(Obj->String.Pointer, "S2", 2) == 0)) 616146024Sphilip goto good; 617128561Sphilip 618146024Sphilip /* L2B is like L3C but has no lcd_get method */ 619146024Sphilip else if (strncmp(model->name, "L3C", 3) == 0 && 620146024Sphilip strncmp(Obj->String.Pointer, "L2B", 3) == 0) { 621146024Sphilip model->lcd_get = NULL; 622146024Sphilip goto good; 623146024Sphilip } 624146024Sphilip 625146024Sphilip /* A3G is like M6R but with a different lcd_get method */ 626146024Sphilip else if (strncmp(model->name, "M6R", 3) == 0 && 627146024Sphilip strncmp(Obj->String.Pointer, "A3G", 3) == 0) { 628146024Sphilip model->lcd_get = "\\BLFG"; 629146024Sphilip goto good; 630146024Sphilip } 631146024Sphilip 632146024Sphilip /* M2N and W1N are like xxN with added WLED */ 633146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 634146024Sphilip (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || 635146024Sphilip strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { 636146024Sphilip model->wled_set = "WLED"; 637146024Sphilip goto good; 638146024Sphilip } 639146024Sphilip 640146024Sphilip /* M5N and S5N are like xxN without MLED */ 641146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 642146024Sphilip (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || 643146024Sphilip strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { 644146024Sphilip model->mled_set = NULL; 645146024Sphilip goto good; 646146024Sphilip } 647146024Sphilip } 648146024Sphilip 649137632Sphilip sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 650137632Sphilip sbuf_finish(sb); 651128561Sphilip 652137632Sphilip device_printf(dev, sbuf_data(sb)); 653137632Sphilip 654137632Sphilip sbuf_delete(sb); 655137632Sphilip AcpiOsFree(Buf.Pointer); 656137632Sphilip 657128561Sphilip return (ENXIO); 658128561Sphilip} 659128561Sphilip 660128561Sphilipstatic int 661128561Sphilipacpi_asus_attach(device_t dev) 662128561Sphilip{ 663128561Sphilip struct acpi_asus_softc *sc; 664128561Sphilip struct acpi_softc *acpi_sc; 665128561Sphilip 666128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 667128561Sphilip 668128561Sphilip sc = device_get_softc(dev); 669128561Sphilip acpi_sc = acpi_device_get_parent_softc(dev); 670128561Sphilip 671128561Sphilip /* Build sysctl tree */ 672128561Sphilip sysctl_ctx_init(&sc->sysctl_ctx); 673128561Sphilip sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 674128561Sphilip SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 675128561Sphilip OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 676128561Sphilip 677143894Sphilip /* Hook up nodes */ 678143894Sphilip for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 679143894Sphilip if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 680143894Sphilip continue; 681143894Sphilip 682143894Sphilip SYSCTL_ADD_PROC(&sc->sysctl_ctx, 683143894Sphilip SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 684143894Sphilip acpi_asus_sysctls[i].name, 685180062Srpaulo acpi_asus_sysctls[i].flags, 686143894Sphilip sc, i, acpi_asus_sysctl, "I", 687143894Sphilip acpi_asus_sysctls[i].description); 688143894Sphilip } 689143894Sphilip 690128561Sphilip /* Attach leds */ 691146022Sphilip if (sc->model->bled_set) { 692146022Sphilip sc->s_bled.busy = 0; 693146022Sphilip sc->s_bled.sc = sc; 694146022Sphilip sc->s_bled.type = ACPI_ASUS_LED_BLED; 695146022Sphilip sc->s_bled.cdev = 696178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_bled, 697178069Sjkim "bled", 1); 698146022Sphilip } 699146022Sphilip 700178069Sjkim if (sc->model->dled_set) { 701178069Sjkim sc->s_dled.busy = 0; 702178069Sjkim sc->s_dled.sc = sc; 703178069Sjkim sc->s_dled.type = ACPI_ASUS_LED_DLED; 704178069Sjkim sc->s_dled.cdev = 705178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled"); 706178069Sjkim } 707178069Sjkim 708178069Sjkim if (sc->model->gled_set) { 709178069Sjkim sc->s_gled.busy = 0; 710178069Sjkim sc->s_gled.sc = sc; 711178069Sjkim sc->s_gled.type = ACPI_ASUS_LED_GLED; 712178069Sjkim sc->s_gled.cdev = 713178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled"); 714178069Sjkim } 715178069Sjkim 716133095Sphilip if (sc->model->mled_set) { 717144339Sphilip sc->s_mled.busy = 0; 718144339Sphilip sc->s_mled.sc = sc; 719133095Sphilip sc->s_mled.type = ACPI_ASUS_LED_MLED; 720133095Sphilip sc->s_mled.cdev = 721133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 722133095Sphilip } 723128561Sphilip 724133095Sphilip if (sc->model->tled_set) { 725144339Sphilip sc->s_tled.busy = 0; 726144339Sphilip sc->s_tled.sc = sc; 727133095Sphilip sc->s_tled.type = ACPI_ASUS_LED_TLED; 728133095Sphilip sc->s_tled.cdev = 729178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_tled, 730178069Sjkim "tled", 1); 731133095Sphilip } 732128561Sphilip 733133095Sphilip if (sc->model->wled_set) { 734144339Sphilip sc->s_wled.busy = 0; 735144339Sphilip sc->s_wled.sc = sc; 736133095Sphilip sc->s_wled.type = ACPI_ASUS_LED_WLED; 737133095Sphilip sc->s_wled.cdev = 738178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_wled, 739178069Sjkim "wled", 1); 740133095Sphilip } 741128561Sphilip 742128561Sphilip /* Activate hotkeys */ 743128561Sphilip AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 744128561Sphilip 745128561Sphilip /* Handle notifies */ 746180062Srpaulo if (sc->model->n_func == NULL) 747180062Srpaulo sc->model->n_func = acpi_asus_notify; 748180062Srpaulo 749132610Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 750180062Srpaulo sc->model->n_func, dev); 751137631Sphilip 752128561Sphilip return (0); 753128561Sphilip} 754128561Sphilip 755128561Sphilipstatic int 756128561Sphilipacpi_asus_detach(device_t dev) 757128561Sphilip{ 758128561Sphilip struct acpi_asus_softc *sc; 759137631Sphilip 760128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 761128561Sphilip 762128561Sphilip sc = device_get_softc(dev); 763128561Sphilip 764128561Sphilip /* Turn the lights off */ 765146022Sphilip if (sc->model->bled_set) 766146022Sphilip led_destroy(sc->s_bled.cdev); 767146022Sphilip 768178069Sjkim if (sc->model->dled_set) 769178069Sjkim led_destroy(sc->s_dled.cdev); 770178069Sjkim 771178069Sjkim if (sc->model->gled_set) 772178069Sjkim led_destroy(sc->s_gled.cdev); 773178069Sjkim 774128561Sphilip if (sc->model->mled_set) 775133095Sphilip led_destroy(sc->s_mled.cdev); 776128561Sphilip 777128561Sphilip if (sc->model->tled_set) 778133095Sphilip led_destroy(sc->s_tled.cdev); 779128561Sphilip 780128561Sphilip if (sc->model->wled_set) 781133095Sphilip led_destroy(sc->s_wled.cdev); 782128561Sphilip 783128561Sphilip /* Remove notify handler */ 784132610Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 785132610Snjl acpi_asus_notify); 786128561Sphilip 787128561Sphilip /* Free sysctl tree */ 788128561Sphilip sysctl_ctx_free(&sc->sysctl_ctx); 789128561Sphilip 790128561Sphilip return (0); 791128561Sphilip} 792128561Sphilip 793128561Sphilipstatic void 794144339Sphilipacpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 795128561Sphilip{ 796128561Sphilip struct acpi_asus_softc *sc; 797133095Sphilip char *method; 798144339Sphilip int state; 799144339Sphilip 800128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 801128561Sphilip 802144339Sphilip sc = led->sc; 803128561Sphilip 804133095Sphilip switch (led->type) { 805146022Sphilip case ACPI_ASUS_LED_BLED: 806146022Sphilip method = sc->model->bled_set; 807146022Sphilip state = led->state; 808146022Sphilip break; 809178069Sjkim case ACPI_ASUS_LED_DLED: 810178069Sjkim method = sc->model->dled_set; 811178069Sjkim state = led->state; 812178069Sjkim break; 813178069Sjkim case ACPI_ASUS_LED_GLED: 814178069Sjkim method = sc->model->gled_set; 815178069Sjkim state = led->state + 1; /* 1: off, 2: on */ 816178069Sjkim break; 817143894Sphilip case ACPI_ASUS_LED_MLED: 818143894Sphilip method = sc->model->mled_set; 819178069Sjkim state = !led->state; /* inverted */ 820143894Sphilip break; 821143894Sphilip case ACPI_ASUS_LED_TLED: 822143894Sphilip method = sc->model->tled_set; 823144339Sphilip state = led->state; 824143894Sphilip break; 825143894Sphilip case ACPI_ASUS_LED_WLED: 826143894Sphilip method = sc->model->wled_set; 827144339Sphilip state = led->state; 828143894Sphilip break; 829143894Sphilip default: 830143894Sphilip printf("acpi_asus_led: invalid LED type %d\n", 831143894Sphilip (int)led->type); 832143894Sphilip return; 833133095Sphilip } 834128561Sphilip 835133095Sphilip acpi_SetInteger(sc->handle, method, state); 836144339Sphilip led->busy = 0; 837128561Sphilip} 838144339Sphilip 839144339Sphilipstatic void 840144339Sphilipacpi_asus_led(struct acpi_asus_led *led, int state) 841144339Sphilip{ 842128561Sphilip 843144339Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 844144339Sphilip 845144339Sphilip if (led->busy) 846144339Sphilip return; 847144339Sphilip 848144339Sphilip led->busy = 1; 849144339Sphilip led->state = state; 850144339Sphilip 851167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led); 852144339Sphilip} 853144339Sphilip 854128561Sphilipstatic int 855143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 856128561Sphilip{ 857128561Sphilip struct acpi_asus_softc *sc; 858143894Sphilip int arg; 859143894Sphilip int error = 0; 860143894Sphilip int function; 861143894Sphilip int method; 862143894Sphilip 863128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 864128561Sphilip 865128561Sphilip sc = (struct acpi_asus_softc *)oidp->oid_arg1; 866143894Sphilip function = oidp->oid_arg2; 867143894Sphilip method = acpi_asus_sysctls[function].method; 868143894Sphilip 869133628Snjl ACPI_SERIAL_BEGIN(asus); 870143894Sphilip arg = acpi_asus_sysctl_get(sc, method); 871143894Sphilip error = sysctl_handle_int(oidp, &arg, 0, req); 872128561Sphilip 873128561Sphilip /* Sanity check */ 874143894Sphilip if (error != 0 || req->newptr == NULL) 875133092Snjl goto out; 876128561Sphilip 877143894Sphilip /* Update */ 878143894Sphilip error = acpi_asus_sysctl_set(sc, method, arg); 879128561Sphilip 880143894Sphilipout: 881143894Sphilip ACPI_SERIAL_END(asus); 882143894Sphilip return (error); 883143894Sphilip} 884128561Sphilip 885143894Sphilipstatic int 886143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 887143894Sphilip{ 888143894Sphilip int val = 0; 889128561Sphilip 890143894Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 891143894Sphilip ACPI_SERIAL_ASSERT(asus); 892143894Sphilip 893143894Sphilip switch (method) { 894143894Sphilip case ACPI_ASUS_METHOD_BRN: 895143894Sphilip val = sc->s_brn; 896143894Sphilip break; 897143894Sphilip case ACPI_ASUS_METHOD_DISP: 898143894Sphilip val = sc->s_disp; 899143894Sphilip break; 900143894Sphilip case ACPI_ASUS_METHOD_LCD: 901143894Sphilip val = sc->s_lcd; 902143894Sphilip break; 903180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 904180062Srpaulo val = sc->s_cam; 905180062Srpaulo break; 906180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 907180062Srpaulo val = sc->s_crd; 908180062Srpaulo break; 909128561Sphilip } 910128561Sphilip 911143894Sphilip return (val); 912128561Sphilip} 913128561Sphilip 914128561Sphilipstatic int 915143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 916128561Sphilip{ 917143937Sphilip ACPI_STATUS status = AE_OK; 918128561Sphilip 919128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 920143894Sphilip ACPI_SERIAL_ASSERT(asus); 921128561Sphilip 922143894Sphilip switch (method) { 923143894Sphilip case ACPI_ASUS_METHOD_BRN: 924143894Sphilip if (arg < 0 || arg > 15) 925143894Sphilip return (EINVAL); 926128561Sphilip 927143894Sphilip if (sc->model->brn_set) 928143894Sphilip status = acpi_SetInteger(sc->handle, 929143894Sphilip sc->model->brn_set, arg); 930143894Sphilip else { 931143894Sphilip while (arg != 0) { 932143894Sphilip status = AcpiEvaluateObject(sc->handle, 933143894Sphilip (arg > 0) ? sc->model->brn_up : 934143894Sphilip sc->model->brn_dn, NULL, NULL); 935143894Sphilip (arg > 0) ? arg-- : arg++; 936143894Sphilip } 937143894Sphilip } 938128561Sphilip 939143894Sphilip if (ACPI_SUCCESS(status)) 940143894Sphilip sc->s_brn = arg; 941128561Sphilip 942143894Sphilip break; 943143894Sphilip case ACPI_ASUS_METHOD_DISP: 944143894Sphilip if (arg < 0 || arg > 7) 945143894Sphilip return (EINVAL); 946128561Sphilip 947143894Sphilip status = acpi_SetInteger(sc->handle, 948143894Sphilip sc->model->disp_set, arg); 949128561Sphilip 950143894Sphilip if (ACPI_SUCCESS(status)) 951143894Sphilip sc->s_disp = arg; 952128561Sphilip 953143894Sphilip break; 954143894Sphilip case ACPI_ASUS_METHOD_LCD: 955143894Sphilip if (arg < 0 || arg > 1) 956143894Sphilip return (EINVAL); 957143894Sphilip 958143894Sphilip if (strncmp(sc->model->name, "L3H", 3) != 0) 959143894Sphilip status = AcpiEvaluateObject(sc->handle, 960143894Sphilip sc->model->lcd_set, NULL, NULL); 961143894Sphilip else 962143894Sphilip status = acpi_SetInteger(sc->handle, 963143894Sphilip sc->model->lcd_set, 0x7); 964143894Sphilip 965143894Sphilip if (ACPI_SUCCESS(status)) 966143894Sphilip sc->s_lcd = arg; 967143894Sphilip 968143894Sphilip break; 969180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 970180062Srpaulo if (arg < 0 || arg > 1) 971180062Srpaulo return (EINVAL); 972180062Srpaulo 973180062Srpaulo status = AcpiEvaluateObject(sc->handle, 974180062Srpaulo sc->model->cam_set, NULL, NULL); 975180062Srpaulo 976180062Srpaulo if (ACPI_SUCCESS(status)) 977180062Srpaulo sc->s_cam = arg; 978180062Srpaulo break; 979180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 980180062Srpaulo if (arg < 0 || arg > 1) 981180062Srpaulo return (EINVAL); 982180062Srpaulo 983180062Srpaulo status = AcpiEvaluateObject(sc->handle, 984180062Srpaulo sc->model->crd_set, NULL, NULL); 985180062Srpaulo 986180062Srpaulo if (ACPI_SUCCESS(status)) 987180062Srpaulo sc->s_crd = arg; 988180062Srpaulo break; 989143894Sphilip } 990143894Sphilip 991143894Sphilip return (0); 992128561Sphilip} 993128561Sphilip 994128561Sphilipstatic int 995143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 996128561Sphilip{ 997143894Sphilip ACPI_STATUS status; 998128561Sphilip 999143894Sphilip switch (method) { 1000143894Sphilip case ACPI_ASUS_METHOD_BRN: 1001143894Sphilip if (sc->model->brn_get) { 1002143894Sphilip /* GPLV/SPLV models */ 1003143894Sphilip status = acpi_GetInteger(sc->handle, 1004143894Sphilip sc->model->brn_get, &sc->s_brn); 1005143894Sphilip if (ACPI_SUCCESS(status)) 1006143894Sphilip return (TRUE); 1007143894Sphilip } else if (sc->model->brn_up) { 1008143894Sphilip /* Relative models */ 1009143894Sphilip status = AcpiEvaluateObject(sc->handle, 1010143894Sphilip sc->model->brn_up, NULL, NULL); 1011143894Sphilip if (ACPI_FAILURE(status)) 1012143894Sphilip return (FALSE); 1013128561Sphilip 1014143894Sphilip status = AcpiEvaluateObject(sc->handle, 1015143894Sphilip sc->model->brn_dn, NULL, NULL); 1016143894Sphilip if (ACPI_FAILURE(status)) 1017143894Sphilip return (FALSE); 1018128561Sphilip 1019143894Sphilip return (TRUE); 1020143894Sphilip } 1021143894Sphilip return (FALSE); 1022143894Sphilip case ACPI_ASUS_METHOD_DISP: 1023143894Sphilip if (sc->model->disp_get) { 1024143894Sphilip status = acpi_GetInteger(sc->handle, 1025143894Sphilip sc->model->disp_get, &sc->s_disp); 1026143894Sphilip if (ACPI_SUCCESS(status)) 1027143894Sphilip return (TRUE); 1028143894Sphilip } 1029143894Sphilip return (FALSE); 1030143894Sphilip case ACPI_ASUS_METHOD_LCD: 1031178069Sjkim if (sc->model->lcd_get) { 1032178069Sjkim if (strncmp(sc->model->name, "G2K", 3) == 0) { 1033178069Sjkim ACPI_BUFFER Buf; 1034178069Sjkim ACPI_OBJECT Arg, Obj; 1035178069Sjkim ACPI_OBJECT_LIST Args; 1036128561Sphilip 1037178069Sjkim Arg.Type = ACPI_TYPE_INTEGER; 1038178069Sjkim Arg.Integer.Value = 0x11; 1039178069Sjkim Args.Count = 1; 1040178069Sjkim Args.Pointer = &Arg; 1041178069Sjkim Buf.Length = sizeof(Obj); 1042178069Sjkim Buf.Pointer = &Obj; 1043128561Sphilip 1044178069Sjkim status = AcpiEvaluateObject(sc->handle, 1045178069Sjkim sc->model->lcd_get, &Args, &Buf); 1046178069Sjkim if (ACPI_SUCCESS(status) && 1047178069Sjkim Obj.Type == ACPI_TYPE_INTEGER) { 1048178069Sjkim sc->s_lcd = Obj.Integer.Value; 1049178069Sjkim return (TRUE); 1050178069Sjkim } 1051178069Sjkim } else if (strncmp(sc->model->name, "L3H", 3) == 0) { 1052178069Sjkim ACPI_BUFFER Buf; 1053178069Sjkim ACPI_OBJECT Arg[2], Obj; 1054178069Sjkim ACPI_OBJECT_LIST Args; 1055128561Sphilip 1056178069Sjkim /* L3H is a bit special */ 1057178069Sjkim Arg[0].Type = ACPI_TYPE_INTEGER; 1058178069Sjkim Arg[0].Integer.Value = 0x02; 1059178069Sjkim Arg[1].Type = ACPI_TYPE_INTEGER; 1060178069Sjkim Arg[1].Integer.Value = 0x03; 1061128561Sphilip 1062178069Sjkim Args.Count = 2; 1063178069Sjkim Args.Pointer = Arg; 1064178069Sjkim 1065178069Sjkim Buf.Length = sizeof(Obj); 1066178069Sjkim Buf.Pointer = &Obj; 1067178069Sjkim 1068178069Sjkim status = AcpiEvaluateObject(sc->handle, 1069178069Sjkim sc->model->lcd_get, &Args, &Buf); 1070178069Sjkim if (ACPI_SUCCESS(status) && 1071178069Sjkim Obj.Type == ACPI_TYPE_INTEGER) { 1072178069Sjkim sc->s_lcd = Obj.Integer.Value >> 8; 1073178069Sjkim return (TRUE); 1074178069Sjkim } 1075178069Sjkim } else { 1076178069Sjkim status = acpi_GetInteger(sc->handle, 1077178069Sjkim sc->model->lcd_get, &sc->s_lcd); 1078178069Sjkim if (ACPI_SUCCESS(status)) 1079178069Sjkim return (TRUE); 1080143894Sphilip } 1081143894Sphilip } 1082143894Sphilip return (FALSE); 1083180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1084180062Srpaulo if (sc->model->cam_get) { 1085180062Srpaulo status = acpi_GetInteger(sc->handle, 1086180062Srpaulo sc->model->cam_get, &sc->s_cam); 1087180062Srpaulo if (ACPI_SUCCESS(status)) 1088180062Srpaulo return (TRUE); 1089180062Srpaulo } 1090180062Srpaulo return (FALSE); 1091180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1092180062Srpaulo if (sc->model->crd_get) { 1093180062Srpaulo status = acpi_GetInteger(sc->handle, 1094180062Srpaulo sc->model->crd_get, &sc->s_crd); 1095180062Srpaulo if (ACPI_SUCCESS(status)) 1096180062Srpaulo return (TRUE); 1097180062Srpaulo } 1098180062Srpaulo return (FALSE); 1099143894Sphilip } 1100143894Sphilip return (FALSE); 1101128561Sphilip} 1102128561Sphilip 1103128561Sphilipstatic void 1104128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1105128561Sphilip{ 1106128561Sphilip struct acpi_asus_softc *sc; 1107128561Sphilip struct acpi_softc *acpi_sc; 1108128561Sphilip 1109128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1110128561Sphilip 1111128561Sphilip sc = device_get_softc((device_t)context); 1112128561Sphilip acpi_sc = acpi_device_get_parent_softc(sc->dev); 1113128561Sphilip 1114133628Snjl ACPI_SERIAL_BEGIN(asus); 1115128561Sphilip if ((notify & ~0x10) <= 15) { 1116132610Snjl sc->s_brn = notify & ~0x10; 1117128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1118128561Sphilip } else if ((notify & ~0x20) <= 15) { 1119132610Snjl sc->s_brn = notify & ~0x20; 1120128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1121128561Sphilip } else if (notify == 0x33) { 1122128561Sphilip sc->s_lcd = 1; 1123128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 1124128561Sphilip } else if (notify == 0x34) { 1125128561Sphilip sc->s_lcd = 0; 1126128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 1127128561Sphilip } else { 1128128561Sphilip /* Notify devd(8) */ 1129128561Sphilip acpi_UserNotify("ASUS", h, notify); 1130128561Sphilip } 1131133628Snjl ACPI_SERIAL_END(asus); 1132128561Sphilip} 1133180062Srpaulo 1134180062Srpaulostatic void 1135180062Srpauloacpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1136180062Srpaulo{ 1137180062Srpaulo struct acpi_asus_softc *sc; 1138180062Srpaulo struct acpi_softc *acpi_sc; 1139180062Srpaulo 1140180062Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1141180062Srpaulo 1142180062Srpaulo sc = device_get_softc((device_t)context); 1143180062Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1144180062Srpaulo 1145180062Srpaulo ACPI_SERIAL_BEGIN(asus); 1146180062Srpaulo if ((notify & ~0x20) <= 15) { 1147180062Srpaulo sc->s_brn = notify & ~0x20; 1148180062Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, 1149180062Srpaulo "Brightness increased/decreased\n"); 1150180062Srpaulo } else { 1151180062Srpaulo /* Notify devd(8) */ 1152180062Srpaulo acpi_UserNotify("ASUS-Eee", h, notify); 1153180062Srpaulo } 1154180062Srpaulo ACPI_SERIAL_END(asus); 1155180062Srpaulo} 1156