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; 468273377Shselasky int flag_anybody; 469143894Sphilip} acpi_asus_sysctls[] = { 470143894Sphilip { 471143894Sphilip .name = "lcd_backlight", 472143894Sphilip .method = ACPI_ASUS_METHOD_LCD, 473180062Srpaulo .description = "state of the lcd backlight", 474273377Shselasky .flag_anybody = 1 475143894Sphilip }, 476143894Sphilip { 477143894Sphilip .name = "lcd_brightness", 478143894Sphilip .method = ACPI_ASUS_METHOD_BRN, 479180062Srpaulo .description = "brightness of the lcd panel", 480273377Shselasky .flag_anybody = 1 481143894Sphilip }, 482143894Sphilip { 483143894Sphilip .name = "video_output", 484143894Sphilip .method = ACPI_ASUS_METHOD_DISP, 485180062Srpaulo .description = "display output state", 486143894Sphilip }, 487180062Srpaulo { 488180062Srpaulo .name = "camera", 489180062Srpaulo .method = ACPI_ASUS_METHOD_CAMERA, 490180062Srpaulo .description = "internal camera state", 491180062Srpaulo }, 492180062Srpaulo { 493180062Srpaulo .name = "cardreader", 494180062Srpaulo .method = ACPI_ASUS_METHOD_CARDRD, 495180062Srpaulo .description = "internal card reader state", 496180062Srpaulo }, 497180268Srpaulo { 498180268Srpaulo .name = "wlan", 499180268Srpaulo .method = ACPI_ASUS_METHOD_WLAN, 500180268Srpaulo .description = "wireless lan state", 501180268Srpaulo }, 502143894Sphilip 503143894Sphilip { .name = NULL } 504143894Sphilip}; 505143894Sphilip 506133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 507133628Snjl 508128561Sphilip/* Function prototypes */ 509128561Sphilipstatic int acpi_asus_probe(device_t dev); 510128561Sphilipstatic int acpi_asus_attach(device_t dev); 511128561Sphilipstatic int acpi_asus_detach(device_t dev); 512128561Sphilip 513133095Sphilipstatic void acpi_asus_led(struct acpi_asus_led *led, int state); 514144339Sphilipstatic void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 515128561Sphilip 516143894Sphilipstatic int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 517143894Sphilipstatic int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 518143894Sphilipstatic int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 519143894Sphilipstatic int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 520128561Sphilip 521128561Sphilipstatic void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 522128561Sphilip 523128561Sphilipstatic device_method_t acpi_asus_methods[] = { 524137631Sphilip DEVMETHOD(device_probe, acpi_asus_probe), 525128561Sphilip DEVMETHOD(device_attach, acpi_asus_attach), 526128561Sphilip DEVMETHOD(device_detach, acpi_asus_detach), 527128561Sphilip 528128561Sphilip { 0, 0 } 529128561Sphilip}; 530128561Sphilip 531128561Sphilipstatic driver_t acpi_asus_driver = { 532128561Sphilip "acpi_asus", 533128561Sphilip acpi_asus_methods, 534128561Sphilip sizeof(struct acpi_asus_softc) 535128561Sphilip}; 536128561Sphilip 537128561Sphilipstatic devclass_t acpi_asus_devclass; 538128561Sphilip 539128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 540128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 541128561Sphilip 542128561Sphilipstatic int 543128561Sphilipacpi_asus_probe(device_t dev) 544128561Sphilip{ 545128561Sphilip struct acpi_asus_model *model; 546128561Sphilip struct acpi_asus_softc *sc; 547128561Sphilip struct sbuf *sb; 548128561Sphilip ACPI_BUFFER Buf; 549128561Sphilip ACPI_OBJECT Arg, *Obj; 550128561Sphilip ACPI_OBJECT_LIST Args; 551178178Srpaulo static char *asus_ids[] = { "ATK0100", "ASUS010", NULL }; 552178178Srpaulo char *rstr; 553128561Sphilip 554128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 555128561Sphilip 556178178Srpaulo if (acpi_disabled("asus")) 557137632Sphilip return (ENXIO); 558178178Srpaulo rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids); 559178178Srpaulo if (rstr == NULL) { 560178178Srpaulo return (ENXIO); 561178178Srpaulo } 562137631Sphilip 563137632Sphilip sc = device_get_softc(dev); 564137632Sphilip sc->dev = dev; 565137632Sphilip sc->handle = acpi_get_handle(dev); 566128561Sphilip 567137632Sphilip Arg.Type = ACPI_TYPE_INTEGER; 568137632Sphilip Arg.Integer.Value = 0; 569128561Sphilip 570137632Sphilip Args.Count = 1; 571137632Sphilip Args.Pointer = &Arg; 572137631Sphilip 573137632Sphilip Buf.Pointer = NULL; 574137632Sphilip Buf.Length = ACPI_ALLOCATE_BUFFER; 575128561Sphilip 576137632Sphilip AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 577137632Sphilip Obj = Buf.Pointer; 578137245Sphilip 579137632Sphilip /* 580137632Sphilip * The Samsung P30 returns a null-pointer from INIT, we 581137632Sphilip * can identify it from the 'ODEM' string in the DSDT. 582137632Sphilip */ 583137632Sphilip if (Obj->String.Pointer == NULL) { 584137632Sphilip ACPI_STATUS status; 585137632Sphilip ACPI_TABLE_HEADER th; 586137245Sphilip 587167814Sjkim status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th); 588137632Sphilip if (ACPI_FAILURE(status)) { 589137632Sphilip device_printf(dev, "Unsupported (Samsung?) laptop\n"); 590137632Sphilip AcpiOsFree(Buf.Pointer); 591137632Sphilip return (ENXIO); 592137245Sphilip } 593137245Sphilip 594137632Sphilip if (strncmp("ODEM", th.OemTableId, 4) == 0) { 595137632Sphilip sc->model = &acpi_samsung_models[0]; 596137632Sphilip device_set_desc(dev, "Samsung P30 Laptop Extras"); 597137632Sphilip AcpiOsFree(Buf.Pointer); 598137632Sphilip return (0); 599137632Sphilip } 600178178Srpaulo 601190695Sattilio /* EeePC */ 602178231Srpaulo if (strncmp("ASUS010", rstr, 7) == 0) { 603178178Srpaulo sc->model = &acpi_eeepc_models[0]; 604178178Srpaulo device_set_desc(dev, "ASUS EeePC"); 605178178Srpaulo AcpiOsFree(Buf.Pointer); 606178178Srpaulo return (0); 607178178Srpaulo } 608137632Sphilip } 609137245Sphilip 610181463Sdes sb = sbuf_new_auto(); 611137632Sphilip if (sb == NULL) 612137632Sphilip return (ENOMEM); 613128561Sphilip 614137632Sphilip /* 615137632Sphilip * Asus laptops are simply identified by name, easy! 616137632Sphilip */ 617146024Sphilip for (model = acpi_asus_models; model->name != NULL; model++) { 618137632Sphilip if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 619146024Sphilip 620146024Sphilipgood: 621146024Sphilip sbuf_printf(sb, "Asus %s Laptop Extras", 622146024Sphilip Obj->String.Pointer); 623137632Sphilip sbuf_finish(sb); 624128561Sphilip 625137632Sphilip sc->model = model; 626144076Spjd device_set_desc_copy(dev, sbuf_data(sb)); 627128561Sphilip 628137632Sphilip sbuf_delete(sb); 629137632Sphilip AcpiOsFree(Buf.Pointer); 630137632Sphilip return (0); 631137632Sphilip } 632146024Sphilip 633146024Sphilip /* 634146024Sphilip * Some models look exactly the same as other models, but have 635146024Sphilip * their own ids. If we spot these, set them up with the same 636146024Sphilip * details as the models they're like, possibly dealing with 637146024Sphilip * small differences. 638146024Sphilip * 639146024Sphilip * XXX: there must be a prettier way to do this! 640146024Sphilip */ 641146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 642146024Sphilip (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || 643146024Sphilip strncmp(Obj->String.Pointer, "S1N", 3) == 0)) 644146024Sphilip goto good; 645146024Sphilip else if (strncmp(model->name, "A1x", 3) == 0 && 646146024Sphilip strncmp(Obj->String.Pointer, "A1", 2) == 0) 647146024Sphilip goto good; 648146024Sphilip else if (strncmp(model->name, "A2x", 3) == 0 && 649146024Sphilip strncmp(Obj->String.Pointer, "A2", 2) == 0) 650146024Sphilip goto good; 651190695Sattilio else if (strncmp(model->name, "A3F", 3) == 0 && 652190695Sattilio strncmp(Obj->String.Pointer, "A6F", 3) == 0) 653190695Sattilio goto good; 654146024Sphilip else if (strncmp(model->name, "D1x", 3) == 0 && 655146024Sphilip strncmp(Obj->String.Pointer, "D1", 2) == 0) 656146024Sphilip goto good; 657146024Sphilip else if (strncmp(model->name, "L3H", 3) == 0 && 658146024Sphilip strncmp(Obj->String.Pointer, "L2E", 3) == 0) 659146024Sphilip goto good; 660146024Sphilip else if (strncmp(model->name, "L5x", 3) == 0 && 661146024Sphilip strncmp(Obj->String.Pointer, "L5", 2) == 0) 662146024Sphilip goto good; 663146024Sphilip else if (strncmp(model->name, "M2E", 3) == 0 && 664146024Sphilip (strncmp(Obj->String.Pointer, "M2", 2) == 0 || 665146024Sphilip strncmp(Obj->String.Pointer, "L4E", 3) == 0)) 666146024Sphilip goto good; 667146024Sphilip else if (strncmp(model->name, "S1x", 3) == 0 && 668146024Sphilip (strncmp(Obj->String.Pointer, "L8", 2) == 0 || 669146024Sphilip strncmp(Obj->String.Pointer, "S1", 2) == 0)) 670146024Sphilip goto good; 671146024Sphilip else if (strncmp(model->name, "S2x", 3) == 0 && 672146024Sphilip (strncmp(Obj->String.Pointer, "J1", 2) == 0 || 673146024Sphilip strncmp(Obj->String.Pointer, "S2", 2) == 0)) 674146024Sphilip goto good; 675128561Sphilip 676146024Sphilip /* L2B is like L3C but has no lcd_get method */ 677146024Sphilip else if (strncmp(model->name, "L3C", 3) == 0 && 678146024Sphilip strncmp(Obj->String.Pointer, "L2B", 3) == 0) { 679146024Sphilip model->lcd_get = NULL; 680146024Sphilip goto good; 681146024Sphilip } 682146024Sphilip 683146024Sphilip /* A3G is like M6R but with a different lcd_get method */ 684146024Sphilip else if (strncmp(model->name, "M6R", 3) == 0 && 685146024Sphilip strncmp(Obj->String.Pointer, "A3G", 3) == 0) { 686146024Sphilip model->lcd_get = "\\BLFG"; 687146024Sphilip goto good; 688146024Sphilip } 689146024Sphilip 690146024Sphilip /* M2N and W1N are like xxN with added WLED */ 691146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 692146024Sphilip (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || 693146024Sphilip strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { 694146024Sphilip model->wled_set = "WLED"; 695146024Sphilip goto good; 696146024Sphilip } 697146024Sphilip 698146024Sphilip /* M5N and S5N are like xxN without MLED */ 699146024Sphilip else if (strncmp(model->name, "xxN", 3) == 0 && 700146024Sphilip (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || 701146024Sphilip strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { 702146024Sphilip model->mled_set = NULL; 703146024Sphilip goto good; 704146024Sphilip } 705146024Sphilip } 706146024Sphilip 707137632Sphilip sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 708137632Sphilip sbuf_finish(sb); 709128561Sphilip 710209066Sjkim device_printf(dev, "%s", sbuf_data(sb)); 711137632Sphilip 712137632Sphilip sbuf_delete(sb); 713137632Sphilip AcpiOsFree(Buf.Pointer); 714137632Sphilip 715128561Sphilip return (ENXIO); 716128561Sphilip} 717128561Sphilip 718128561Sphilipstatic int 719128561Sphilipacpi_asus_attach(device_t dev) 720128561Sphilip{ 721128561Sphilip struct acpi_asus_softc *sc; 722128561Sphilip struct acpi_softc *acpi_sc; 723128561Sphilip 724128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 725128561Sphilip 726128561Sphilip sc = device_get_softc(dev); 727128561Sphilip acpi_sc = acpi_device_get_parent_softc(dev); 728128561Sphilip 729128561Sphilip /* Build sysctl tree */ 730128561Sphilip sysctl_ctx_init(&sc->sysctl_ctx); 731128561Sphilip sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 732128561Sphilip SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 733128561Sphilip OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 734128561Sphilip 735143894Sphilip /* Hook up nodes */ 736143894Sphilip for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 737143894Sphilip if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 738143894Sphilip continue; 739143894Sphilip 740273377Shselasky if (acpi_asus_sysctls[i].flag_anybody != 0) { 741273377Shselasky SYSCTL_ADD_PROC(&sc->sysctl_ctx, 742273377Shselasky SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 743273377Shselasky acpi_asus_sysctls[i].name, 744273377Shselasky CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 745273377Shselasky sc, i, acpi_asus_sysctl, "I", 746273377Shselasky acpi_asus_sysctls[i].description); 747273377Shselasky } else { 748273377Shselasky SYSCTL_ADD_PROC(&sc->sysctl_ctx, 749273377Shselasky SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 750273377Shselasky acpi_asus_sysctls[i].name, 751273377Shselasky CTLTYPE_INT | CTLFLAG_RW, 752273377Shselasky sc, i, acpi_asus_sysctl, "I", 753273377Shselasky acpi_asus_sysctls[i].description); 754273377Shselasky } 755143894Sphilip } 756143894Sphilip 757128561Sphilip /* Attach leds */ 758146022Sphilip if (sc->model->bled_set) { 759146022Sphilip sc->s_bled.busy = 0; 760146022Sphilip sc->s_bled.sc = sc; 761146022Sphilip sc->s_bled.type = ACPI_ASUS_LED_BLED; 762146022Sphilip sc->s_bled.cdev = 763178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_bled, 764178069Sjkim "bled", 1); 765146022Sphilip } 766146022Sphilip 767178069Sjkim if (sc->model->dled_set) { 768178069Sjkim sc->s_dled.busy = 0; 769178069Sjkim sc->s_dled.sc = sc; 770178069Sjkim sc->s_dled.type = ACPI_ASUS_LED_DLED; 771178069Sjkim sc->s_dled.cdev = 772178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled"); 773178069Sjkim } 774178069Sjkim 775178069Sjkim if (sc->model->gled_set) { 776178069Sjkim sc->s_gled.busy = 0; 777178069Sjkim sc->s_gled.sc = sc; 778178069Sjkim sc->s_gled.type = ACPI_ASUS_LED_GLED; 779178069Sjkim sc->s_gled.cdev = 780178069Sjkim led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled"); 781178069Sjkim } 782178069Sjkim 783133095Sphilip if (sc->model->mled_set) { 784144339Sphilip sc->s_mled.busy = 0; 785144339Sphilip sc->s_mled.sc = sc; 786133095Sphilip sc->s_mled.type = ACPI_ASUS_LED_MLED; 787133095Sphilip sc->s_mled.cdev = 788133095Sphilip led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 789133095Sphilip } 790128561Sphilip 791133095Sphilip if (sc->model->tled_set) { 792144339Sphilip sc->s_tled.busy = 0; 793144339Sphilip sc->s_tled.sc = sc; 794133095Sphilip sc->s_tled.type = ACPI_ASUS_LED_TLED; 795133095Sphilip sc->s_tled.cdev = 796178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_tled, 797178069Sjkim "tled", 1); 798133095Sphilip } 799128561Sphilip 800133095Sphilip if (sc->model->wled_set) { 801144339Sphilip sc->s_wled.busy = 0; 802144339Sphilip sc->s_wled.sc = sc; 803133095Sphilip sc->s_wled.type = ACPI_ASUS_LED_WLED; 804133095Sphilip sc->s_wled.cdev = 805178069Sjkim led_create_state((led_t *)acpi_asus_led, &sc->s_wled, 806178069Sjkim "wled", 1); 807133095Sphilip } 808128561Sphilip 809128561Sphilip /* Activate hotkeys */ 810128561Sphilip AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 811128561Sphilip 812128561Sphilip /* Handle notifies */ 813180062Srpaulo if (sc->model->n_func == NULL) 814180062Srpaulo sc->model->n_func = acpi_asus_notify; 815180062Srpaulo 816132610Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 817180062Srpaulo sc->model->n_func, dev); 818137631Sphilip 819184625Srpaulo /* Find and hook the 'LCDD' object */ 820184625Srpaulo if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) { 821184625Srpaulo ACPI_STATUS res; 822184625Srpaulo 823184625Srpaulo sc->lcdd_handle = NULL; 824184625Srpaulo res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ? 825184625Srpaulo NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle)); 826184625Srpaulo if (ACPI_SUCCESS(res)) { 827184625Srpaulo AcpiInstallNotifyHandler((sc->lcdd_handle), 828184625Srpaulo ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev); 829184625Srpaulo } else { 830184625Srpaulo printf("%s: unable to find LCD device '%s'\n", 831184625Srpaulo __func__, sc->model->lcdd); 832184625Srpaulo } 833184625Srpaulo } 834184625Srpaulo 835128561Sphilip return (0); 836128561Sphilip} 837128561Sphilip 838128561Sphilipstatic int 839128561Sphilipacpi_asus_detach(device_t dev) 840128561Sphilip{ 841128561Sphilip struct acpi_asus_softc *sc; 842137631Sphilip 843128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 844128561Sphilip 845128561Sphilip sc = device_get_softc(dev); 846128561Sphilip 847128561Sphilip /* Turn the lights off */ 848146022Sphilip if (sc->model->bled_set) 849146022Sphilip led_destroy(sc->s_bled.cdev); 850146022Sphilip 851178069Sjkim if (sc->model->dled_set) 852178069Sjkim led_destroy(sc->s_dled.cdev); 853178069Sjkim 854178069Sjkim if (sc->model->gled_set) 855178069Sjkim led_destroy(sc->s_gled.cdev); 856178069Sjkim 857128561Sphilip if (sc->model->mled_set) 858133095Sphilip led_destroy(sc->s_mled.cdev); 859128561Sphilip 860128561Sphilip if (sc->model->tled_set) 861133095Sphilip led_destroy(sc->s_tled.cdev); 862128561Sphilip 863128561Sphilip if (sc->model->wled_set) 864133095Sphilip led_destroy(sc->s_wled.cdev); 865128561Sphilip 866128561Sphilip /* Remove notify handler */ 867132610Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 868132610Snjl acpi_asus_notify); 869184625Srpaulo 870184625Srpaulo if (sc->lcdd_handle) { 871184625Srpaulo KASSERT(sc->model->lcdd_n_func != NULL, 872184625Srpaulo ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero")); 873184625Srpaulo AcpiRemoveNotifyHandler((sc->lcdd_handle), 874184625Srpaulo ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func); 875184625Srpaulo } 876128561Sphilip 877128561Sphilip /* Free sysctl tree */ 878128561Sphilip sysctl_ctx_free(&sc->sysctl_ctx); 879128561Sphilip 880128561Sphilip return (0); 881128561Sphilip} 882128561Sphilip 883128561Sphilipstatic void 884144339Sphilipacpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 885128561Sphilip{ 886128561Sphilip struct acpi_asus_softc *sc; 887133095Sphilip char *method; 888144339Sphilip int state; 889144339Sphilip 890128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 891128561Sphilip 892144339Sphilip sc = led->sc; 893128561Sphilip 894133095Sphilip switch (led->type) { 895146022Sphilip case ACPI_ASUS_LED_BLED: 896146022Sphilip method = sc->model->bled_set; 897146022Sphilip state = led->state; 898146022Sphilip break; 899178069Sjkim case ACPI_ASUS_LED_DLED: 900178069Sjkim method = sc->model->dled_set; 901178069Sjkim state = led->state; 902178069Sjkim break; 903178069Sjkim case ACPI_ASUS_LED_GLED: 904178069Sjkim method = sc->model->gled_set; 905178069Sjkim state = led->state + 1; /* 1: off, 2: on */ 906178069Sjkim break; 907143894Sphilip case ACPI_ASUS_LED_MLED: 908143894Sphilip method = sc->model->mled_set; 909178069Sjkim state = !led->state; /* inverted */ 910143894Sphilip break; 911143894Sphilip case ACPI_ASUS_LED_TLED: 912143894Sphilip method = sc->model->tled_set; 913144339Sphilip state = led->state; 914143894Sphilip break; 915143894Sphilip case ACPI_ASUS_LED_WLED: 916143894Sphilip method = sc->model->wled_set; 917144339Sphilip state = led->state; 918143894Sphilip break; 919143894Sphilip default: 920143894Sphilip printf("acpi_asus_led: invalid LED type %d\n", 921143894Sphilip (int)led->type); 922143894Sphilip return; 923133095Sphilip } 924128561Sphilip 925133095Sphilip acpi_SetInteger(sc->handle, method, state); 926144339Sphilip led->busy = 0; 927128561Sphilip} 928144339Sphilip 929144339Sphilipstatic void 930144339Sphilipacpi_asus_led(struct acpi_asus_led *led, int state) 931144339Sphilip{ 932128561Sphilip 933144339Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 934144339Sphilip 935144339Sphilip if (led->busy) 936144339Sphilip return; 937144339Sphilip 938144339Sphilip led->busy = 1; 939144339Sphilip led->state = state; 940144339Sphilip 941167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led); 942144339Sphilip} 943144339Sphilip 944128561Sphilipstatic int 945143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 946128561Sphilip{ 947128561Sphilip struct acpi_asus_softc *sc; 948143894Sphilip int arg; 949143894Sphilip int error = 0; 950143894Sphilip int function; 951143894Sphilip int method; 952143894Sphilip 953128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 954128561Sphilip 955128561Sphilip sc = (struct acpi_asus_softc *)oidp->oid_arg1; 956143894Sphilip function = oidp->oid_arg2; 957143894Sphilip method = acpi_asus_sysctls[function].method; 958143894Sphilip 959133628Snjl ACPI_SERIAL_BEGIN(asus); 960143894Sphilip arg = acpi_asus_sysctl_get(sc, method); 961143894Sphilip error = sysctl_handle_int(oidp, &arg, 0, req); 962128561Sphilip 963128561Sphilip /* Sanity check */ 964143894Sphilip if (error != 0 || req->newptr == NULL) 965133092Snjl goto out; 966128561Sphilip 967143894Sphilip /* Update */ 968143894Sphilip error = acpi_asus_sysctl_set(sc, method, arg); 969128561Sphilip 970143894Sphilipout: 971143894Sphilip ACPI_SERIAL_END(asus); 972143894Sphilip return (error); 973143894Sphilip} 974128561Sphilip 975143894Sphilipstatic int 976143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 977143894Sphilip{ 978143894Sphilip int val = 0; 979128561Sphilip 980143894Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 981143894Sphilip ACPI_SERIAL_ASSERT(asus); 982143894Sphilip 983143894Sphilip switch (method) { 984143894Sphilip case ACPI_ASUS_METHOD_BRN: 985143894Sphilip val = sc->s_brn; 986143894Sphilip break; 987143894Sphilip case ACPI_ASUS_METHOD_DISP: 988143894Sphilip val = sc->s_disp; 989143894Sphilip break; 990143894Sphilip case ACPI_ASUS_METHOD_LCD: 991143894Sphilip val = sc->s_lcd; 992143894Sphilip break; 993180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 994180062Srpaulo val = sc->s_cam; 995180062Srpaulo break; 996180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 997180062Srpaulo val = sc->s_crd; 998180062Srpaulo break; 999180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1000180268Srpaulo val = sc->s_wlan; 1001180268Srpaulo break; 1002128561Sphilip } 1003128561Sphilip 1004143894Sphilip return (val); 1005128561Sphilip} 1006128561Sphilip 1007128561Sphilipstatic int 1008143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 1009128561Sphilip{ 1010180268Srpaulo ACPI_STATUS status = AE_OK; 1011180268Srpaulo ACPI_OBJECT_LIST acpiargs; 1012186529Sstas ACPI_OBJECT acpiarg[1]; 1013128561Sphilip 1014128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1015143894Sphilip ACPI_SERIAL_ASSERT(asus); 1016128561Sphilip 1017180268Srpaulo acpiargs.Count = 1; 1018180268Srpaulo acpiargs.Pointer = acpiarg; 1019180268Srpaulo acpiarg[0].Type = ACPI_TYPE_INTEGER; 1020180268Srpaulo acpiarg[0].Integer.Value = arg; 1021180268Srpaulo 1022143894Sphilip switch (method) { 1023143894Sphilip case ACPI_ASUS_METHOD_BRN: 1024143894Sphilip if (arg < 0 || arg > 15) 1025143894Sphilip return (EINVAL); 1026128561Sphilip 1027143894Sphilip if (sc->model->brn_set) 1028143894Sphilip status = acpi_SetInteger(sc->handle, 1029143894Sphilip sc->model->brn_set, arg); 1030143894Sphilip else { 1031143894Sphilip while (arg != 0) { 1032143894Sphilip status = AcpiEvaluateObject(sc->handle, 1033143894Sphilip (arg > 0) ? sc->model->brn_up : 1034143894Sphilip sc->model->brn_dn, NULL, NULL); 1035143894Sphilip (arg > 0) ? arg-- : arg++; 1036143894Sphilip } 1037143894Sphilip } 1038128561Sphilip 1039143894Sphilip if (ACPI_SUCCESS(status)) 1040143894Sphilip sc->s_brn = arg; 1041128561Sphilip 1042143894Sphilip break; 1043143894Sphilip case ACPI_ASUS_METHOD_DISP: 1044143894Sphilip if (arg < 0 || arg > 7) 1045143894Sphilip return (EINVAL); 1046128561Sphilip 1047143894Sphilip status = acpi_SetInteger(sc->handle, 1048143894Sphilip sc->model->disp_set, arg); 1049128561Sphilip 1050143894Sphilip if (ACPI_SUCCESS(status)) 1051143894Sphilip sc->s_disp = arg; 1052128561Sphilip 1053143894Sphilip break; 1054143894Sphilip case ACPI_ASUS_METHOD_LCD: 1055143894Sphilip if (arg < 0 || arg > 1) 1056143894Sphilip return (EINVAL); 1057143894Sphilip 1058143894Sphilip if (strncmp(sc->model->name, "L3H", 3) != 0) 1059143894Sphilip status = AcpiEvaluateObject(sc->handle, 1060143894Sphilip sc->model->lcd_set, NULL, NULL); 1061143894Sphilip else 1062143894Sphilip status = acpi_SetInteger(sc->handle, 1063143894Sphilip sc->model->lcd_set, 0x7); 1064143894Sphilip 1065143894Sphilip if (ACPI_SUCCESS(status)) 1066143894Sphilip sc->s_lcd = arg; 1067143894Sphilip 1068143894Sphilip break; 1069180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1070180062Srpaulo if (arg < 0 || arg > 1) 1071180062Srpaulo return (EINVAL); 1072180062Srpaulo 1073180062Srpaulo status = AcpiEvaluateObject(sc->handle, 1074180268Srpaulo sc->model->cam_set, &acpiargs, NULL); 1075180062Srpaulo 1076180062Srpaulo if (ACPI_SUCCESS(status)) 1077180062Srpaulo sc->s_cam = arg; 1078180062Srpaulo break; 1079180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1080180062Srpaulo if (arg < 0 || arg > 1) 1081180062Srpaulo return (EINVAL); 1082180062Srpaulo 1083180062Srpaulo status = AcpiEvaluateObject(sc->handle, 1084180268Srpaulo sc->model->crd_set, &acpiargs, NULL); 1085180062Srpaulo 1086180062Srpaulo if (ACPI_SUCCESS(status)) 1087180062Srpaulo sc->s_crd = arg; 1088180062Srpaulo break; 1089180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1090180268Srpaulo if (arg < 0 || arg > 1) 1091180268Srpaulo return (EINVAL); 1092180268Srpaulo 1093180268Srpaulo status = AcpiEvaluateObject(sc->handle, 1094180268Srpaulo sc->model->wlan_set, &acpiargs, NULL); 1095180268Srpaulo 1096180268Srpaulo if (ACPI_SUCCESS(status)) 1097180268Srpaulo sc->s_wlan = arg; 1098180268Srpaulo break; 1099143894Sphilip } 1100143894Sphilip 1101143894Sphilip return (0); 1102128561Sphilip} 1103128561Sphilip 1104128561Sphilipstatic int 1105143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 1106128561Sphilip{ 1107143894Sphilip ACPI_STATUS status; 1108128561Sphilip 1109143894Sphilip switch (method) { 1110143894Sphilip case ACPI_ASUS_METHOD_BRN: 1111143894Sphilip if (sc->model->brn_get) { 1112143894Sphilip /* GPLV/SPLV models */ 1113143894Sphilip status = acpi_GetInteger(sc->handle, 1114143894Sphilip sc->model->brn_get, &sc->s_brn); 1115143894Sphilip if (ACPI_SUCCESS(status)) 1116143894Sphilip return (TRUE); 1117143894Sphilip } else if (sc->model->brn_up) { 1118143894Sphilip /* Relative models */ 1119143894Sphilip status = AcpiEvaluateObject(sc->handle, 1120143894Sphilip sc->model->brn_up, NULL, NULL); 1121143894Sphilip if (ACPI_FAILURE(status)) 1122143894Sphilip return (FALSE); 1123128561Sphilip 1124143894Sphilip status = AcpiEvaluateObject(sc->handle, 1125143894Sphilip sc->model->brn_dn, NULL, NULL); 1126143894Sphilip if (ACPI_FAILURE(status)) 1127143894Sphilip return (FALSE); 1128128561Sphilip 1129143894Sphilip return (TRUE); 1130143894Sphilip } 1131143894Sphilip return (FALSE); 1132143894Sphilip case ACPI_ASUS_METHOD_DISP: 1133143894Sphilip if (sc->model->disp_get) { 1134143894Sphilip status = acpi_GetInteger(sc->handle, 1135143894Sphilip sc->model->disp_get, &sc->s_disp); 1136143894Sphilip if (ACPI_SUCCESS(status)) 1137143894Sphilip return (TRUE); 1138143894Sphilip } 1139143894Sphilip return (FALSE); 1140143894Sphilip case ACPI_ASUS_METHOD_LCD: 1141178069Sjkim if (sc->model->lcd_get) { 1142203811Sjkim if (strncmp(sc->model->name, "L3H", 3) == 0) { 1143178069Sjkim ACPI_BUFFER Buf; 1144178069Sjkim ACPI_OBJECT Arg[2], Obj; 1145178069Sjkim ACPI_OBJECT_LIST Args; 1146128561Sphilip 1147178069Sjkim /* L3H is a bit special */ 1148178069Sjkim Arg[0].Type = ACPI_TYPE_INTEGER; 1149178069Sjkim Arg[0].Integer.Value = 0x02; 1150178069Sjkim Arg[1].Type = ACPI_TYPE_INTEGER; 1151178069Sjkim Arg[1].Integer.Value = 0x03; 1152128561Sphilip 1153178069Sjkim Args.Count = 2; 1154178069Sjkim Args.Pointer = Arg; 1155178069Sjkim 1156178069Sjkim Buf.Length = sizeof(Obj); 1157178069Sjkim Buf.Pointer = &Obj; 1158178069Sjkim 1159178069Sjkim status = AcpiEvaluateObject(sc->handle, 1160178069Sjkim sc->model->lcd_get, &Args, &Buf); 1161178069Sjkim if (ACPI_SUCCESS(status) && 1162178069Sjkim Obj.Type == ACPI_TYPE_INTEGER) { 1163178069Sjkim sc->s_lcd = Obj.Integer.Value >> 8; 1164178069Sjkim return (TRUE); 1165178069Sjkim } 1166178069Sjkim } else { 1167178069Sjkim status = acpi_GetInteger(sc->handle, 1168178069Sjkim sc->model->lcd_get, &sc->s_lcd); 1169178069Sjkim if (ACPI_SUCCESS(status)) 1170178069Sjkim return (TRUE); 1171143894Sphilip } 1172143894Sphilip } 1173143894Sphilip return (FALSE); 1174180062Srpaulo case ACPI_ASUS_METHOD_CAMERA: 1175180062Srpaulo if (sc->model->cam_get) { 1176180062Srpaulo status = acpi_GetInteger(sc->handle, 1177180062Srpaulo sc->model->cam_get, &sc->s_cam); 1178180062Srpaulo if (ACPI_SUCCESS(status)) 1179180062Srpaulo return (TRUE); 1180180062Srpaulo } 1181180062Srpaulo return (FALSE); 1182180062Srpaulo case ACPI_ASUS_METHOD_CARDRD: 1183180062Srpaulo if (sc->model->crd_get) { 1184180062Srpaulo status = acpi_GetInteger(sc->handle, 1185180062Srpaulo sc->model->crd_get, &sc->s_crd); 1186180062Srpaulo if (ACPI_SUCCESS(status)) 1187180062Srpaulo return (TRUE); 1188180062Srpaulo } 1189180062Srpaulo return (FALSE); 1190180268Srpaulo case ACPI_ASUS_METHOD_WLAN: 1191180268Srpaulo if (sc->model->wlan_get) { 1192180268Srpaulo status = acpi_GetInteger(sc->handle, 1193180268Srpaulo sc->model->wlan_get, &sc->s_wlan); 1194180268Srpaulo if (ACPI_SUCCESS(status)) 1195180268Srpaulo return (TRUE); 1196180268Srpaulo } 1197180268Srpaulo return (FALSE); 1198143894Sphilip } 1199143894Sphilip return (FALSE); 1200128561Sphilip} 1201128561Sphilip 1202128561Sphilipstatic void 1203128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1204128561Sphilip{ 1205128561Sphilip struct acpi_asus_softc *sc; 1206128561Sphilip struct acpi_softc *acpi_sc; 1207128561Sphilip 1208128561Sphilip ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1209128561Sphilip 1210128561Sphilip sc = device_get_softc((device_t)context); 1211128561Sphilip acpi_sc = acpi_device_get_parent_softc(sc->dev); 1212128561Sphilip 1213133628Snjl ACPI_SERIAL_BEGIN(asus); 1214128561Sphilip if ((notify & ~0x10) <= 15) { 1215132610Snjl sc->s_brn = notify & ~0x10; 1216128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1217128561Sphilip } else if ((notify & ~0x20) <= 15) { 1218132610Snjl sc->s_brn = notify & ~0x20; 1219128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1220128561Sphilip } else if (notify == 0x33) { 1221128561Sphilip sc->s_lcd = 1; 1222128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 1223128561Sphilip } else if (notify == 0x34) { 1224128561Sphilip sc->s_lcd = 0; 1225128561Sphilip ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 1226184625Srpaulo } else if (notify == 0x86) { 1227184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1228184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1229184625Srpaulo } else if (notify == 0x87) { 1230184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1231184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1232128561Sphilip } else { 1233128561Sphilip /* Notify devd(8) */ 1234128561Sphilip acpi_UserNotify("ASUS", h, notify); 1235128561Sphilip } 1236133628Snjl ACPI_SERIAL_END(asus); 1237128561Sphilip} 1238180062Srpaulo 1239180062Srpaulostatic void 1240184625Srpauloacpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1241184625Srpaulo{ 1242184625Srpaulo struct acpi_asus_softc *sc; 1243184625Srpaulo struct acpi_softc *acpi_sc; 1244184625Srpaulo 1245184625Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1246184625Srpaulo 1247184625Srpaulo sc = device_get_softc((device_t)context); 1248184625Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1249184625Srpaulo 1250184625Srpaulo ACPI_SERIAL_BEGIN(asus); 1251184625Srpaulo switch (notify) { 1252184625Srpaulo case 0x87: 1253184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1254184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1255184625Srpaulo break; 1256184625Srpaulo case 0x86: 1257184625Srpaulo acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1258184625Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1259184625Srpaulo break; 1260184625Srpaulo } 1261184625Srpaulo ACPI_SERIAL_END(asus); 1262184625Srpaulo} 1263184625Srpaulo 1264184625Srpaulostatic void 1265180062Srpauloacpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1266180062Srpaulo{ 1267180062Srpaulo struct acpi_asus_softc *sc; 1268180062Srpaulo struct acpi_softc *acpi_sc; 1269180062Srpaulo 1270180062Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1271180062Srpaulo 1272180062Srpaulo sc = device_get_softc((device_t)context); 1273180062Srpaulo acpi_sc = acpi_device_get_parent_softc(sc->dev); 1274180062Srpaulo 1275180062Srpaulo ACPI_SERIAL_BEGIN(asus); 1276180062Srpaulo if ((notify & ~0x20) <= 15) { 1277180062Srpaulo sc->s_brn = notify & ~0x20; 1278180062Srpaulo ACPI_VPRINT(sc->dev, acpi_sc, 1279180062Srpaulo "Brightness increased/decreased\n"); 1280180062Srpaulo } else { 1281180062Srpaulo /* Notify devd(8) */ 1282180062Srpaulo acpi_UserNotify("ASUS-Eee", h, notify); 1283180062Srpaulo } 1284180062Srpaulo ACPI_SERIAL_END(asus); 1285180062Srpaulo} 1286