acpi_asus.c revision 186529
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 186529 2008-12-27 20:48:11Z stas $"); 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 60180268Srpaulo#define ACPI_ASUS_METHOD_WLAN 6 61143894Sphilip 62138825Snjl#define _COMPONENT ACPI_OEM 63128561SphilipACPI_MODULE_NAME("ASUS") 64128561Sphilip 65128561Sphilipstruct acpi_asus_model { 66128561Sphilip char *name; 67128561Sphilip 68146022Sphilip char *bled_set; 69178069Sjkim char *dled_set; 70178069Sjkim char *gled_set; 71128561Sphilip char *mled_set; 72128561Sphilip char *tled_set; 73128561Sphilip char *wled_set; 74128561Sphilip 75128561Sphilip char *brn_get; 76128561Sphilip char *brn_set; 77128561Sphilip char *brn_up; 78128561Sphilip char *brn_dn; 79128561Sphilip 80128561Sphilip char *lcd_get; 81128561Sphilip char *lcd_set; 82128561Sphilip 83128561Sphilip char *disp_get; 84128561Sphilip char *disp_set; 85180062Srpaulo 86180062Srpaulo char *cam_get; 87180062Srpaulo char *cam_set; 88180062Srpaulo 89180062Srpaulo char *crd_get; 90180062Srpaulo char *crd_set; 91180062Srpaulo 92180268Srpaulo char *wlan_get; 93180268Srpaulo char *wlan_set; 94180268Srpaulo 95180062Srpaulo void (*n_func)(ACPI_HANDLE, UINT32, void *); 96184625Srpaulo 97184625Srpaulo char *lcdd; 98184625Srpaulo void (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *); 99128561Sphilip}; 100128561Sphilip 101133095Sphilipstruct acpi_asus_led { 102144339Sphilip struct acpi_asus_softc *sc; 103133095Sphilip struct cdev *cdev; 104144339Sphilip int busy; 105144339Sphilip int state; 106133095Sphilip enum { 107146022Sphilip ACPI_ASUS_LED_BLED, 108178069Sjkim ACPI_ASUS_LED_DLED, 109178069Sjkim ACPI_ASUS_LED_GLED, 110133095Sphilip ACPI_ASUS_LED_MLED, 111133095Sphilip ACPI_ASUS_LED_TLED, 112133095Sphilip ACPI_ASUS_LED_WLED, 113133095Sphilip } type; 114133095Sphilip}; 115133095Sphilip 116128561Sphilipstruct acpi_asus_softc { 117128561Sphilip device_t dev; 118128561Sphilip ACPI_HANDLE handle; 119184625Srpaulo ACPI_HANDLE lcdd_handle; 120128561Sphilip 121128561Sphilip struct acpi_asus_model *model; 122128561Sphilip struct sysctl_ctx_list sysctl_ctx; 123128561Sphilip struct sysctl_oid *sysctl_tree; 124128561Sphilip 125146022Sphilip struct acpi_asus_led s_bled; 126178069Sjkim struct acpi_asus_led s_dled; 127178069Sjkim struct acpi_asus_led s_gled; 128133095Sphilip struct acpi_asus_led s_mled; 129133095Sphilip struct acpi_asus_led s_tled; 130133095Sphilip struct acpi_asus_led s_wled; 131128561Sphilip 132128561Sphilip int s_brn; 133128561Sphilip int s_disp; 134128561Sphilip int s_lcd; 135180062Srpaulo int s_cam; 136180062Srpaulo int s_crd; 137180268Srpaulo int s_wlan; 138128561Sphilip}; 139128561Sphilip 140184625Srpaulostatic void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, 141184625Srpaulo void *context); 142184625Srpaulo 143137245Sphilip/* 144137245Sphilip * We can identify Asus laptops from the string they return 145137245Sphilip * as a result of calling the ATK0100 'INIT' method. 146137245Sphilip */ 147128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = { 148128561Sphilip { 149146024Sphilip .name = "xxN", 150146024Sphilip .mled_set = "MLED", 151146024Sphilip .wled_set = "WLED", 152146024Sphilip .lcd_get = "\\BKLT", 153146024Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 154146024Sphilip .brn_get = "GPLV", 155146024Sphilip .brn_set = "SPLV", 156146024Sphilip .disp_get = "\\ADVG", 157146024Sphilip .disp_set = "SDSP" 158146024Sphilip }, 159146024Sphilip { 160146024Sphilip .name = "A1x", 161146024Sphilip .mled_set = "MLED", 162146024Sphilip .lcd_get = "\\BKLI", 163146024Sphilip .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 164146024Sphilip .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E", 165146024Sphilip .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F" 166146024Sphilip }, 167146024Sphilip { 168146024Sphilip .name = "A2x", 169146024Sphilip .mled_set = "MLED", 170146024Sphilip .wled_set = "WLED", 171146024Sphilip .lcd_get = "\\BAOF", 172146024Sphilip .lcd_set = "\\Q10", 173146024Sphilip .brn_get = "GPLV", 174146024Sphilip .brn_set = "SPLV", 175146024Sphilip .disp_get = "\\INFB", 176146024Sphilip .disp_set = "SDSP" 177146024Sphilip }, 178146024Sphilip { 179170216Sphilip .name = "A3N", 180170216Sphilip .mled_set = "MLED", 181170216Sphilip .bled_set = "BLED", 182170216Sphilip .wled_set = "WLED", 183170216Sphilip .lcd_get = NULL, 184170216Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 185170216Sphilip .brn_set = "SPLV", 186170216Sphilip .brn_get = "SDSP", 187170216Sphilip .disp_set = "SDSP", 188170216Sphilip .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD" 189170216Sphilip }, 190170216Sphilip { 191155022Sphilip .name = "A4D", 192155022Sphilip .mled_set = "MLED", 193155022Sphilip .brn_up = "\\_SB_.PCI0.SBRG.EC0._Q0E", 194155022Sphilip .brn_dn = "\\_SB_.PCI0.SBRG.EC0._Q0F", 195155022Sphilip .brn_get = "GPLV", 196155022Sphilip .brn_set = "SPLV", 197155022Sphilip#ifdef notyet 198155022Sphilip .disp_get = "\\_SB_.PCI0.SBRG.EC0._Q10", 199155022Sphilip .disp_set = "\\_SB_.PCI0.SBRG.EC0._Q11" 200155022Sphilip#endif 201155022Sphilip }, 202155022Sphilip { 203155021Sphilip .name = "A6V", 204155021Sphilip .bled_set = "BLED", 205155021Sphilip .mled_set = "MLED", 206155021Sphilip .wled_set = "WLED", 207155021Sphilip .lcd_get = NULL, 208155021Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 209155021Sphilip .brn_get = "GPLV", 210155021Sphilip .brn_set = "SPLV", 211155021Sphilip .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", 212155021Sphilip .disp_set = "SDSP" 213155021Sphilip }, 214155021Sphilip { 215184625Srpaulo .name = "A8SR", 216184625Srpaulo .bled_set = "BLED", 217184625Srpaulo .mled_set = "MLED", 218184625Srpaulo .wled_set = "WLED", 219184625Srpaulo .lcd_get = NULL, 220184625Srpaulo .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 221184625Srpaulo .brn_get = "GPLV", 222184625Srpaulo .brn_set = "SPLV", 223184625Srpaulo .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 224184625Srpaulo .disp_set = "SDSP", 225184625Srpaulo .lcdd = "\\_SB.PCI0.P0P1.VGA.LCDD", 226184625Srpaulo .lcdd_n_func = acpi_asus_lcdd_notify 227184625Srpaulo }, 228184625Srpaulo { 229146024Sphilip .name = "D1x", 230146024Sphilip .mled_set = "MLED", 231146024Sphilip .lcd_get = "\\GP11", 232146024Sphilip .lcd_set = "\\Q0D", 233146024Sphilip .brn_up = "\\Q0C", 234146024Sphilip .brn_dn = "\\Q0B", 235146024Sphilip .disp_get = "\\INFB", 236146024Sphilip .disp_set = "SDSP" 237146024Sphilip }, 238146024Sphilip { 239178069Sjkim .name = "G2K", 240178069Sjkim .bled_set = "BLED", 241178069Sjkim .dled_set = "DLED", 242178069Sjkim .gled_set = "GLED", 243178069Sjkim .mled_set = "MLED", 244178069Sjkim .tled_set = "TLED", 245178069Sjkim .wled_set = "WLED", 246178069Sjkim .brn_get = "GPLV", 247178069Sjkim .brn_set = "SPLV", 248178069Sjkim .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN", 249178069Sjkim .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 250178069Sjkim .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD", 251178069Sjkim .disp_set = "SDSP", 252178069Sjkim }, 253178069Sjkim { 254128561Sphilip .name = "L2D", 255128561Sphilip .mled_set = "MLED", 256128561Sphilip .wled_set = "WLED", 257128561Sphilip .brn_up = "\\Q0E", 258128561Sphilip .brn_dn = "\\Q0F", 259128561Sphilip .lcd_get = "\\SGP0", 260128561Sphilip .lcd_set = "\\Q10" 261128561Sphilip }, 262128561Sphilip { 263128561Sphilip .name = "L3C", 264128561Sphilip .mled_set = "MLED", 265128561Sphilip .wled_set = "WLED", 266128561Sphilip .brn_get = "GPLV", 267128561Sphilip .brn_set = "SPLV", 268128561Sphilip .lcd_get = "\\GL32", 269128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 270128561Sphilip }, 271128561Sphilip { 272128561Sphilip .name = "L3D", 273128561Sphilip .mled_set = "MLED", 274128561Sphilip .wled_set = "WLED", 275128561Sphilip .brn_get = "GPLV", 276128561Sphilip .brn_set = "SPLV", 277128561Sphilip .lcd_get = "\\BKLG", 278128561Sphilip .lcd_set = "\\Q10" 279128561Sphilip }, 280128561Sphilip { 281128561Sphilip .name = "L3H", 282128561Sphilip .mled_set = "MLED", 283128561Sphilip .wled_set = "WLED", 284128561Sphilip .brn_get = "GPLV", 285128561Sphilip .brn_set = "SPLV", 286128561Sphilip .lcd_get = "\\_SB.PCI0.PM.PBC", 287128561Sphilip .lcd_set = "EHK", 288128561Sphilip .disp_get = "\\_SB.INFB", 289128561Sphilip .disp_set = "SDSP" 290128561Sphilip }, 291128561Sphilip { 292137388Sphilip .name = "L4R", 293137388Sphilip .mled_set = "MLED", 294137388Sphilip .wled_set = "WLED", 295137388Sphilip .brn_get = "GPLV", 296137388Sphilip .brn_set = "SPLV", 297137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 298137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 299137388Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 300137388Sphilip .disp_set = "SDSP" 301137388Sphilip }, 302137388Sphilip { 303146024Sphilip .name = "L5x", 304146024Sphilip .mled_set = "MLED", 305146024Sphilip .tled_set = "TLED", 306146024Sphilip .lcd_get = "\\BAOF", 307146024Sphilip .lcd_set = "\\Q0D", 308146024Sphilip .brn_get = "GPLV", 309146024Sphilip .brn_set = "SPLV", 310146024Sphilip .disp_get = "\\INFB", 311146024Sphilip .disp_set = "SDSP" 312146024Sphilip }, 313146024Sphilip { 314128561Sphilip .name = "L8L" 315181885Srpaulo /* Only has hotkeys, apparently */ 316128561Sphilip }, 317128561Sphilip { 318128561Sphilip .name = "M1A", 319128561Sphilip .mled_set = "MLED", 320128561Sphilip .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 321128561Sphilip .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 322128561Sphilip .lcd_get = "\\PNOF", 323128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 324128561Sphilip }, 325128561Sphilip { 326128561Sphilip .name = "M2E", 327128561Sphilip .mled_set = "MLED", 328128561Sphilip .wled_set = "WLED", 329128561Sphilip .brn_get = "GPLV", 330128561Sphilip .brn_set = "SPLV", 331128561Sphilip .lcd_get = "\\GP06", 332128561Sphilip .lcd_set = "\\Q10" 333128561Sphilip }, 334128561Sphilip { 335137127Sphilip .name = "M6N", 336137127Sphilip .mled_set = "MLED", 337137127Sphilip .wled_set = "WLED", 338137127Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 339137127Sphilip .lcd_get = "\\_SB.BKLT", 340137127Sphilip .brn_set = "SPLV", 341137127Sphilip .brn_get = "GPLV", 342137127Sphilip .disp_set = "SDSP", 343137127Sphilip .disp_get = "\\SSTE" 344137127Sphilip }, 345137388Sphilip { 346137388Sphilip .name = "M6R", 347137388Sphilip .mled_set = "MLED", 348137388Sphilip .wled_set = "WLED", 349137388Sphilip .brn_get = "GPLV", 350137388Sphilip .brn_set = "SPLV", 351137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 352137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 353137388Sphilip .disp_get = "\\SSTE", 354137388Sphilip .disp_set = "SDSP" 355137388Sphilip }, 356146022Sphilip { 357146024Sphilip .name = "S1x", 358146024Sphilip .mled_set = "MLED", 359146022Sphilip .wled_set = "WLED", 360146024Sphilip .lcd_get = "\\PNOF", 361146024Sphilip .lcd_set = "\\_SB.PCI0.PX40.Q10", 362146022Sphilip .brn_get = "GPLV", 363146024Sphilip .brn_set = "SPLV" 364146022Sphilip }, 365146022Sphilip { 366146024Sphilip .name = "S2x", 367146022Sphilip .mled_set = "MLED", 368146024Sphilip .lcd_get = "\\BKLI", 369146024Sphilip .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 370146024Sphilip .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B", 371146024Sphilip .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A" 372146024Sphilip }, 373146024Sphilip { 374146024Sphilip .name = "V6V", 375146024Sphilip .bled_set = "BLED", 376146024Sphilip .tled_set = "TLED", 377146022Sphilip .wled_set = "WLED", 378146022Sphilip .lcd_get = "\\BKLT", 379146022Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 380146022Sphilip .brn_get = "GPLV", 381146022Sphilip .brn_set = "SPLV", 382146024Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 383146022Sphilip .disp_set = "SDSP" 384146022Sphilip }, 385157605Sphilip { 386157605Sphilip .name = "W5A", 387157605Sphilip .bled_set = "BLED", 388157605Sphilip .lcd_get = "\\BKLT", 389157605Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 390157605Sphilip .brn_get = "GPLV", 391157605Sphilip .brn_set = "SPLV", 392157605Sphilip .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", 393157605Sphilip .disp_set = "SDSP" 394157605Sphilip }, 395137245Sphilip 396137245Sphilip { .name = NULL } 397137245Sphilip}; 398137245Sphilip 399137245Sphilip/* 400137245Sphilip * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 401137245Sphilip * but they can't be probed quite the same way as Asus laptops. 402137245Sphilip */ 403137245Sphilipstatic struct acpi_asus_model acpi_samsung_models[] = { 404137127Sphilip { 405128561Sphilip .name = "P30", 406128561Sphilip .wled_set = "WLED", 407128561Sphilip .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 408128561Sphilip .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 409128561Sphilip .lcd_get = "\\BKLT", 410128561Sphilip .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 411128561Sphilip }, 412128561Sphilip 413128561Sphilip { .name = NULL } 414128561Sphilip}; 415128561Sphilip 416180062Srpaulostatic void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context); 417180062Srpaulo 418178178Srpaulo/* 419178178Srpaulo * EeePC have an Asus ASUS010 gadget interface, 420178178Srpaulo * but they can't be probed quite the same way as Asus laptops. 421178178Srpaulo */ 422178178Srpaulostatic struct acpi_asus_model acpi_eeepc_models[] = { 423178178Srpaulo { 424178178Srpaulo .name = "EEE", 425178178Srpaulo .brn_get = "\\_SB.ATKD.PBLG", 426180062Srpaulo .brn_set = "\\_SB.ATKD.PBLS", 427180062Srpaulo .cam_get = "\\_SB.ATKD.CAMG", 428180062Srpaulo .cam_set = "\\_SB.ATKD.CAMS", 429180062Srpaulo .crd_set = "\\_SB.ATKD.CRDS", 430180062Srpaulo .crd_get = "\\_SB.ATKD.CRDG", 431180268Srpaulo .wlan_get = "\\_SB.ATKD.WLDG", 432180268Srpaulo .wlan_set = "\\_SB.ATKD.WLDS", 433180062Srpaulo .n_func = acpi_asus_eeepc_notify 434178178Srpaulo }, 435178178Srpaulo 436178178Srpaulo { .name = NULL } 437178178Srpaulo}; 438178178Srpaulo 439143894Sphilipstatic struct { 440143894Sphilip char *name; 441143894Sphilip char *description; 442143894Sphilip int method; 443180062Srpaulo int flags; 444143894Sphilip} acpi_asus_sysctls[] = { 445143894Sphilip { 446143894Sphilip .name = "lcd_backlight", 447143894Sphilip .method = ACPI_ASUS_METHOD_LCD, 448180062Srpaulo .description = "state of the lcd backlight", 449180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY 450143894Sphilip }, 451143894Sphilip { 452143894Sphilip .name = "lcd_brightness", 453143894Sphilip .method = ACPI_ASUS_METHOD_BRN, 454180062Srpaulo .description = "brightness of the lcd panel", 455180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY 456143894Sphilip }, 457143894Sphilip { 458143894Sphilip .name = "video_output", 459143894Sphilip .method = ACPI_ASUS_METHOD_DISP, 460180062Srpaulo .description = "display output state", 461180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 462143894Sphilip }, 463180062Srpaulo { 464180062Srpaulo .name = "camera", 465180062Srpaulo .method = ACPI_ASUS_METHOD_CAMERA, 466180062Srpaulo .description = "internal camera state", 467180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 468180062Srpaulo }, 469180062Srpaulo { 470180062Srpaulo .name = "cardreader", 471180062Srpaulo .method = ACPI_ASUS_METHOD_CARDRD, 472180062Srpaulo .description = "internal card reader state", 473180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 474180062Srpaulo }, 475180268Srpaulo { 476180268Srpaulo .name = "wlan", 477180268Srpaulo .method = ACPI_ASUS_METHOD_WLAN, 478180268Srpaulo .description = "wireless lan state", 479180268Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 480180268Srpaulo }, 481143894Sphilip 482143894Sphilip { .name = NULL } 483143894Sphilip}; 484143894Sphilip 485133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 486133628Snjl 487128561Sphilip/* Function prototypes */ 488128561Sphilipstatic int acpi_asus_probe(device_t dev); 489128561Sphilipstatic int acpi_asus_attach(device_t dev); 490128561Sphilipstatic int acpi_asus_detach(device_t dev); 491128561Sphilip 492133095Sphilipstatic void acpi_asus_led(struct acpi_asus_led *led, int state); 493144339Sphilipstatic void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 494128561Sphilip 495143894Sphilipstatic int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 496143894Sphilipstatic int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 497143894Sphilipstatic int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 498143894Sphilipstatic int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 499128561Sphilip 500128561Sphilipstatic void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 501128561Sphilip 502128561Sphilipstatic device_method_t acpi_asus_methods[] = { 503137631Sphilip DEVMETHOD(device_probe, acpi_asus_probe), 504128561Sphilip DEVMETHOD(device_attach, acpi_asus_attach), 505128561Sphilip DEVMETHOD(device_detach, acpi_asus_detach), 506128561Sphilip 507128561Sphilip { 0, 0 } 508128561Sphilip}; 509128561Sphilip 510128561Sphilipstatic driver_t acpi_asus_driver = { 511128561Sphilip "acpi_asus", 512128561Sphilip acpi_asus_methods, 513128561Sphilip sizeof(struct acpi_asus_softc) 514128561Sphilip}; 515128561Sphilip 516128561Sphilipstatic devclass_t acpi_asus_devclass; 517128561Sphilip 518128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 519128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 520128561Sphilip 521128561Sphilipstatic int 522128561Sphilipacpi_asus_probe(device_t dev) 523128561Sphilip{ 524128561Sphilip struct acpi_asus_model *model; 525128561Sphilip struct acpi_asus_softc *sc; 526128561Sphilip struct sbuf *sb; 527128561Sphilip ACPI_BUFFER Buf; 528128561Sphilip ACPI_OBJECT Arg, *Obj; 529128561Sphilip ACPI_OBJECT_LIST Args; 530178178Srpaulo static char *asus_ids[] = { "ATK0100", "ASUS010", NULL }; 531178178Srpaulo char *rstr; 532128561Sphilip 533128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 534128561Sphilip 535178178Srpaulo if (acpi_disabled("asus")) 536137632Sphilip return (ENXIO); 537178178Srpaulo rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids); 538178178Srpaulo if (rstr == NULL) { 539178178Srpaulo return (ENXIO); 540178178Srpaulo } 541137631Sphilip 542137632Sphilip sc = device_get_softc(dev); 543137632Sphilip sc->dev = dev; 544137632Sphilip sc->handle = acpi_get_handle(dev); 545128561Sphilip 546137632Sphilip Arg.Type = ACPI_TYPE_INTEGER; 547137632Sphilip Arg.Integer.Value = 0; 548128561Sphilip 549137632Sphilip Args.Count = 1; 550137632Sphilip Args.Pointer = &Arg; 551137631Sphilip 552137632Sphilip Buf.Pointer = NULL; 553137632Sphilip Buf.Length = ACPI_ALLOCATE_BUFFER; 554128561Sphilip 555137632Sphilip AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 556137632Sphilip Obj = Buf.Pointer; 557137245Sphilip 558137632Sphilip /* 559137632Sphilip * The Samsung P30 returns a null-pointer from INIT, we 560137632Sphilip * can identify it from the 'ODEM' string in the DSDT. 561137632Sphilip */ 562137632Sphilip if (Obj->String.Pointer == NULL) { 563137632Sphilip ACPI_STATUS status; 564137632Sphilip ACPI_TABLE_HEADER th; 565137245Sphilip 566167814Sjkim status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th); 567137632Sphilip if (ACPI_FAILURE(status)) { 568137632Sphilip device_printf(dev, "Unsupported (Samsung?) laptop\n"); 569137632Sphilip AcpiOsFree(Buf.Pointer); 570137632Sphilip return (ENXIO); 571137245Sphilip } 572137245Sphilip 573137632Sphilip if (strncmp("ODEM", th.OemTableId, 4) == 0) { 574137632Sphilip sc->model = &acpi_samsung_models[0]; 575137632Sphilip device_set_desc(dev, "Samsung P30 Laptop Extras"); 576137632Sphilip AcpiOsFree(Buf.Pointer); 577137632Sphilip return (0); 578137632Sphilip } 579178178Srpaulo 580178178Srpaulo /* if EeePC */ 581178231Srpaulo if (strncmp("ASUS010", rstr, 7) == 0) { 582178178Srpaulo sc->model = &acpi_eeepc_models[0]; 583178178Srpaulo device_set_desc(dev, "ASUS EeePC"); 584178178Srpaulo AcpiOsFree(Buf.Pointer); 585178178Srpaulo return (0); 586178178Srpaulo } 587137632Sphilip } 588137245Sphilip 589181463Sdes sb = sbuf_new_auto(); 590137632Sphilip if (sb == NULL) 591137632Sphilip return (ENOMEM); 592128561Sphilip 593137632Sphilip /* 594137632Sphilip * Asus laptops are simply identified by name, easy! 595137632Sphilip */ 596146024Sphilip for (model = acpi_asus_models; model->name != NULL; model++) { 597137632Sphilip if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 598146024Sphilip 599146024Sphilipgood: 600146024Sphilip sbuf_printf(sb, "Asus %s Laptop Extras", 601146024Sphilip Obj->String.Pointer); 602137632Sphilip sbuf_finish(sb); 603128561Sphilip 604137632Sphilip sc->model = model; 605144076Spjd device_set_desc_copy(dev, sbuf_data(sb)); 606128561Sphilip 607137632Sphilip sbuf_delete(sb); 608137632Sphilip AcpiOsFree(Buf.Pointer); 609137632Sphilip return (0); 610137632Sphilip } 611146024Sphilip 612146024Sphilip /* 613146024Sphilip * Some models look exactly the same as other models, but have 614146024Sphilip * their own ids. If we spot these, set them up with the same 615146024Sphilip * details as the models they're like, possibly dealing with 616146024Sphilip * small differences. 617146024Sphilip * 618146024Sphilip * XXX: there must be a prettier way to do this! 619146024Sphilip */ 620146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 621146024Sphilip (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || 622146024Sphilip strncmp(Obj->String.Pointer, "S1N", 3) == 0)) 623146024Sphilip goto good; 624146024Sphilip else if (strncmp(model->name, "A1x", 3) == 0 && 625146024Sphilip strncmp(Obj->String.Pointer, "A1", 2) == 0) 626146024Sphilip goto good; 627146024Sphilip else if (strncmp(model->name, "A2x", 3) == 0 && 628146024Sphilip strncmp(Obj->String.Pointer, "A2", 2) == 0) 629146024Sphilip goto good; 630146024Sphilip else if (strncmp(model->name, "D1x", 3) == 0 && 631146024Sphilip strncmp(Obj->String.Pointer, "D1", 2) == 0) 632146024Sphilip goto good; 633146024Sphilip else if (strncmp(model->name, "L3H", 3) == 0 && 634146024Sphilip strncmp(Obj->String.Pointer, "L2E", 3) == 0) 635146024Sphilip goto good; 636146024Sphilip else if (strncmp(model->name, "L5x", 3) == 0 && 637146024Sphilip strncmp(Obj->String.Pointer, "L5", 2) == 0) 638146024Sphilip goto good; 639146024Sphilip else if (strncmp(model->name, "M2E", 3) == 0 && 640146024Sphilip (strncmp(Obj->String.Pointer, "M2", 2) == 0 || 641146024Sphilip strncmp(Obj->String.Pointer, "L4E", 3) == 0)) 642146024Sphilip goto good; 643146024Sphilip else if (strncmp(model->name, "S1x", 3) == 0 && 644146024Sphilip (strncmp(Obj->String.Pointer, "L8", 2) == 0 || 645146024Sphilip strncmp(Obj->String.Pointer, "S1", 2) == 0)) 646146024Sphilip goto good; 647146024Sphilip else if (strncmp(model->name, "S2x", 3) == 0 && 648146024Sphilip (strncmp(Obj->String.Pointer, "J1", 2) == 0 || 649146024Sphilip strncmp(Obj->String.Pointer, "S2", 2) == 0)) 650146024Sphilip goto good; 651128561Sphilip 652146024Sphilip /* L2B is like L3C but has no lcd_get method */ 653146024Sphilip else if (strncmp(model->name, "L3C", 3) == 0 && 654146024Sphilip strncmp(Obj->String.Pointer, "L2B", 3) == 0) { 655146024Sphilip model->lcd_get = NULL; 656146024Sphilip goto good; 657146024Sphilip } 658146024Sphilip 659146024Sphilip /* A3G is like M6R but with a different lcd_get method */ 660146024Sphilip else if (strncmp(model->name, "M6R", 3) == 0 && 661146024Sphilip strncmp(Obj->String.Pointer, "A3G", 3) == 0) { 662146024Sphilip model->lcd_get = "\\BLFG"; 663146024Sphilip goto good; 664146024Sphilip } 665146024Sphilip 666146024Sphilip /* M2N and W1N are like xxN with added WLED */ 667146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 668146024Sphilip (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || 669146024Sphilip strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { 670146024Sphilip model->wled_set = "WLED"; 671146024Sphilip goto good; 672146024Sphilip } 673146024Sphilip 674146024Sphilip /* M5N and S5N are like xxN without MLED */ 675146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 676146024Sphilip (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || 677146024Sphilip strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { 678146024Sphilip model->mled_set = NULL; 679146024Sphilip goto good; 680146024Sphilip } 681146024Sphilip } 682146024Sphilip 683137632Sphilip sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 684137632Sphilip sbuf_finish(sb); 685128561Sphilip 686137632Sphilip device_printf(dev, sbuf_data(sb)); 687137632Sphilip 688137632Sphilip sbuf_delete(sb); 689137632Sphilip AcpiOsFree(Buf.Pointer); 690137632Sphilip 691128561Sphilip return (ENXIO); 692128561Sphilip} 693128561Sphilip 694128561Sphilipstatic int 695128561Sphilipacpi_asus_attach(device_t dev) 696128561Sphilip{ 697128561Sphilip struct acpi_asus_softc *sc; 698128561Sphilip struct acpi_softc *acpi_sc; 699128561Sphilip 700128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 701128561Sphilip 702128561Sphilip sc = device_get_softc(dev); 703128561Sphilip acpi_sc = acpi_device_get_parent_softc(dev); 704128561Sphilip 705128561Sphilip /* Build sysctl tree */ 706128561Sphilip sysctl_ctx_init(&sc->sysctl_ctx); 707128561Sphilip sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 708128561Sphilip SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 709128561Sphilip OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 710128561Sphilip 711143894Sphilip /* Hook up nodes */ 712143894Sphilip for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 713143894Sphilip if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 714143894Sphilip continue; 715143894Sphilip 716143894Sphilip SYSCTL_ADD_PROC(&sc->sysctl_ctx, 717143894Sphilip SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 718143894Sphilip acpi_asus_sysctls[i].name, 719180062Srpaulo acpi_asus_sysctls[i].flags, 720143894Sphilip sc, i, acpi_asus_sysctl, "I", 721143894Sphilip acpi_asus_sysctls[i].description); 722143894Sphilip } 723143894Sphilip 724128561Sphilip /* Attach leds */ 725146022Sphilip if (sc->model->bled_set) { 726146022Sphilip sc->s_bled.busy = 0; 727146022Sphilip sc->s_bled.sc = sc; 728146022Sphilip sc->s_bled.type = ACPI_ASUS_LED_BLED; 729146022Sphilip sc->s_bled.cdev = 730178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_bled, 731178069Sjkim "bled", 1); 732146022Sphilip } 733146022Sphilip 734178069Sjkim if (sc->model->dled_set) { 735178069Sjkim sc->s_dled.busy = 0; 736178069Sjkim sc->s_dled.sc = sc; 737178069Sjkim sc->s_dled.type = ACPI_ASUS_LED_DLED; 738178069Sjkim sc->s_dled.cdev = 739178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled"); 740178069Sjkim } 741178069Sjkim 742178069Sjkim if (sc->model->gled_set) { 743178069Sjkim sc->s_gled.busy = 0; 744178069Sjkim sc->s_gled.sc = sc; 745178069Sjkim sc->s_gled.type = ACPI_ASUS_LED_GLED; 746178069Sjkim sc->s_gled.cdev = 747178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled"); 748178069Sjkim } 749178069Sjkim 750133095Sphilip if (sc->model->mled_set) { 751144339Sphilip sc->s_mled.busy = 0; 752144339Sphilip sc->s_mled.sc = sc; 753133095Sphilip sc->s_mled.type = ACPI_ASUS_LED_MLED; 754133095Sphilip sc->s_mled.cdev = 755133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 756133095Sphilip } 757128561Sphilip 758133095Sphilip if (sc->model->tled_set) { 759144339Sphilip sc->s_tled.busy = 0; 760144339Sphilip sc->s_tled.sc = sc; 761133095Sphilip sc->s_tled.type = ACPI_ASUS_LED_TLED; 762133095Sphilip sc->s_tled.cdev = 763178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_tled, 764178069Sjkim "tled", 1); 765133095Sphilip } 766128561Sphilip 767133095Sphilip if (sc->model->wled_set) { 768144339Sphilip sc->s_wled.busy = 0; 769144339Sphilip sc->s_wled.sc = sc; 770133095Sphilip sc->s_wled.type = ACPI_ASUS_LED_WLED; 771133095Sphilip sc->s_wled.cdev = 772178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_wled, 773178069Sjkim "wled", 1); 774133095Sphilip } 775128561Sphilip 776128561Sphilip /* Activate hotkeys */ 777128561Sphilip AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 778128561Sphilip 779128561Sphilip /* Handle notifies */ 780180062Srpaulo if (sc->model->n_func == NULL) 781180062Srpaulo sc->model->n_func = acpi_asus_notify; 782180062Srpaulo 783132610Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 784180062Srpaulo sc->model->n_func, dev); 785137631Sphilip 786184625Srpaulo /* Find and hook the 'LCDD' object */ 787184625Srpaulo if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) { 788184625Srpaulo ACPI_STATUS res; 789184625Srpaulo 790184625Srpaulo sc->lcdd_handle = NULL; 791184625Srpaulo res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ? 792184625Srpaulo NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle)); 793184625Srpaulo if (ACPI_SUCCESS(res)) { 794184625Srpaulo AcpiInstallNotifyHandler((sc->lcdd_handle), 795184625Srpaulo ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev); 796184625Srpaulo } else { 797184625Srpaulo printf("%s: unable to find LCD device '%s'\n", 798184625Srpaulo __func__, sc->model->lcdd); 799184625Srpaulo } 800184625Srpaulo } 801184625Srpaulo 802128561Sphilip return (0); 803128561Sphilip} 804128561Sphilip 805128561Sphilipstatic int 806128561Sphilipacpi_asus_detach(device_t dev) 807128561Sphilip{ 808128561Sphilip struct acpi_asus_softc *sc; 809137631Sphilip 810128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 811128561Sphilip 812128561Sphilip sc = device_get_softc(dev); 813128561Sphilip 814128561Sphilip /* Turn the lights off */ 815146022Sphilip if (sc->model->bled_set) 816146022Sphilip led_destroy(sc->s_bled.cdev); 817146022Sphilip 818178069Sjkim if (sc->model->dled_set) 819178069Sjkim led_destroy(sc->s_dled.cdev); 820178069Sjkim 821178069Sjkim if (sc->model->gled_set) 822178069Sjkim led_destroy(sc->s_gled.cdev); 823178069Sjkim 824128561Sphilip if (sc->model->mled_set) 825133095Sphilip led_destroy(sc->s_mled.cdev); 826128561Sphilip 827128561Sphilip if (sc->model->tled_set) 828133095Sphilip led_destroy(sc->s_tled.cdev); 829128561Sphilip 830128561Sphilip if (sc->model->wled_set) 831133095Sphilip led_destroy(sc->s_wled.cdev); 832128561Sphilip 833128561Sphilip /* Remove notify handler */ 834132610Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 835132610Snjl acpi_asus_notify); 836184625Srpaulo 837184625Srpaulo if (sc->lcdd_handle) { 838184625Srpaulo KASSERT(sc->model->lcdd_n_func != NULL, 839184625Srpaulo ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero")); 840184625Srpaulo AcpiRemoveNotifyHandler((sc->lcdd_handle), 841184625Srpaulo ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func); 842184625Srpaulo } 843128561Sphilip 844128561Sphilip /* Free sysctl tree */ 845128561Sphilip sysctl_ctx_free(&sc->sysctl_ctx); 846128561Sphilip 847128561Sphilip return (0); 848128561Sphilip} 849128561Sphilip 850128561Sphilipstatic void 851144339Sphilipacpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 852128561Sphilip{ 853128561Sphilip struct acpi_asus_softc *sc; 854133095Sphilip char *method; 855144339Sphilip int state; 856144339Sphilip 857128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 858128561Sphilip 859144339Sphilip sc = led->sc; 860128561Sphilip 861133095Sphilip switch (led->type) { 862146022Sphilip case ACPI_ASUS_LED_BLED: 863146022Sphilip method = sc->model->bled_set; 864146022Sphilip state = led->state; 865146022Sphilip break; 866178069Sjkim case ACPI_ASUS_LED_DLED: 867178069Sjkim method = sc->model->dled_set; 868178069Sjkim state = led->state; 869178069Sjkim break; 870178069Sjkim case ACPI_ASUS_LED_GLED: 871178069Sjkim method = sc->model->gled_set; 872178069Sjkim state = led->state + 1; /* 1: off, 2: on */ 873178069Sjkim break; 874143894Sphilip case ACPI_ASUS_LED_MLED: 875143894Sphilip method = sc->model->mled_set; 876178069Sjkim state = !led->state; /* inverted */ 877143894Sphilip break; 878143894Sphilip case ACPI_ASUS_LED_TLED: 879143894Sphilip method = sc->model->tled_set; 880144339Sphilip state = led->state; 881143894Sphilip break; 882143894Sphilip case ACPI_ASUS_LED_WLED: 883143894Sphilip method = sc->model->wled_set; 884144339Sphilip state = led->state; 885143894Sphilip break; 886143894Sphilip default: 887143894Sphilip printf("acpi_asus_led: invalid LED type %d\n", 888143894Sphilip (int)led->type); 889143894Sphilip return; 890133095Sphilip } 891128561Sphilip 892133095Sphilip acpi_SetInteger(sc->handle, method, state); 893144339Sphilip led->busy = 0; 894128561Sphilip} 895144339Sphilip 896144339Sphilipstatic void 897144339Sphilipacpi_asus_led(struct acpi_asus_led *led, int state) 898144339Sphilip{ 899128561Sphilip 900144339Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 901144339Sphilip 902144339Sphilip if (led->busy) 903144339Sphilip return; 904144339Sphilip 905144339Sphilip led->busy = 1; 906144339Sphilip led->state = state; 907144339Sphilip 908167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led); 909144339Sphilip} 910144339Sphilip 911128561Sphilipstatic int 912143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 913128561Sphilip{ 914128561Sphilip struct acpi_asus_softc *sc; 915143894Sphilip int arg; 916143894Sphilip int error = 0; 917143894Sphilip int function; 918143894Sphilip int method; 919143894Sphilip 920128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 921128561Sphilip 922128561Sphilip sc = (struct acpi_asus_softc *)oidp->oid_arg1; 923143894Sphilip function = oidp->oid_arg2; 924143894Sphilip method = acpi_asus_sysctls[function].method; 925143894Sphilip 926133628Snjl ACPI_SERIAL_BEGIN(asus); 927143894Sphilip arg = acpi_asus_sysctl_get(sc, method); 928143894Sphilip error = sysctl_handle_int(oidp, &arg, 0, req); 929128561Sphilip 930128561Sphilip /* Sanity check */ 931143894Sphilip if (error != 0 || req->newptr == NULL) 932133092Snjl goto out; 933128561Sphilip 934143894Sphilip /* Update */ 935143894Sphilip error = acpi_asus_sysctl_set(sc, method, arg); 936128561Sphilip 937143894Sphilipout: 938143894Sphilip ACPI_SERIAL_END(asus); 939143894Sphilip return (error); 940143894Sphilip} 941128561Sphilip 942143894Sphilipstatic int 943143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 944143894Sphilip{ 945143894Sphilip int val = 0; 946128561Sphilip 947143894Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 948143894Sphilip ACPI_SERIAL_ASSERT(asus); 949143894Sphilip 950143894Sphilip switch (method) { 951143894Sphilip case ACPI_ASUS_METHOD_BRN: 952143894Sphilip val = sc->s_brn; 953143894Sphilip break; 954143894Sphilip case ACPI_ASUS_METHOD_DISP: 955143894Sphilip val = sc->s_disp; 956143894Sphilip break; 957143894Sphilip case ACPI_ASUS_METHOD_LCD: 958143894Sphilip val = sc->s_lcd; 959143894Sphilip break; 960180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 961180062Srpaulo val = sc->s_cam; 962180062Srpaulo break; 963180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 964180062Srpaulo val = sc->s_crd; 965180062Srpaulo break; 966180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 967180268Srpaulo val = sc->s_wlan; 968180268Srpaulo break; 969128561Sphilip } 970128561Sphilip 971143894Sphilip return (val); 972128561Sphilip} 973128561Sphilip 974128561Sphilipstatic int 975143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 976128561Sphilip{ 977180268Srpaulo ACPI_STATUS status = AE_OK; 978180268Srpaulo ACPI_OBJECT_LIST acpiargs; 979186529Sstas ACPI_OBJECT acpiarg[1]; 980128561Sphilip 981128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 982143894Sphilip ACPI_SERIAL_ASSERT(asus); 983128561Sphilip 984180268Srpaulo acpiargs.Count = 1; 985180268Srpaulo acpiargs.Pointer = acpiarg; 986180268Srpaulo acpiarg[0].Type = ACPI_TYPE_INTEGER; 987180268Srpaulo acpiarg[0].Integer.Value = arg; 988180268Srpaulo 989143894Sphilip switch (method) { 990143894Sphilip case ACPI_ASUS_METHOD_BRN: 991143894Sphilip if (arg < 0 || arg > 15) 992143894Sphilip return (EINVAL); 993128561Sphilip 994143894Sphilip if (sc->model->brn_set) 995143894Sphilip status = acpi_SetInteger(sc->handle, 996143894Sphilip sc->model->brn_set, arg); 997143894Sphilip else { 998143894Sphilip while (arg != 0) { 999143894Sphilip status = AcpiEvaluateObject(sc->handle, 1000143894Sphilip (arg > 0) ? sc->model->brn_up : 1001143894Sphilip sc->model->brn_dn, NULL, NULL); 1002143894Sphilip (arg > 0) ? arg-- : arg++; 1003143894Sphilip } 1004143894Sphilip } 1005128561Sphilip 1006143894Sphilip if (ACPI_SUCCESS(status)) 1007143894Sphilip sc->s_brn = arg; 1008128561Sphilip 1009143894Sphilip break; 1010143894Sphilip case ACPI_ASUS_METHOD_DISP: 1011143894Sphilip if (arg < 0 || arg > 7) 1012143894Sphilip return (EINVAL); 1013128561Sphilip 1014143894Sphilip status = acpi_SetInteger(sc->handle, 1015143894Sphilip sc->model->disp_set, arg); 1016128561Sphilip 1017143894Sphilip if (ACPI_SUCCESS(status)) 1018143894Sphilip sc->s_disp = arg; 1019128561Sphilip 1020143894Sphilip break; 1021143894Sphilip case ACPI_ASUS_METHOD_LCD: 1022143894Sphilip if (arg < 0 || arg > 1) 1023143894Sphilip return (EINVAL); 1024143894Sphilip 1025143894Sphilip if (strncmp(sc->model->name, "L3H", 3) != 0) 1026143894Sphilip status = AcpiEvaluateObject(sc->handle, 1027143894Sphilip sc->model->lcd_set, NULL, NULL); 1028143894Sphilip else 1029143894Sphilip status = acpi_SetInteger(sc->handle, 1030143894Sphilip sc->model->lcd_set, 0x7); 1031143894Sphilip 1032143894Sphilip if (ACPI_SUCCESS(status)) 1033143894Sphilip sc->s_lcd = arg; 1034143894Sphilip 1035143894Sphilip break; 1036180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1037180062Srpaulo if (arg < 0 || arg > 1) 1038180062Srpaulo return (EINVAL); 1039180062Srpaulo 1040180062Srpaulo status = AcpiEvaluateObject(sc->handle, 1041180268Srpaulo sc->model->cam_set, &acpiargs, NULL); 1042180062Srpaulo 1043180062Srpaulo if (ACPI_SUCCESS(status)) 1044180062Srpaulo sc->s_cam = arg; 1045180062Srpaulo break; 1046180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1047180062Srpaulo if (arg < 0 || arg > 1) 1048180062Srpaulo return (EINVAL); 1049180062Srpaulo 1050180062Srpaulo status = AcpiEvaluateObject(sc->handle, 1051180268Srpaulo sc->model->crd_set, &acpiargs, NULL); 1052180062Srpaulo 1053180062Srpaulo if (ACPI_SUCCESS(status)) 1054180062Srpaulo sc->s_crd = arg; 1055180062Srpaulo break; 1056180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1057180268Srpaulo if (arg < 0 || arg > 1) 1058180268Srpaulo return (EINVAL); 1059180268Srpaulo 1060180268Srpaulo status = AcpiEvaluateObject(sc->handle, 1061180268Srpaulo sc->model->wlan_set, &acpiargs, NULL); 1062180268Srpaulo 1063180268Srpaulo if (ACPI_SUCCESS(status)) 1064180268Srpaulo sc->s_wlan = arg; 1065180268Srpaulo break; 1066143894Sphilip } 1067143894Sphilip 1068143894Sphilip return (0); 1069128561Sphilip} 1070128561Sphilip 1071128561Sphilipstatic int 1072143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 1073128561Sphilip{ 1074143894Sphilip ACPI_STATUS status; 1075128561Sphilip 1076143894Sphilip switch (method) { 1077143894Sphilip case ACPI_ASUS_METHOD_BRN: 1078143894Sphilip if (sc->model->brn_get) { 1079143894Sphilip /* GPLV/SPLV models */ 1080143894Sphilip status = acpi_GetInteger(sc->handle, 1081143894Sphilip sc->model->brn_get, &sc->s_brn); 1082143894Sphilip if (ACPI_SUCCESS(status)) 1083143894Sphilip return (TRUE); 1084143894Sphilip } else if (sc->model->brn_up) { 1085143894Sphilip /* Relative models */ 1086143894Sphilip status = AcpiEvaluateObject(sc->handle, 1087143894Sphilip sc->model->brn_up, NULL, NULL); 1088143894Sphilip if (ACPI_FAILURE(status)) 1089143894Sphilip return (FALSE); 1090128561Sphilip 1091143894Sphilip status = AcpiEvaluateObject(sc->handle, 1092143894Sphilip sc->model->brn_dn, NULL, NULL); 1093143894Sphilip if (ACPI_FAILURE(status)) 1094143894Sphilip return (FALSE); 1095128561Sphilip 1096143894Sphilip return (TRUE); 1097143894Sphilip } 1098143894Sphilip return (FALSE); 1099143894Sphilip case ACPI_ASUS_METHOD_DISP: 1100143894Sphilip if (sc->model->disp_get) { 1101143894Sphilip status = acpi_GetInteger(sc->handle, 1102143894Sphilip sc->model->disp_get, &sc->s_disp); 1103143894Sphilip if (ACPI_SUCCESS(status)) 1104143894Sphilip return (TRUE); 1105143894Sphilip } 1106143894Sphilip return (FALSE); 1107143894Sphilip case ACPI_ASUS_METHOD_LCD: 1108178069Sjkim if (sc->model->lcd_get) { 1109178069Sjkim if (strncmp(sc->model->name, "G2K", 3) == 0) { 1110178069Sjkim ACPI_BUFFER Buf; 1111178069Sjkim ACPI_OBJECT Arg, Obj; 1112178069Sjkim ACPI_OBJECT_LIST Args; 1113128561Sphilip 1114178069Sjkim Arg.Type = ACPI_TYPE_INTEGER; 1115178069Sjkim Arg.Integer.Value = 0x11; 1116178069Sjkim Args.Count = 1; 1117178069Sjkim Args.Pointer = &Arg; 1118178069Sjkim Buf.Length = sizeof(Obj); 1119178069Sjkim Buf.Pointer = &Obj; 1120128561Sphilip 1121178069Sjkim status = AcpiEvaluateObject(sc->handle, 1122178069Sjkim sc->model->lcd_get, &Args, &Buf); 1123178069Sjkim if (ACPI_SUCCESS(status) && 1124178069Sjkim Obj.Type == ACPI_TYPE_INTEGER) { 1125178069Sjkim sc->s_lcd = Obj.Integer.Value; 1126178069Sjkim return (TRUE); 1127178069Sjkim } 1128178069Sjkim } else if (strncmp(sc->model->name, "L3H", 3) == 0) { 1129178069Sjkim ACPI_BUFFER Buf; 1130178069Sjkim ACPI_OBJECT Arg[2], Obj; 1131178069Sjkim ACPI_OBJECT_LIST Args; 1132128561Sphilip 1133178069Sjkim /* L3H is a bit special */ 1134178069Sjkim Arg[0].Type = ACPI_TYPE_INTEGER; 1135178069Sjkim Arg[0].Integer.Value = 0x02; 1136178069Sjkim Arg[1].Type = ACPI_TYPE_INTEGER; 1137178069Sjkim Arg[1].Integer.Value = 0x03; 1138128561Sphilip 1139178069Sjkim Args.Count = 2; 1140178069Sjkim Args.Pointer = Arg; 1141178069Sjkim 1142178069Sjkim Buf.Length = sizeof(Obj); 1143178069Sjkim Buf.Pointer = &Obj; 1144178069Sjkim 1145178069Sjkim status = AcpiEvaluateObject(sc->handle, 1146178069Sjkim sc->model->lcd_get, &Args, &Buf); 1147178069Sjkim if (ACPI_SUCCESS(status) && 1148178069Sjkim Obj.Type == ACPI_TYPE_INTEGER) { 1149178069Sjkim sc->s_lcd = Obj.Integer.Value >> 8; 1150178069Sjkim return (TRUE); 1151178069Sjkim } 1152178069Sjkim } else { 1153178069Sjkim status = acpi_GetInteger(sc->handle, 1154178069Sjkim sc->model->lcd_get, &sc->s_lcd); 1155178069Sjkim if (ACPI_SUCCESS(status)) 1156178069Sjkim return (TRUE); 1157143894Sphilip } 1158143894Sphilip } 1159143894Sphilip return (FALSE); 1160180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1161180062Srpaulo if (sc->model->cam_get) { 1162180062Srpaulo status = acpi_GetInteger(sc->handle, 1163180062Srpaulo sc->model->cam_get, &sc->s_cam); 1164180062Srpaulo if (ACPI_SUCCESS(status)) 1165180062Srpaulo return (TRUE); 1166180062Srpaulo } 1167180062Srpaulo return (FALSE); 1168180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1169180062Srpaulo if (sc->model->crd_get) { 1170180062Srpaulo status = acpi_GetInteger(sc->handle, 1171180062Srpaulo sc->model->crd_get, &sc->s_crd); 1172180062Srpaulo if (ACPI_SUCCESS(status)) 1173180062Srpaulo return (TRUE); 1174180062Srpaulo } 1175180062Srpaulo return (FALSE); 1176180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1177180268Srpaulo if (sc->model->wlan_get) { 1178180268Srpaulo status = acpi_GetInteger(sc->handle, 1179180268Srpaulo sc->model->wlan_get, &sc->s_wlan); 1180180268Srpaulo if (ACPI_SUCCESS(status)) 1181180268Srpaulo return (TRUE); 1182180268Srpaulo } 1183180268Srpaulo return (FALSE); 1184143894Sphilip } 1185143894Sphilip return (FALSE); 1186128561Sphilip} 1187128561Sphilip 1188128561Sphilipstatic void 1189128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1190128561Sphilip{ 1191128561Sphilip struct acpi_asus_softc *sc; 1192128561Sphilip struct acpi_softc *acpi_sc; 1193128561Sphilip 1194128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1195128561Sphilip 1196128561Sphilip sc = device_get_softc((device_t)context); 1197128561Sphilip acpi_sc = acpi_device_get_parent_softc(sc->dev); 1198128561Sphilip 1199133628Snjl ACPI_SERIAL_BEGIN(asus); 1200128561Sphilip if ((notify & ~0x10) <= 15) { 1201132610Snjl sc->s_brn = notify & ~0x10; 1202128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1203128561Sphilip } else if ((notify & ~0x20) <= 15) { 1204132610Snjl sc->s_brn = notify & ~0x20; 1205128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1206128561Sphilip } else if (notify == 0x33) { 1207128561Sphilip sc->s_lcd = 1; 1208128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 1209128561Sphilip } else if (notify == 0x34) { 1210128561Sphilip sc->s_lcd = 0; 1211128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 1212184625Srpaulo } else if (notify == 0x86) { 1213184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1214184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1215184625Srpaulo } else if (notify == 0x87) { 1216184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1217184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1218128561Sphilip } else { 1219128561Sphilip /* Notify devd(8) */ 1220128561Sphilip acpi_UserNotify("ASUS", h, notify); 1221128561Sphilip } 1222133628Snjl ACPI_SERIAL_END(asus); 1223128561Sphilip} 1224180062Srpaulo 1225180062Srpaulostatic void 1226184625Srpauloacpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1227184625Srpaulo{ 1228184625Srpaulo struct acpi_asus_softc *sc; 1229184625Srpaulo struct acpi_softc *acpi_sc; 1230184625Srpaulo 1231184625Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1232184625Srpaulo 1233184625Srpaulo sc = device_get_softc((device_t)context); 1234184625Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1235184625Srpaulo 1236184625Srpaulo ACPI_SERIAL_BEGIN(asus); 1237184625Srpaulo switch (notify) { 1238184625Srpaulo case 0x87: 1239184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1240184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1241184625Srpaulo break; 1242184625Srpaulo case 0x86: 1243184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1244184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1245184625Srpaulo break; 1246184625Srpaulo } 1247184625Srpaulo ACPI_SERIAL_END(asus); 1248184625Srpaulo} 1249184625Srpaulo 1250184625Srpaulostatic void 1251180062Srpauloacpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1252180062Srpaulo{ 1253180062Srpaulo struct acpi_asus_softc *sc; 1254180062Srpaulo struct acpi_softc *acpi_sc; 1255180062Srpaulo 1256180062Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1257180062Srpaulo 1258180062Srpaulo sc = device_get_softc((device_t)context); 1259180062Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1260180062Srpaulo 1261180062Srpaulo ACPI_SERIAL_BEGIN(asus); 1262180062Srpaulo if ((notify & ~0x20) <= 15) { 1263180062Srpaulo sc->s_brn = notify & ~0x20; 1264180062Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, 1265180062Srpaulo "Brightness increased/decreased\n"); 1266180062Srpaulo } else { 1267180062Srpaulo /* Notify devd(8) */ 1268180062Srpaulo acpi_UserNotify("ASUS-Eee", h, notify); 1269180062Srpaulo } 1270180062Srpaulo ACPI_SERIAL_END(asus); 1271180062Srpaulo} 1272