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$"); 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 50193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 51193530Sjkim#include <contrib/dev/acpica/include/accommon.h> 52193530Sjkim 53128561Sphilip#include <dev/acpica/acpivar.h> 54128561Sphilip#include <dev/led/led.h> 55128561Sphilip 56143894Sphilip/* Methods */ 57143894Sphilip#define ACPI_ASUS_METHOD_BRN 1 58143894Sphilip#define ACPI_ASUS_METHOD_DISP 2 59143894Sphilip#define ACPI_ASUS_METHOD_LCD 3 60180062Srpaulo#define ACPI_ASUS_METHOD_CAMERA 4 61180075Sremko#define ACPI_ASUS_METHOD_CARDRD 5 62180268Srpaulo#define ACPI_ASUS_METHOD_WLAN 6 63143894Sphilip 64138825Snjl#define _COMPONENT ACPI_OEM 65128561SphilipACPI_MODULE_NAME("ASUS") 66128561Sphilip 67128561Sphilipstruct acpi_asus_model { 68128561Sphilip char *name; 69128561Sphilip 70146022Sphilip char *bled_set; 71178069Sjkim char *dled_set; 72178069Sjkim char *gled_set; 73128561Sphilip char *mled_set; 74128561Sphilip char *tled_set; 75128561Sphilip char *wled_set; 76128561Sphilip 77128561Sphilip char *brn_get; 78128561Sphilip char *brn_set; 79128561Sphilip char *brn_up; 80128561Sphilip char *brn_dn; 81128561Sphilip 82128561Sphilip char *lcd_get; 83128561Sphilip char *lcd_set; 84128561Sphilip 85128561Sphilip char *disp_get; 86128561Sphilip char *disp_set; 87180062Srpaulo 88180062Srpaulo char *cam_get; 89180062Srpaulo char *cam_set; 90180062Srpaulo 91180062Srpaulo char *crd_get; 92180062Srpaulo char *crd_set; 93180062Srpaulo 94180268Srpaulo char *wlan_get; 95180268Srpaulo char *wlan_set; 96180268Srpaulo 97180062Srpaulo void (*n_func)(ACPI_HANDLE, UINT32, void *); 98184625Srpaulo 99184625Srpaulo char *lcdd; 100184625Srpaulo void (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *); 101128561Sphilip}; 102128561Sphilip 103133095Sphilipstruct acpi_asus_led { 104144339Sphilip struct acpi_asus_softc *sc; 105133095Sphilip struct cdev *cdev; 106144339Sphilip int busy; 107144339Sphilip int state; 108133095Sphilip enum { 109146022Sphilip ACPI_ASUS_LED_BLED, 110178069Sjkim ACPI_ASUS_LED_DLED, 111178069Sjkim ACPI_ASUS_LED_GLED, 112133095Sphilip ACPI_ASUS_LED_MLED, 113133095Sphilip ACPI_ASUS_LED_TLED, 114133095Sphilip ACPI_ASUS_LED_WLED, 115133095Sphilip } type; 116133095Sphilip}; 117133095Sphilip 118128561Sphilipstruct acpi_asus_softc { 119128561Sphilip device_t dev; 120128561Sphilip ACPI_HANDLE handle; 121184625Srpaulo ACPI_HANDLE lcdd_handle; 122128561Sphilip 123128561Sphilip struct acpi_asus_model *model; 124128561Sphilip struct sysctl_ctx_list sysctl_ctx; 125128561Sphilip struct sysctl_oid *sysctl_tree; 126128561Sphilip 127146022Sphilip struct acpi_asus_led s_bled; 128178069Sjkim struct acpi_asus_led s_dled; 129178069Sjkim struct acpi_asus_led s_gled; 130133095Sphilip struct acpi_asus_led s_mled; 131133095Sphilip struct acpi_asus_led s_tled; 132133095Sphilip struct acpi_asus_led s_wled; 133128561Sphilip 134128561Sphilip int s_brn; 135128561Sphilip int s_disp; 136128561Sphilip int s_lcd; 137180062Srpaulo int s_cam; 138180062Srpaulo int s_crd; 139180268Srpaulo int s_wlan; 140128561Sphilip}; 141128561Sphilip 142184625Srpaulostatic void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, 143184625Srpaulo void *context); 144184625Srpaulo 145137245Sphilip/* 146137245Sphilip * We can identify Asus laptops from the string they return 147137245Sphilip * as a result of calling the ATK0100 'INIT' method. 148137245Sphilip */ 149128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = { 150128561Sphilip { 151146024Sphilip .name = "xxN", 152146024Sphilip .mled_set = "MLED", 153146024Sphilip .wled_set = "WLED", 154146024Sphilip .lcd_get = "\\BKLT", 155146024Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 156146024Sphilip .brn_get = "GPLV", 157146024Sphilip .brn_set = "SPLV", 158146024Sphilip .disp_get = "\\ADVG", 159146024Sphilip .disp_set = "SDSP" 160146024Sphilip }, 161146024Sphilip { 162146024Sphilip .name = "A1x", 163146024Sphilip .mled_set = "MLED", 164146024Sphilip .lcd_get = "\\BKLI", 165146024Sphilip .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 166146024Sphilip .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E", 167146024Sphilip .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F" 168146024Sphilip }, 169146024Sphilip { 170146024Sphilip .name = "A2x", 171146024Sphilip .mled_set = "MLED", 172146024Sphilip .wled_set = "WLED", 173146024Sphilip .lcd_get = "\\BAOF", 174146024Sphilip .lcd_set = "\\Q10", 175146024Sphilip .brn_get = "GPLV", 176146024Sphilip .brn_set = "SPLV", 177146024Sphilip .disp_get = "\\INFB", 178146024Sphilip .disp_set = "SDSP" 179146024Sphilip }, 180146024Sphilip { 181190695Sattilio .name = "A3E", 182190695Sattilio .mled_set = "MLED", 183190695Sattilio .wled_set = "WLED", 184190695Sattilio .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x67)", 185190695Sattilio .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 186190695Sattilio .brn_get = "GPLV", 187190695Sattilio .brn_set = "SPLV", 188190695Sattilio .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", 189190695Sattilio .disp_set = "SDSP" 190190695Sattilio }, 191190695Sattilio { 192190695Sattilio .name = "A3F", 193190695Sattilio .mled_set = "MLED", 194190695Sattilio .wled_set = "WLED", 195190695Sattilio .bled_set = "BLED", 196190695Sattilio .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x11)", 197190695Sattilio .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 198190695Sattilio .brn_get = "GPLV", 199190695Sattilio .brn_set = "SPLV", 200190695Sattilio .disp_get = "\\SSTE", 201190695Sattilio .disp_set = "SDSP" 202190695Sattilio }, 203190695Sattilio { 204170216Sphilip .name = "A3N", 205170216Sphilip .mled_set = "MLED", 206170216Sphilip .bled_set = "BLED", 207170216Sphilip .wled_set = "WLED", 208190695Sattilio .lcd_get = "\\BKLT", 209170216Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 210190695Sattilio .brn_get = "GPLV", 211170216Sphilip .brn_set = "SPLV", 212190695Sattilio .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", 213190695Sattilio .disp_set = "SDSP" 214170216Sphilip }, 215170216Sphilip { 216155022Sphilip .name = "A4D", 217155022Sphilip .mled_set = "MLED", 218155022Sphilip .brn_up = "\\_SB_.PCI0.SBRG.EC0._Q0E", 219155022Sphilip .brn_dn = "\\_SB_.PCI0.SBRG.EC0._Q0F", 220155022Sphilip .brn_get = "GPLV", 221155022Sphilip .brn_set = "SPLV", 222155022Sphilip#ifdef notyet 223155022Sphilip .disp_get = "\\_SB_.PCI0.SBRG.EC0._Q10", 224155022Sphilip .disp_set = "\\_SB_.PCI0.SBRG.EC0._Q11" 225155022Sphilip#endif 226155022Sphilip }, 227155022Sphilip { 228155021Sphilip .name = "A6V", 229155021Sphilip .bled_set = "BLED", 230155021Sphilip .mled_set = "MLED", 231155021Sphilip .wled_set = "WLED", 232155021Sphilip .lcd_get = NULL, 233155021Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 234155021Sphilip .brn_get = "GPLV", 235155021Sphilip .brn_set = "SPLV", 236155021Sphilip .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", 237155021Sphilip .disp_set = "SDSP" 238155021Sphilip }, 239155021Sphilip { 240184625Srpaulo .name = "A8SR", 241184625Srpaulo .bled_set = "BLED", 242184625Srpaulo .mled_set = "MLED", 243184625Srpaulo .wled_set = "WLED", 244184625Srpaulo .lcd_get = NULL, 245184625Srpaulo .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 246184625Srpaulo .brn_get = "GPLV", 247184625Srpaulo .brn_set = "SPLV", 248184625Srpaulo .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 249184625Srpaulo .disp_set = "SDSP", 250184625Srpaulo .lcdd = "\\_SB.PCI0.P0P1.VGA.LCDD", 251184625Srpaulo .lcdd_n_func = acpi_asus_lcdd_notify 252184625Srpaulo }, 253184625Srpaulo { 254146024Sphilip .name = "D1x", 255146024Sphilip .mled_set = "MLED", 256146024Sphilip .lcd_get = "\\GP11", 257146024Sphilip .lcd_set = "\\Q0D", 258146024Sphilip .brn_up = "\\Q0C", 259146024Sphilip .brn_dn = "\\Q0B", 260146024Sphilip .disp_get = "\\INFB", 261146024Sphilip .disp_set = "SDSP" 262146024Sphilip }, 263146024Sphilip { 264178069Sjkim .name = "G2K", 265178069Sjkim .bled_set = "BLED", 266178069Sjkim .dled_set = "DLED", 267178069Sjkim .gled_set = "GLED", 268178069Sjkim .mled_set = "MLED", 269178069Sjkim .tled_set = "TLED", 270178069Sjkim .wled_set = "WLED", 271178069Sjkim .brn_get = "GPLV", 272178069Sjkim .brn_set = "SPLV", 273203811Sjkim .lcd_get = "GBTL", 274203811Sjkim .lcd_set = "SBTL", 275178069Sjkim .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD", 276178069Sjkim .disp_set = "SDSP", 277178069Sjkim }, 278178069Sjkim { 279128561Sphilip .name = "L2D", 280128561Sphilip .mled_set = "MLED", 281128561Sphilip .wled_set = "WLED", 282128561Sphilip .brn_up = "\\Q0E", 283128561Sphilip .brn_dn = "\\Q0F", 284128561Sphilip .lcd_get = "\\SGP0", 285128561Sphilip .lcd_set = "\\Q10" 286128561Sphilip }, 287128561Sphilip { 288128561Sphilip .name = "L3C", 289128561Sphilip .mled_set = "MLED", 290128561Sphilip .wled_set = "WLED", 291128561Sphilip .brn_get = "GPLV", 292128561Sphilip .brn_set = "SPLV", 293128561Sphilip .lcd_get = "\\GL32", 294128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 295128561Sphilip }, 296128561Sphilip { 297128561Sphilip .name = "L3D", 298128561Sphilip .mled_set = "MLED", 299128561Sphilip .wled_set = "WLED", 300128561Sphilip .brn_get = "GPLV", 301128561Sphilip .brn_set = "SPLV", 302128561Sphilip .lcd_get = "\\BKLG", 303128561Sphilip .lcd_set = "\\Q10" 304128561Sphilip }, 305128561Sphilip { 306128561Sphilip .name = "L3H", 307128561Sphilip .mled_set = "MLED", 308128561Sphilip .wled_set = "WLED", 309128561Sphilip .brn_get = "GPLV", 310128561Sphilip .brn_set = "SPLV", 311128561Sphilip .lcd_get = "\\_SB.PCI0.PM.PBC", 312128561Sphilip .lcd_set = "EHK", 313128561Sphilip .disp_get = "\\_SB.INFB", 314128561Sphilip .disp_set = "SDSP" 315128561Sphilip }, 316128561Sphilip { 317137388Sphilip .name = "L4R", 318137388Sphilip .mled_set = "MLED", 319137388Sphilip .wled_set = "WLED", 320137388Sphilip .brn_get = "GPLV", 321137388Sphilip .brn_set = "SPLV", 322137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 323137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 324137388Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 325137388Sphilip .disp_set = "SDSP" 326137388Sphilip }, 327137388Sphilip { 328146024Sphilip .name = "L5x", 329146024Sphilip .mled_set = "MLED", 330146024Sphilip .tled_set = "TLED", 331146024Sphilip .lcd_get = "\\BAOF", 332146024Sphilip .lcd_set = "\\Q0D", 333146024Sphilip .brn_get = "GPLV", 334146024Sphilip .brn_set = "SPLV", 335146024Sphilip .disp_get = "\\INFB", 336146024Sphilip .disp_set = "SDSP" 337146024Sphilip }, 338146024Sphilip { 339128561Sphilip .name = "L8L" 340181885Srpaulo /* Only has hotkeys, apparently */ 341128561Sphilip }, 342128561Sphilip { 343128561Sphilip .name = "M1A", 344128561Sphilip .mled_set = "MLED", 345128561Sphilip .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 346128561Sphilip .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 347128561Sphilip .lcd_get = "\\PNOF", 348128561Sphilip .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 349128561Sphilip }, 350128561Sphilip { 351128561Sphilip .name = "M2E", 352128561Sphilip .mled_set = "MLED", 353128561Sphilip .wled_set = "WLED", 354128561Sphilip .brn_get = "GPLV", 355128561Sphilip .brn_set = "SPLV", 356128561Sphilip .lcd_get = "\\GP06", 357128561Sphilip .lcd_set = "\\Q10" 358128561Sphilip }, 359128561Sphilip { 360137127Sphilip .name = "M6N", 361137127Sphilip .mled_set = "MLED", 362137127Sphilip .wled_set = "WLED", 363137127Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 364137127Sphilip .lcd_get = "\\_SB.BKLT", 365137127Sphilip .brn_set = "SPLV", 366137127Sphilip .brn_get = "GPLV", 367137127Sphilip .disp_set = "SDSP", 368137127Sphilip .disp_get = "\\SSTE" 369137127Sphilip }, 370137388Sphilip { 371137388Sphilip .name = "M6R", 372137388Sphilip .mled_set = "MLED", 373137388Sphilip .wled_set = "WLED", 374137388Sphilip .brn_get = "GPLV", 375137388Sphilip .brn_set = "SPLV", 376137388Sphilip .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 377137388Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 378137388Sphilip .disp_get = "\\SSTE", 379137388Sphilip .disp_set = "SDSP" 380137388Sphilip }, 381146022Sphilip { 382146024Sphilip .name = "S1x", 383146024Sphilip .mled_set = "MLED", 384146022Sphilip .wled_set = "WLED", 385146024Sphilip .lcd_get = "\\PNOF", 386146024Sphilip .lcd_set = "\\_SB.PCI0.PX40.Q10", 387146022Sphilip .brn_get = "GPLV", 388146024Sphilip .brn_set = "SPLV" 389146022Sphilip }, 390146022Sphilip { 391146024Sphilip .name = "S2x", 392146022Sphilip .mled_set = "MLED", 393146024Sphilip .lcd_get = "\\BKLI", 394146024Sphilip .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 395146024Sphilip .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B", 396146024Sphilip .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A" 397146024Sphilip }, 398146024Sphilip { 399146024Sphilip .name = "V6V", 400146024Sphilip .bled_set = "BLED", 401146024Sphilip .tled_set = "TLED", 402146022Sphilip .wled_set = "WLED", 403146022Sphilip .lcd_get = "\\BKLT", 404146022Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 405146022Sphilip .brn_get = "GPLV", 406146022Sphilip .brn_set = "SPLV", 407146024Sphilip .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 408146022Sphilip .disp_set = "SDSP" 409146022Sphilip }, 410157605Sphilip { 411157605Sphilip .name = "W5A", 412157605Sphilip .bled_set = "BLED", 413157605Sphilip .lcd_get = "\\BKLT", 414157605Sphilip .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 415157605Sphilip .brn_get = "GPLV", 416157605Sphilip .brn_set = "SPLV", 417157605Sphilip .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", 418157605Sphilip .disp_set = "SDSP" 419157605Sphilip }, 420137245Sphilip 421137245Sphilip { .name = NULL } 422137245Sphilip}; 423137245Sphilip 424137245Sphilip/* 425137245Sphilip * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 426137245Sphilip * but they can't be probed quite the same way as Asus laptops. 427137245Sphilip */ 428137245Sphilipstatic struct acpi_asus_model acpi_samsung_models[] = { 429137127Sphilip { 430128561Sphilip .name = "P30", 431128561Sphilip .wled_set = "WLED", 432128561Sphilip .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 433128561Sphilip .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 434128561Sphilip .lcd_get = "\\BKLT", 435128561Sphilip .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 436128561Sphilip }, 437128561Sphilip 438128561Sphilip { .name = NULL } 439128561Sphilip}; 440128561Sphilip 441180062Srpaulostatic void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context); 442180062Srpaulo 443178178Srpaulo/* 444178178Srpaulo * EeePC have an Asus ASUS010 gadget interface, 445178178Srpaulo * but they can't be probed quite the same way as Asus laptops. 446178178Srpaulo */ 447178178Srpaulostatic struct acpi_asus_model acpi_eeepc_models[] = { 448178178Srpaulo { 449178178Srpaulo .name = "EEE", 450178178Srpaulo .brn_get = "\\_SB.ATKD.PBLG", 451180062Srpaulo .brn_set = "\\_SB.ATKD.PBLS", 452180062Srpaulo .cam_get = "\\_SB.ATKD.CAMG", 453180062Srpaulo .cam_set = "\\_SB.ATKD.CAMS", 454180062Srpaulo .crd_set = "\\_SB.ATKD.CRDS", 455180062Srpaulo .crd_get = "\\_SB.ATKD.CRDG", 456180268Srpaulo .wlan_get = "\\_SB.ATKD.WLDG", 457180268Srpaulo .wlan_set = "\\_SB.ATKD.WLDS", 458180062Srpaulo .n_func = acpi_asus_eeepc_notify 459178178Srpaulo }, 460178178Srpaulo 461178178Srpaulo { .name = NULL } 462178178Srpaulo}; 463178178Srpaulo 464143894Sphilipstatic struct { 465143894Sphilip char *name; 466143894Sphilip char *description; 467143894Sphilip int method; 468180062Srpaulo int flags; 469143894Sphilip} acpi_asus_sysctls[] = { 470143894Sphilip { 471143894Sphilip .name = "lcd_backlight", 472143894Sphilip .method = ACPI_ASUS_METHOD_LCD, 473180062Srpaulo .description = "state of the lcd backlight", 474180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY 475143894Sphilip }, 476143894Sphilip { 477143894Sphilip .name = "lcd_brightness", 478143894Sphilip .method = ACPI_ASUS_METHOD_BRN, 479180062Srpaulo .description = "brightness of the lcd panel", 480180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY 481143894Sphilip }, 482143894Sphilip { 483143894Sphilip .name = "video_output", 484143894Sphilip .method = ACPI_ASUS_METHOD_DISP, 485180062Srpaulo .description = "display output state", 486180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 487143894Sphilip }, 488180062Srpaulo { 489180062Srpaulo .name = "camera", 490180062Srpaulo .method = ACPI_ASUS_METHOD_CAMERA, 491180062Srpaulo .description = "internal camera state", 492180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 493180062Srpaulo }, 494180062Srpaulo { 495180062Srpaulo .name = "cardreader", 496180062Srpaulo .method = ACPI_ASUS_METHOD_CARDRD, 497180062Srpaulo .description = "internal card reader state", 498180062Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 499180062Srpaulo }, 500180268Srpaulo { 501180268Srpaulo .name = "wlan", 502180268Srpaulo .method = ACPI_ASUS_METHOD_WLAN, 503180268Srpaulo .description = "wireless lan state", 504180268Srpaulo .flags = CTLTYPE_INT | CTLFLAG_RW 505180268Srpaulo }, 506143894Sphilip 507143894Sphilip { .name = NULL } 508143894Sphilip}; 509143894Sphilip 510133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 511133628Snjl 512128561Sphilip/* Function prototypes */ 513128561Sphilipstatic int acpi_asus_probe(device_t dev); 514128561Sphilipstatic int acpi_asus_attach(device_t dev); 515128561Sphilipstatic int acpi_asus_detach(device_t dev); 516128561Sphilip 517133095Sphilipstatic void acpi_asus_led(struct acpi_asus_led *led, int state); 518144339Sphilipstatic void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 519128561Sphilip 520143894Sphilipstatic int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 521143894Sphilipstatic int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 522143894Sphilipstatic int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 523143894Sphilipstatic int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 524128561Sphilip 525128561Sphilipstatic void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 526128561Sphilip 527128561Sphilipstatic device_method_t acpi_asus_methods[] = { 528137631Sphilip DEVMETHOD(device_probe, acpi_asus_probe), 529128561Sphilip DEVMETHOD(device_attach, acpi_asus_attach), 530128561Sphilip DEVMETHOD(device_detach, acpi_asus_detach), 531128561Sphilip 532128561Sphilip { 0, 0 } 533128561Sphilip}; 534128561Sphilip 535128561Sphilipstatic driver_t acpi_asus_driver = { 536128561Sphilip "acpi_asus", 537128561Sphilip acpi_asus_methods, 538128561Sphilip sizeof(struct acpi_asus_softc) 539128561Sphilip}; 540128561Sphilip 541128561Sphilipstatic devclass_t acpi_asus_devclass; 542128561Sphilip 543128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 544128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 545128561Sphilip 546128561Sphilipstatic int 547128561Sphilipacpi_asus_probe(device_t dev) 548128561Sphilip{ 549128561Sphilip struct acpi_asus_model *model; 550128561Sphilip struct acpi_asus_softc *sc; 551128561Sphilip struct sbuf *sb; 552128561Sphilip ACPI_BUFFER Buf; 553128561Sphilip ACPI_OBJECT Arg, *Obj; 554128561Sphilip ACPI_OBJECT_LIST Args; 555178178Srpaulo static char *asus_ids[] = { "ATK0100", "ASUS010", NULL }; 556178178Srpaulo char *rstr; 557128561Sphilip 558128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 559128561Sphilip 560178178Srpaulo if (acpi_disabled("asus")) 561137632Sphilip return (ENXIO); 562178178Srpaulo rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids); 563178178Srpaulo if (rstr == NULL) { 564178178Srpaulo return (ENXIO); 565178178Srpaulo } 566137631Sphilip 567137632Sphilip sc = device_get_softc(dev); 568137632Sphilip sc->dev = dev; 569137632Sphilip sc->handle = acpi_get_handle(dev); 570128561Sphilip 571137632Sphilip Arg.Type = ACPI_TYPE_INTEGER; 572137632Sphilip Arg.Integer.Value = 0; 573128561Sphilip 574137632Sphilip Args.Count = 1; 575137632Sphilip Args.Pointer = &Arg; 576137631Sphilip 577137632Sphilip Buf.Pointer = NULL; 578137632Sphilip Buf.Length = ACPI_ALLOCATE_BUFFER; 579128561Sphilip 580137632Sphilip AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 581137632Sphilip Obj = Buf.Pointer; 582137245Sphilip 583137632Sphilip /* 584137632Sphilip * The Samsung P30 returns a null-pointer from INIT, we 585137632Sphilip * can identify it from the 'ODEM' string in the DSDT. 586137632Sphilip */ 587137632Sphilip if (Obj->String.Pointer == NULL) { 588137632Sphilip ACPI_STATUS status; 589137632Sphilip ACPI_TABLE_HEADER th; 590137245Sphilip 591167814Sjkim status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th); 592137632Sphilip if (ACPI_FAILURE(status)) { 593137632Sphilip device_printf(dev, "Unsupported (Samsung?) laptop\n"); 594137632Sphilip AcpiOsFree(Buf.Pointer); 595137632Sphilip return (ENXIO); 596137245Sphilip } 597137245Sphilip 598137632Sphilip if (strncmp("ODEM", th.OemTableId, 4) == 0) { 599137632Sphilip sc->model = &acpi_samsung_models[0]; 600137632Sphilip device_set_desc(dev, "Samsung P30 Laptop Extras"); 601137632Sphilip AcpiOsFree(Buf.Pointer); 602137632Sphilip return (0); 603137632Sphilip } 604178178Srpaulo 605190695Sattilio /* EeePC */ 606178231Srpaulo if (strncmp("ASUS010", rstr, 7) == 0) { 607178178Srpaulo sc->model = &acpi_eeepc_models[0]; 608178178Srpaulo device_set_desc(dev, "ASUS EeePC"); 609178178Srpaulo AcpiOsFree(Buf.Pointer); 610178178Srpaulo return (0); 611178178Srpaulo } 612137632Sphilip } 613137245Sphilip 614181463Sdes sb = sbuf_new_auto(); 615137632Sphilip if (sb == NULL) 616137632Sphilip return (ENOMEM); 617128561Sphilip 618137632Sphilip /* 619137632Sphilip * Asus laptops are simply identified by name, easy! 620137632Sphilip */ 621146024Sphilip for (model = acpi_asus_models; model->name != NULL; model++) { 622137632Sphilip if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 623146024Sphilip 624146024Sphilipgood: 625146024Sphilip sbuf_printf(sb, "Asus %s Laptop Extras", 626146024Sphilip Obj->String.Pointer); 627137632Sphilip sbuf_finish(sb); 628128561Sphilip 629137632Sphilip sc->model = model; 630144076Spjd device_set_desc_copy(dev, sbuf_data(sb)); 631128561Sphilip 632137632Sphilip sbuf_delete(sb); 633137632Sphilip AcpiOsFree(Buf.Pointer); 634137632Sphilip return (0); 635137632Sphilip } 636146024Sphilip 637146024Sphilip /* 638146024Sphilip * Some models look exactly the same as other models, but have 639146024Sphilip * their own ids. If we spot these, set them up with the same 640146024Sphilip * details as the models they're like, possibly dealing with 641146024Sphilip * small differences. 642146024Sphilip * 643146024Sphilip * XXX: there must be a prettier way to do this! 644146024Sphilip */ 645146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 646146024Sphilip (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || 647146024Sphilip strncmp(Obj->String.Pointer, "S1N", 3) == 0)) 648146024Sphilip goto good; 649146024Sphilip else if (strncmp(model->name, "A1x", 3) == 0 && 650146024Sphilip strncmp(Obj->String.Pointer, "A1", 2) == 0) 651146024Sphilip goto good; 652146024Sphilip else if (strncmp(model->name, "A2x", 3) == 0 && 653146024Sphilip strncmp(Obj->String.Pointer, "A2", 2) == 0) 654146024Sphilip goto good; 655190695Sattilio else if (strncmp(model->name, "A3F", 3) == 0 && 656190695Sattilio strncmp(Obj->String.Pointer, "A6F", 3) == 0) 657190695Sattilio goto good; 658146024Sphilip else if (strncmp(model->name, "D1x", 3) == 0 && 659146024Sphilip strncmp(Obj->String.Pointer, "D1", 2) == 0) 660146024Sphilip goto good; 661146024Sphilip else if (strncmp(model->name, "L3H", 3) == 0 && 662146024Sphilip strncmp(Obj->String.Pointer, "L2E", 3) == 0) 663146024Sphilip goto good; 664146024Sphilip else if (strncmp(model->name, "L5x", 3) == 0 && 665146024Sphilip strncmp(Obj->String.Pointer, "L5", 2) == 0) 666146024Sphilip goto good; 667146024Sphilip else if (strncmp(model->name, "M2E", 3) == 0 && 668146024Sphilip (strncmp(Obj->String.Pointer, "M2", 2) == 0 || 669146024Sphilip strncmp(Obj->String.Pointer, "L4E", 3) == 0)) 670146024Sphilip goto good; 671146024Sphilip else if (strncmp(model->name, "S1x", 3) == 0 && 672146024Sphilip (strncmp(Obj->String.Pointer, "L8", 2) == 0 || 673146024Sphilip strncmp(Obj->String.Pointer, "S1", 2) == 0)) 674146024Sphilip goto good; 675146024Sphilip else if (strncmp(model->name, "S2x", 3) == 0 && 676146024Sphilip (strncmp(Obj->String.Pointer, "J1", 2) == 0 || 677146024Sphilip strncmp(Obj->String.Pointer, "S2", 2) == 0)) 678146024Sphilip goto good; 679128561Sphilip 680146024Sphilip /* L2B is like L3C but has no lcd_get method */ 681146024Sphilip else if (strncmp(model->name, "L3C", 3) == 0 && 682146024Sphilip strncmp(Obj->String.Pointer, "L2B", 3) == 0) { 683146024Sphilip model->lcd_get = NULL; 684146024Sphilip goto good; 685146024Sphilip } 686146024Sphilip 687146024Sphilip /* A3G is like M6R but with a different lcd_get method */ 688146024Sphilip else if (strncmp(model->name, "M6R", 3) == 0 && 689146024Sphilip strncmp(Obj->String.Pointer, "A3G", 3) == 0) { 690146024Sphilip model->lcd_get = "\\BLFG"; 691146024Sphilip goto good; 692146024Sphilip } 693146024Sphilip 694146024Sphilip /* M2N and W1N are like xxN with added WLED */ 695146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 696146024Sphilip (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || 697146024Sphilip strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { 698146024Sphilip model->wled_set = "WLED"; 699146024Sphilip goto good; 700146024Sphilip } 701146024Sphilip 702146024Sphilip /* M5N and S5N are like xxN without MLED */ 703146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 704146024Sphilip (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || 705146024Sphilip strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { 706146024Sphilip model->mled_set = NULL; 707146024Sphilip goto good; 708146024Sphilip } 709146024Sphilip } 710146024Sphilip 711137632Sphilip sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 712137632Sphilip sbuf_finish(sb); 713128561Sphilip 714209066Sjkim device_printf(dev, "%s", sbuf_data(sb)); 715137632Sphilip 716137632Sphilip sbuf_delete(sb); 717137632Sphilip AcpiOsFree(Buf.Pointer); 718137632Sphilip 719128561Sphilip return (ENXIO); 720128561Sphilip} 721128561Sphilip 722128561Sphilipstatic int 723128561Sphilipacpi_asus_attach(device_t dev) 724128561Sphilip{ 725128561Sphilip struct acpi_asus_softc *sc; 726128561Sphilip struct acpi_softc *acpi_sc; 727128561Sphilip 728128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 729128561Sphilip 730128561Sphilip sc = device_get_softc(dev); 731128561Sphilip acpi_sc = acpi_device_get_parent_softc(dev); 732128561Sphilip 733128561Sphilip /* Build sysctl tree */ 734128561Sphilip sysctl_ctx_init(&sc->sysctl_ctx); 735128561Sphilip sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 736128561Sphilip SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 737128561Sphilip OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 738128561Sphilip 739143894Sphilip /* Hook up nodes */ 740143894Sphilip for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 741143894Sphilip if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 742143894Sphilip continue; 743143894Sphilip 744143894Sphilip SYSCTL_ADD_PROC(&sc->sysctl_ctx, 745143894Sphilip SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 746143894Sphilip acpi_asus_sysctls[i].name, 747180062Srpaulo acpi_asus_sysctls[i].flags, 748143894Sphilip sc, i, acpi_asus_sysctl, "I", 749143894Sphilip acpi_asus_sysctls[i].description); 750143894Sphilip } 751143894Sphilip 752128561Sphilip /* Attach leds */ 753146022Sphilip if (sc->model->bled_set) { 754146022Sphilip sc->s_bled.busy = 0; 755146022Sphilip sc->s_bled.sc = sc; 756146022Sphilip sc->s_bled.type = ACPI_ASUS_LED_BLED; 757146022Sphilip sc->s_bled.cdev = 758178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_bled, 759178069Sjkim "bled", 1); 760146022Sphilip } 761146022Sphilip 762178069Sjkim if (sc->model->dled_set) { 763178069Sjkim sc->s_dled.busy = 0; 764178069Sjkim sc->s_dled.sc = sc; 765178069Sjkim sc->s_dled.type = ACPI_ASUS_LED_DLED; 766178069Sjkim sc->s_dled.cdev = 767178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled"); 768178069Sjkim } 769178069Sjkim 770178069Sjkim if (sc->model->gled_set) { 771178069Sjkim sc->s_gled.busy = 0; 772178069Sjkim sc->s_gled.sc = sc; 773178069Sjkim sc->s_gled.type = ACPI_ASUS_LED_GLED; 774178069Sjkim sc->s_gled.cdev = 775178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled"); 776178069Sjkim } 777178069Sjkim 778133095Sphilip if (sc->model->mled_set) { 779144339Sphilip sc->s_mled.busy = 0; 780144339Sphilip sc->s_mled.sc = sc; 781133095Sphilip sc->s_mled.type = ACPI_ASUS_LED_MLED; 782133095Sphilip sc->s_mled.cdev = 783133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 784133095Sphilip } 785128561Sphilip 786133095Sphilip if (sc->model->tled_set) { 787144339Sphilip sc->s_tled.busy = 0; 788144339Sphilip sc->s_tled.sc = sc; 789133095Sphilip sc->s_tled.type = ACPI_ASUS_LED_TLED; 790133095Sphilip sc->s_tled.cdev = 791178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_tled, 792178069Sjkim "tled", 1); 793133095Sphilip } 794128561Sphilip 795133095Sphilip if (sc->model->wled_set) { 796144339Sphilip sc->s_wled.busy = 0; 797144339Sphilip sc->s_wled.sc = sc; 798133095Sphilip sc->s_wled.type = ACPI_ASUS_LED_WLED; 799133095Sphilip sc->s_wled.cdev = 800178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_wled, 801178069Sjkim "wled", 1); 802133095Sphilip } 803128561Sphilip 804128561Sphilip /* Activate hotkeys */ 805128561Sphilip AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 806128561Sphilip 807128561Sphilip /* Handle notifies */ 808180062Srpaulo if (sc->model->n_func == NULL) 809180062Srpaulo sc->model->n_func = acpi_asus_notify; 810180062Srpaulo 811132610Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 812180062Srpaulo sc->model->n_func, dev); 813137631Sphilip 814184625Srpaulo /* Find and hook the 'LCDD' object */ 815184625Srpaulo if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) { 816184625Srpaulo ACPI_STATUS res; 817184625Srpaulo 818184625Srpaulo sc->lcdd_handle = NULL; 819184625Srpaulo res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ? 820184625Srpaulo NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle)); 821184625Srpaulo if (ACPI_SUCCESS(res)) { 822184625Srpaulo AcpiInstallNotifyHandler((sc->lcdd_handle), 823184625Srpaulo ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev); 824184625Srpaulo } else { 825184625Srpaulo printf("%s: unable to find LCD device '%s'\n", 826184625Srpaulo __func__, sc->model->lcdd); 827184625Srpaulo } 828184625Srpaulo } 829184625Srpaulo 830128561Sphilip return (0); 831128561Sphilip} 832128561Sphilip 833128561Sphilipstatic int 834128561Sphilipacpi_asus_detach(device_t dev) 835128561Sphilip{ 836128561Sphilip struct acpi_asus_softc *sc; 837137631Sphilip 838128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 839128561Sphilip 840128561Sphilip sc = device_get_softc(dev); 841128561Sphilip 842128561Sphilip /* Turn the lights off */ 843146022Sphilip if (sc->model->bled_set) 844146022Sphilip led_destroy(sc->s_bled.cdev); 845146022Sphilip 846178069Sjkim if (sc->model->dled_set) 847178069Sjkim led_destroy(sc->s_dled.cdev); 848178069Sjkim 849178069Sjkim if (sc->model->gled_set) 850178069Sjkim led_destroy(sc->s_gled.cdev); 851178069Sjkim 852128561Sphilip if (sc->model->mled_set) 853133095Sphilip led_destroy(sc->s_mled.cdev); 854128561Sphilip 855128561Sphilip if (sc->model->tled_set) 856133095Sphilip led_destroy(sc->s_tled.cdev); 857128561Sphilip 858128561Sphilip if (sc->model->wled_set) 859133095Sphilip led_destroy(sc->s_wled.cdev); 860128561Sphilip 861128561Sphilip /* Remove notify handler */ 862132610Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 863132610Snjl acpi_asus_notify); 864184625Srpaulo 865184625Srpaulo if (sc->lcdd_handle) { 866184625Srpaulo KASSERT(sc->model->lcdd_n_func != NULL, 867184625Srpaulo ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero")); 868184625Srpaulo AcpiRemoveNotifyHandler((sc->lcdd_handle), 869184625Srpaulo ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func); 870184625Srpaulo } 871128561Sphilip 872128561Sphilip /* Free sysctl tree */ 873128561Sphilip sysctl_ctx_free(&sc->sysctl_ctx); 874128561Sphilip 875128561Sphilip return (0); 876128561Sphilip} 877128561Sphilip 878128561Sphilipstatic void 879144339Sphilipacpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 880128561Sphilip{ 881128561Sphilip struct acpi_asus_softc *sc; 882133095Sphilip char *method; 883144339Sphilip int state; 884144339Sphilip 885128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 886128561Sphilip 887144339Sphilip sc = led->sc; 888128561Sphilip 889133095Sphilip switch (led->type) { 890146022Sphilip case ACPI_ASUS_LED_BLED: 891146022Sphilip method = sc->model->bled_set; 892146022Sphilip state = led->state; 893146022Sphilip break; 894178069Sjkim case ACPI_ASUS_LED_DLED: 895178069Sjkim method = sc->model->dled_set; 896178069Sjkim state = led->state; 897178069Sjkim break; 898178069Sjkim case ACPI_ASUS_LED_GLED: 899178069Sjkim method = sc->model->gled_set; 900178069Sjkim state = led->state + 1; /* 1: off, 2: on */ 901178069Sjkim break; 902143894Sphilip case ACPI_ASUS_LED_MLED: 903143894Sphilip method = sc->model->mled_set; 904178069Sjkim state = !led->state; /* inverted */ 905143894Sphilip break; 906143894Sphilip case ACPI_ASUS_LED_TLED: 907143894Sphilip method = sc->model->tled_set; 908144339Sphilip state = led->state; 909143894Sphilip break; 910143894Sphilip case ACPI_ASUS_LED_WLED: 911143894Sphilip method = sc->model->wled_set; 912144339Sphilip state = led->state; 913143894Sphilip break; 914143894Sphilip default: 915143894Sphilip printf("acpi_asus_led: invalid LED type %d\n", 916143894Sphilip (int)led->type); 917143894Sphilip return; 918133095Sphilip } 919128561Sphilip 920133095Sphilip acpi_SetInteger(sc->handle, method, state); 921144339Sphilip led->busy = 0; 922128561Sphilip} 923144339Sphilip 924144339Sphilipstatic void 925144339Sphilipacpi_asus_led(struct acpi_asus_led *led, int state) 926144339Sphilip{ 927128561Sphilip 928144339Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 929144339Sphilip 930144339Sphilip if (led->busy) 931144339Sphilip return; 932144339Sphilip 933144339Sphilip led->busy = 1; 934144339Sphilip led->state = state; 935144339Sphilip 936167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led); 937144339Sphilip} 938144339Sphilip 939128561Sphilipstatic int 940143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 941128561Sphilip{ 942128561Sphilip struct acpi_asus_softc *sc; 943143894Sphilip int arg; 944143894Sphilip int error = 0; 945143894Sphilip int function; 946143894Sphilip int method; 947143894Sphilip 948128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 949128561Sphilip 950128561Sphilip sc = (struct acpi_asus_softc *)oidp->oid_arg1; 951143894Sphilip function = oidp->oid_arg2; 952143894Sphilip method = acpi_asus_sysctls[function].method; 953143894Sphilip 954133628Snjl ACPI_SERIAL_BEGIN(asus); 955143894Sphilip arg = acpi_asus_sysctl_get(sc, method); 956143894Sphilip error = sysctl_handle_int(oidp, &arg, 0, req); 957128561Sphilip 958128561Sphilip /* Sanity check */ 959143894Sphilip if (error != 0 || req->newptr == NULL) 960133092Snjl goto out; 961128561Sphilip 962143894Sphilip /* Update */ 963143894Sphilip error = acpi_asus_sysctl_set(sc, method, arg); 964128561Sphilip 965143894Sphilipout: 966143894Sphilip ACPI_SERIAL_END(asus); 967143894Sphilip return (error); 968143894Sphilip} 969128561Sphilip 970143894Sphilipstatic int 971143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 972143894Sphilip{ 973143894Sphilip int val = 0; 974128561Sphilip 975143894Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 976143894Sphilip ACPI_SERIAL_ASSERT(asus); 977143894Sphilip 978143894Sphilip switch (method) { 979143894Sphilip case ACPI_ASUS_METHOD_BRN: 980143894Sphilip val = sc->s_brn; 981143894Sphilip break; 982143894Sphilip case ACPI_ASUS_METHOD_DISP: 983143894Sphilip val = sc->s_disp; 984143894Sphilip break; 985143894Sphilip case ACPI_ASUS_METHOD_LCD: 986143894Sphilip val = sc->s_lcd; 987143894Sphilip break; 988180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 989180062Srpaulo val = sc->s_cam; 990180062Srpaulo break; 991180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 992180062Srpaulo val = sc->s_crd; 993180062Srpaulo break; 994180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 995180268Srpaulo val = sc->s_wlan; 996180268Srpaulo break; 997128561Sphilip } 998128561Sphilip 999143894Sphilip return (val); 1000128561Sphilip} 1001128561Sphilip 1002128561Sphilipstatic int 1003143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 1004128561Sphilip{ 1005180268Srpaulo ACPI_STATUS status = AE_OK; 1006180268Srpaulo ACPI_OBJECT_LIST acpiargs; 1007186529Sstas ACPI_OBJECT acpiarg[1]; 1008128561Sphilip 1009128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1010143894Sphilip ACPI_SERIAL_ASSERT(asus); 1011128561Sphilip 1012180268Srpaulo acpiargs.Count = 1; 1013180268Srpaulo acpiargs.Pointer = acpiarg; 1014180268Srpaulo acpiarg[0].Type = ACPI_TYPE_INTEGER; 1015180268Srpaulo acpiarg[0].Integer.Value = arg; 1016180268Srpaulo 1017143894Sphilip switch (method) { 1018143894Sphilip case ACPI_ASUS_METHOD_BRN: 1019143894Sphilip if (arg < 0 || arg > 15) 1020143894Sphilip return (EINVAL); 1021128561Sphilip 1022143894Sphilip if (sc->model->brn_set) 1023143894Sphilip status = acpi_SetInteger(sc->handle, 1024143894Sphilip sc->model->brn_set, arg); 1025143894Sphilip else { 1026143894Sphilip while (arg != 0) { 1027143894Sphilip status = AcpiEvaluateObject(sc->handle, 1028143894Sphilip (arg > 0) ? sc->model->brn_up : 1029143894Sphilip sc->model->brn_dn, NULL, NULL); 1030143894Sphilip (arg > 0) ? arg-- : arg++; 1031143894Sphilip } 1032143894Sphilip } 1033128561Sphilip 1034143894Sphilip if (ACPI_SUCCESS(status)) 1035143894Sphilip sc->s_brn = arg; 1036128561Sphilip 1037143894Sphilip break; 1038143894Sphilip case ACPI_ASUS_METHOD_DISP: 1039143894Sphilip if (arg < 0 || arg > 7) 1040143894Sphilip return (EINVAL); 1041128561Sphilip 1042143894Sphilip status = acpi_SetInteger(sc->handle, 1043143894Sphilip sc->model->disp_set, arg); 1044128561Sphilip 1045143894Sphilip if (ACPI_SUCCESS(status)) 1046143894Sphilip sc->s_disp = arg; 1047128561Sphilip 1048143894Sphilip break; 1049143894Sphilip case ACPI_ASUS_METHOD_LCD: 1050143894Sphilip if (arg < 0 || arg > 1) 1051143894Sphilip return (EINVAL); 1052143894Sphilip 1053143894Sphilip if (strncmp(sc->model->name, "L3H", 3) != 0) 1054143894Sphilip status = AcpiEvaluateObject(sc->handle, 1055143894Sphilip sc->model->lcd_set, NULL, NULL); 1056143894Sphilip else 1057143894Sphilip status = acpi_SetInteger(sc->handle, 1058143894Sphilip sc->model->lcd_set, 0x7); 1059143894Sphilip 1060143894Sphilip if (ACPI_SUCCESS(status)) 1061143894Sphilip sc->s_lcd = arg; 1062143894Sphilip 1063143894Sphilip break; 1064180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1065180062Srpaulo if (arg < 0 || arg > 1) 1066180062Srpaulo return (EINVAL); 1067180062Srpaulo 1068180062Srpaulo status = AcpiEvaluateObject(sc->handle, 1069180268Srpaulo sc->model->cam_set, &acpiargs, NULL); 1070180062Srpaulo 1071180062Srpaulo if (ACPI_SUCCESS(status)) 1072180062Srpaulo sc->s_cam = arg; 1073180062Srpaulo break; 1074180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1075180062Srpaulo if (arg < 0 || arg > 1) 1076180062Srpaulo return (EINVAL); 1077180062Srpaulo 1078180062Srpaulo status = AcpiEvaluateObject(sc->handle, 1079180268Srpaulo sc->model->crd_set, &acpiargs, NULL); 1080180062Srpaulo 1081180062Srpaulo if (ACPI_SUCCESS(status)) 1082180062Srpaulo sc->s_crd = arg; 1083180062Srpaulo break; 1084180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1085180268Srpaulo if (arg < 0 || arg > 1) 1086180268Srpaulo return (EINVAL); 1087180268Srpaulo 1088180268Srpaulo status = AcpiEvaluateObject(sc->handle, 1089180268Srpaulo sc->model->wlan_set, &acpiargs, NULL); 1090180268Srpaulo 1091180268Srpaulo if (ACPI_SUCCESS(status)) 1092180268Srpaulo sc->s_wlan = arg; 1093180268Srpaulo break; 1094143894Sphilip } 1095143894Sphilip 1096143894Sphilip return (0); 1097128561Sphilip} 1098128561Sphilip 1099128561Sphilipstatic int 1100143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 1101128561Sphilip{ 1102143894Sphilip ACPI_STATUS status; 1103128561Sphilip 1104143894Sphilip switch (method) { 1105143894Sphilip case ACPI_ASUS_METHOD_BRN: 1106143894Sphilip if (sc->model->brn_get) { 1107143894Sphilip /* GPLV/SPLV models */ 1108143894Sphilip status = acpi_GetInteger(sc->handle, 1109143894Sphilip sc->model->brn_get, &sc->s_brn); 1110143894Sphilip if (ACPI_SUCCESS(status)) 1111143894Sphilip return (TRUE); 1112143894Sphilip } else if (sc->model->brn_up) { 1113143894Sphilip /* Relative models */ 1114143894Sphilip status = AcpiEvaluateObject(sc->handle, 1115143894Sphilip sc->model->brn_up, NULL, NULL); 1116143894Sphilip if (ACPI_FAILURE(status)) 1117143894Sphilip return (FALSE); 1118128561Sphilip 1119143894Sphilip status = AcpiEvaluateObject(sc->handle, 1120143894Sphilip sc->model->brn_dn, NULL, NULL); 1121143894Sphilip if (ACPI_FAILURE(status)) 1122143894Sphilip return (FALSE); 1123128561Sphilip 1124143894Sphilip return (TRUE); 1125143894Sphilip } 1126143894Sphilip return (FALSE); 1127143894Sphilip case ACPI_ASUS_METHOD_DISP: 1128143894Sphilip if (sc->model->disp_get) { 1129143894Sphilip status = acpi_GetInteger(sc->handle, 1130143894Sphilip sc->model->disp_get, &sc->s_disp); 1131143894Sphilip if (ACPI_SUCCESS(status)) 1132143894Sphilip return (TRUE); 1133143894Sphilip } 1134143894Sphilip return (FALSE); 1135143894Sphilip case ACPI_ASUS_METHOD_LCD: 1136178069Sjkim if (sc->model->lcd_get) { 1137203811Sjkim if (strncmp(sc->model->name, "L3H", 3) == 0) { 1138178069Sjkim ACPI_BUFFER Buf; 1139178069Sjkim ACPI_OBJECT Arg[2], Obj; 1140178069Sjkim ACPI_OBJECT_LIST Args; 1141128561Sphilip 1142178069Sjkim /* L3H is a bit special */ 1143178069Sjkim Arg[0].Type = ACPI_TYPE_INTEGER; 1144178069Sjkim Arg[0].Integer.Value = 0x02; 1145178069Sjkim Arg[1].Type = ACPI_TYPE_INTEGER; 1146178069Sjkim Arg[1].Integer.Value = 0x03; 1147128561Sphilip 1148178069Sjkim Args.Count = 2; 1149178069Sjkim Args.Pointer = Arg; 1150178069Sjkim 1151178069Sjkim Buf.Length = sizeof(Obj); 1152178069Sjkim Buf.Pointer = &Obj; 1153178069Sjkim 1154178069Sjkim status = AcpiEvaluateObject(sc->handle, 1155178069Sjkim sc->model->lcd_get, &Args, &Buf); 1156178069Sjkim if (ACPI_SUCCESS(status) && 1157178069Sjkim Obj.Type == ACPI_TYPE_INTEGER) { 1158178069Sjkim sc->s_lcd = Obj.Integer.Value >> 8; 1159178069Sjkim return (TRUE); 1160178069Sjkim } 1161178069Sjkim } else { 1162178069Sjkim status = acpi_GetInteger(sc->handle, 1163178069Sjkim sc->model->lcd_get, &sc->s_lcd); 1164178069Sjkim if (ACPI_SUCCESS(status)) 1165178069Sjkim return (TRUE); 1166143894Sphilip } 1167143894Sphilip } 1168143894Sphilip return (FALSE); 1169180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1170180062Srpaulo if (sc->model->cam_get) { 1171180062Srpaulo status = acpi_GetInteger(sc->handle, 1172180062Srpaulo sc->model->cam_get, &sc->s_cam); 1173180062Srpaulo if (ACPI_SUCCESS(status)) 1174180062Srpaulo return (TRUE); 1175180062Srpaulo } 1176180062Srpaulo return (FALSE); 1177180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1178180062Srpaulo if (sc->model->crd_get) { 1179180062Srpaulo status = acpi_GetInteger(sc->handle, 1180180062Srpaulo sc->model->crd_get, &sc->s_crd); 1181180062Srpaulo if (ACPI_SUCCESS(status)) 1182180062Srpaulo return (TRUE); 1183180062Srpaulo } 1184180062Srpaulo return (FALSE); 1185180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1186180268Srpaulo if (sc->model->wlan_get) { 1187180268Srpaulo status = acpi_GetInteger(sc->handle, 1188180268Srpaulo sc->model->wlan_get, &sc->s_wlan); 1189180268Srpaulo if (ACPI_SUCCESS(status)) 1190180268Srpaulo return (TRUE); 1191180268Srpaulo } 1192180268Srpaulo return (FALSE); 1193143894Sphilip } 1194143894Sphilip return (FALSE); 1195128561Sphilip} 1196128561Sphilip 1197128561Sphilipstatic void 1198128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1199128561Sphilip{ 1200128561Sphilip struct acpi_asus_softc *sc; 1201128561Sphilip struct acpi_softc *acpi_sc; 1202128561Sphilip 1203128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1204128561Sphilip 1205128561Sphilip sc = device_get_softc((device_t)context); 1206128561Sphilip acpi_sc = acpi_device_get_parent_softc(sc->dev); 1207128561Sphilip 1208133628Snjl ACPI_SERIAL_BEGIN(asus); 1209128561Sphilip if ((notify & ~0x10) <= 15) { 1210132610Snjl sc->s_brn = notify & ~0x10; 1211128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1212128561Sphilip } else if ((notify & ~0x20) <= 15) { 1213132610Snjl sc->s_brn = notify & ~0x20; 1214128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1215128561Sphilip } else if (notify == 0x33) { 1216128561Sphilip sc->s_lcd = 1; 1217128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 1218128561Sphilip } else if (notify == 0x34) { 1219128561Sphilip sc->s_lcd = 0; 1220128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 1221184625Srpaulo } else if (notify == 0x86) { 1222184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1223184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1224184625Srpaulo } else if (notify == 0x87) { 1225184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1226184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1227128561Sphilip } else { 1228128561Sphilip /* Notify devd(8) */ 1229128561Sphilip acpi_UserNotify("ASUS", h, notify); 1230128561Sphilip } 1231133628Snjl ACPI_SERIAL_END(asus); 1232128561Sphilip} 1233180062Srpaulo 1234180062Srpaulostatic void 1235184625Srpauloacpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1236184625Srpaulo{ 1237184625Srpaulo struct acpi_asus_softc *sc; 1238184625Srpaulo struct acpi_softc *acpi_sc; 1239184625Srpaulo 1240184625Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1241184625Srpaulo 1242184625Srpaulo sc = device_get_softc((device_t)context); 1243184625Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1244184625Srpaulo 1245184625Srpaulo ACPI_SERIAL_BEGIN(asus); 1246184625Srpaulo switch (notify) { 1247184625Srpaulo case 0x87: 1248184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1249184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1250184625Srpaulo break; 1251184625Srpaulo case 0x86: 1252184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1253184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1254184625Srpaulo break; 1255184625Srpaulo } 1256184625Srpaulo ACPI_SERIAL_END(asus); 1257184625Srpaulo} 1258184625Srpaulo 1259184625Srpaulostatic void 1260180062Srpauloacpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1261180062Srpaulo{ 1262180062Srpaulo struct acpi_asus_softc *sc; 1263180062Srpaulo struct acpi_softc *acpi_sc; 1264180062Srpaulo 1265180062Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1266180062Srpaulo 1267180062Srpaulo sc = device_get_softc((device_t)context); 1268180062Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1269180062Srpaulo 1270180062Srpaulo ACPI_SERIAL_BEGIN(asus); 1271180062Srpaulo if ((notify & ~0x20) <= 15) { 1272180062Srpaulo sc->s_brn = notify & ~0x20; 1273180062Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, 1274180062Srpaulo "Brightness increased/decreased\n"); 1275180062Srpaulo } else { 1276180062Srpaulo /* Notify devd(8) */ 1277180062Srpaulo acpi_UserNotify("ASUS-Eee", h, notify); 1278180062Srpaulo } 1279180062Srpaulo ACPI_SERIAL_END(asus); 1280180062Srpaulo} 1281