Path: blob/main/sys/dev/acpi_support/acpi_fujitsu.c
39536 views
/*-1* Copyright (c) 2002 Sean Bullington <seanATstalker.org>2* 2003-2008 Anish Mistry <[email protected]>3* 2004 Mark Santcroos <[email protected]>4* All Rights Reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*27*/2829#include <sys/cdefs.h>30#include "opt_acpi.h"31#include <sys/param.h>32#include <sys/kernel.h>33#include <sys/bus.h>34#include <sys/module.h>35#include <sys/sysctl.h>3637#include <contrib/dev/acpica/include/acpi.h>38#include <contrib/dev/acpica/include/accommon.h>3940#include <dev/acpica/acpivar.h>4142/* Hooks for the ACPI CA debugging infrastructure */43#define _COMPONENT ACPI_OEM44ACPI_MODULE_NAME("Fujitsu")4546/* Change and update bits for the hotkeys */47#define VOLUME_MUTE_BIT 0x400000004849/* Values of settings */50#define GENERAL_SETTING_BITS 0x0fffffff51#define MOUSE_SETTING_BITS GENERAL_SETTING_BITS52#define VOLUME_SETTING_BITS GENERAL_SETTING_BITS53#define BRIGHTNESS_SETTING_BITS GENERAL_SETTING_BITS5455/* Possible state changes */56/*57* These are NOT arbitrary values. They are the58* GHKS return value from the device that says which59* hotkey is active. They should match up with a bit60* from the GSIF bitmask.61*/62#define BRIGHT_CHANGED 0x0163#define VOLUME_CHANGED 0x0464#define MOUSE_CHANGED 0x0865/*66* It is unknown which hotkey this bit is supposed to indicate, but67* according to values from GSIF this is a valid flag.68*/69#define UNKNOWN_CHANGED 0x107071/* sysctl values */72#define FN_MUTE 073#define FN_POINTER_ENABLE 174#define FN_LCD_BRIGHTNESS 275#define FN_VOLUME 37677/* Methods */78#define METHOD_GBLL 179#define METHOD_GMOU 280#define METHOD_GVOL 381#define METHOD_MUTE 482#define METHOD_RBLL 583#define METHOD_RVOL 684#define METHOD_GSIF 785#define METHOD_GHKS 886#define METHOD_GBLS 98788/* Notify event */89#define ACPI_NOTIFY_STATUS_CHANGED 0x809091/*92* Holds a control method name and its associated integer value.93* Only used for no-argument control methods which return a value.94*/95struct int_nameval {96char *name;97int value;98int exists;99};100101/*102* Driver extension for the FUJITSU ACPI driver.103*/104struct acpi_fujitsu_softc {105device_t dev;106ACPI_HANDLE handle;107108/* Control methods */109struct int_nameval _sta, /* unused */110gbll, /* brightness */111gbls, /* get brightness state */112ghks, /* hotkey selector */113gbuf, /* unused (buffer?) */114gmou, /* mouse */115gsif, /* function key bitmask */116gvol, /* volume */117rbll, /* number of brightness levels (radix) */118rvol; /* number of volume levels (radix) */119120/* State variables */121uint8_t bIsMuted; /* Is volume muted */122uint8_t bIntPtrEnabled; /* Is internal ptr enabled */123uint32_t lastValChanged; /* The last value updated */124125/* sysctl tree */126struct sysctl_ctx_list sysctl_ctx;127struct sysctl_oid *sysctl_tree;128};129130/* Driver entry point forward declarations. */131static int acpi_fujitsu_probe(device_t dev);132static int acpi_fujitsu_attach(device_t dev);133static int acpi_fujitsu_detach(device_t dev);134static int acpi_fujitsu_suspend(device_t dev);135static int acpi_fujitsu_resume(device_t dev);136137static void acpi_fujitsu_notify_status_changed(void *arg);138static void acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context);139static int acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS);140141/* Utility function declarations */142static uint8_t acpi_fujitsu_update(struct acpi_fujitsu_softc *sc);143static uint8_t acpi_fujitsu_init(struct acpi_fujitsu_softc *sc);144static uint8_t acpi_fujitsu_check_hardware(struct acpi_fujitsu_softc *sc);145146/* Driver/Module specific structure definitions. */147static device_method_t acpi_fujitsu_methods[] = {148/* Device interface */149DEVMETHOD(device_probe, acpi_fujitsu_probe),150DEVMETHOD(device_attach, acpi_fujitsu_attach),151DEVMETHOD(device_detach, acpi_fujitsu_detach),152DEVMETHOD(device_suspend, acpi_fujitsu_suspend),153DEVMETHOD(device_resume, acpi_fujitsu_resume),154155DEVMETHOD_END156};157158static driver_t acpi_fujitsu_driver = {159"acpi_fujitsu",160acpi_fujitsu_methods,161sizeof(struct acpi_fujitsu_softc),162};163164/* Prototype for function hotkeys for getting/setting a value. */165static int acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method);166static int acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value);167168static char *fujitsu_ids[] = { "FUJ02B1", NULL };169170ACPI_SERIAL_DECL(fujitsu, "Fujitsu Function Hotkeys");171172/* sysctl names and function calls */173static struct {174char *name;175int method;176char *description;177} sysctl_table[] = {178{179.name = "mute",180.method = METHOD_MUTE,181.description = "Speakers/headphones mute status"182},183{184.name = "pointer_enable",185.method = METHOD_GMOU,186.description = "Enable and disable the internal pointer"187},188{189.name = "lcd_brightness",190.method = METHOD_GBLL,191.description = "Brightness level of the LCD panel"192},193{194.name = "lcd_brightness",195.method = METHOD_GBLS,196.description = "Brightness level of the LCD panel"197},198{199.name = "volume",200.method = METHOD_GVOL,201.description = "Speakers/headphones volume level"202},203{204.name = "volume_radix",205.method = METHOD_RVOL,206.description = "Number of volume level steps"207},208{209.name = "lcd_brightness_radix",210.method = METHOD_RBLL,211.description = "Number of brightness level steps"212},213{ NULL, 0, NULL }214};215216DRIVER_MODULE(acpi_fujitsu, acpi, acpi_fujitsu_driver, 0, 0);217MODULE_DEPEND(acpi_fujitsu, acpi, 1, 1, 1);218MODULE_VERSION(acpi_fujitsu, 1);219220static int221acpi_fujitsu_probe(device_t dev)222{223char *name;224int rv;225226rv = ACPI_ID_PROBE(device_get_parent(dev), dev, fujitsu_ids, &name);227if (acpi_disabled("fujitsu") || rv > 0 || device_get_unit(dev) > 1)228return (ENXIO);229device_set_descf(dev, "Fujitsu Function Hotkeys %s", name);230231return (rv);232}233234static int235acpi_fujitsu_attach(device_t dev)236{237struct acpi_fujitsu_softc *sc;238239ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);240241sc = device_get_softc(dev);242sc->dev = dev;243sc->handle = acpi_get_handle(dev);244245/* Install notification handler */246AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,247acpi_fujitsu_notify_handler, sc);248249/* Snag our default values for the hotkeys / hotkey states. */250ACPI_SERIAL_BEGIN(fujitsu);251if (!acpi_fujitsu_init(sc))252device_printf(dev, "Couldn't initialize hotkey states!\n");253ACPI_SERIAL_END(fujitsu);254255return (0);256}257258/*259* Called when the system is being suspended, simply260* set an event to be signalled when we wake up.261*/262static int263acpi_fujitsu_suspend(device_t dev)264{265266return (0);267}268269static int270acpi_fujitsu_resume(device_t dev)271{272struct acpi_fujitsu_softc *sc;273ACPI_STATUS status;274275sc = device_get_softc(dev);276277/*278* The pointer needs to be re-enabled for279* some revisions of the P series (2120).280*/281ACPI_SERIAL_BEGIN(fujitsu);282283if(sc->gmou.exists) {284status = acpi_SetInteger(sc->handle, "SMOU", 1);285if (ACPI_FAILURE(status))286device_printf(sc->dev, "Couldn't enable pointer\n");287}288ACPI_SERIAL_END(fujitsu);289290return (0);291}292293static void294acpi_fujitsu_notify_status_changed(void *arg)295{296struct acpi_fujitsu_softc *sc;297298ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);299300sc = (struct acpi_fujitsu_softc *)arg;301302/*303* Since our notify function is called, we know something has304* happened. So the only reason for acpi_fujitsu_update to fail305* is if we can't find what has changed or an error occurs.306*/307ACPI_SERIAL_BEGIN(fujitsu);308acpi_fujitsu_update(sc);309ACPI_SERIAL_END(fujitsu);310}311312static void313acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context)314{315struct acpi_fujitsu_softc *sc;316317ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);318319sc = (struct acpi_fujitsu_softc *)context;320321switch (notify) {322case ACPI_NOTIFY_STATUS_CHANGED:323AcpiOsExecute(OSL_NOTIFY_HANDLER,324acpi_fujitsu_notify_status_changed, sc);325break;326default:327/* unknown notification value */328break;329}330}331332static int333acpi_fujitsu_detach(device_t dev)334{335struct acpi_fujitsu_softc *sc;336337sc = device_get_softc(dev);338AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,339acpi_fujitsu_notify_handler);340341sysctl_ctx_free(&sc->sysctl_ctx);342343return (0);344}345346/*347* Initializes the names of the ACPI control methods and grabs348* the current state of all of the ACPI hotkeys into the softc.349*/350static uint8_t351acpi_fujitsu_init(struct acpi_fujitsu_softc *sc)352{353struct acpi_softc *acpi_sc;354int i, exists;355356ACPI_SERIAL_ASSERT(fujitsu);357358/* Setup all of the names for each control method */359sc->_sta.name = "_STA";360sc->gbll.name = "GBLL";361sc->gbls.name = "GBLS";362sc->ghks.name = "GHKS";363sc->gmou.name = "GMOU";364sc->gsif.name = "GSIF";365sc->gvol.name = "GVOL";366sc->ghks.name = "GHKS";367sc->gsif.name = "GSIF";368sc->rbll.name = "RBLL";369sc->rvol.name = "RVOL";370371/* Determine what hardware functionality is available */372acpi_fujitsu_check_hardware(sc);373374/* Build the sysctl tree */375acpi_sc = acpi_device_get_parent_softc(sc->dev);376sysctl_ctx_init(&sc->sysctl_ctx);377sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,378SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),379OID_AUTO, "fujitsu", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");380381for (i = 0; sysctl_table[i].name != NULL; i++) {382switch(sysctl_table[i].method) {383case METHOD_GMOU:384exists = sc->gmou.exists;385break;386case METHOD_GBLL:387exists = sc->gbll.exists;388break;389case METHOD_GBLS:390exists = sc->gbls.exists;391break;392case METHOD_GVOL:393case METHOD_MUTE:394exists = sc->gvol.exists;395break;396case METHOD_RVOL:397exists = sc->rvol.exists;398break;399case METHOD_RBLL:400exists = sc->rbll.exists;401break;402default:403/* Allow by default */404exists = 1;405break;406}407if(!exists)408continue;409SYSCTL_ADD_PROC(&sc->sysctl_ctx,410SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,411sysctl_table[i].name,412CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |413CTLFLAG_MPSAFE, sc, i, acpi_fujitsu_sysctl, "I",414sysctl_table[i].description);415}416417/* Set the hotkeys to their initial states */418if (!acpi_fujitsu_update(sc)) {419device_printf(sc->dev, "Couldn't init hotkey states\n");420return (FALSE);421}422423return (TRUE);424}425426static int427acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS)428{429struct acpi_fujitsu_softc *sc;430int method;431int arg;432int function_num, error = 0;433434sc = (struct acpi_fujitsu_softc *)oidp->oid_arg1;435function_num = oidp->oid_arg2;436method = sysctl_table[function_num].method;437438ACPI_SERIAL_BEGIN(fujitsu);439440/* Get the current value */441arg = acpi_fujitsu_method_get(sc, method);442error = sysctl_handle_int(oidp, &arg, 0, req);443444if (error != 0 || req->newptr == NULL)445goto out;446447/* Update the value */448error = acpi_fujitsu_method_set(sc, method, arg);449450out:451ACPI_SERIAL_END(fujitsu);452return (error);453}454455static int456acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method)457{458struct int_nameval nv;459ACPI_STATUS status;460461ACPI_SERIAL_ASSERT(fujitsu);462463switch (method) {464case METHOD_GBLL:465nv = sc->gbll;466break;467case METHOD_GBLS:468nv = sc->gbls;469break;470case METHOD_GMOU:471nv = sc->gmou;472break;473case METHOD_GVOL:474case METHOD_MUTE:475nv = sc->gvol;476break;477case METHOD_GHKS:478nv = sc->ghks;479break;480case METHOD_GSIF:481nv = sc->gsif;482break;483case METHOD_RBLL:484nv = sc->rbll;485break;486case METHOD_RVOL:487nv = sc->rvol;488break;489default:490return (FALSE);491}492493if(!nv.exists)494return (EINVAL);495496status = acpi_GetInteger(sc->handle, nv.name, &nv.value);497if (ACPI_FAILURE(status)) {498device_printf(sc->dev, "Couldn't query method (%s)\n", nv.name);499return (FALSE);500}501502if (method == METHOD_MUTE) {503sc->bIsMuted = (uint8_t)((nv.value & VOLUME_MUTE_BIT) != 0);504return (sc->bIsMuted);505}506507nv.value &= GENERAL_SETTING_BITS;508return (nv.value);509}510511static int512acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value)513{514struct int_nameval nv;515ACPI_STATUS status;516char *control;517int changed;518519ACPI_SERIAL_ASSERT(fujitsu);520521switch (method) {522case METHOD_GBLL:523changed = BRIGHT_CHANGED;524control = "SBLL";525nv = sc->gbll;526break;527case METHOD_GBLS:528changed = BRIGHT_CHANGED;529control = "SBL2";530nv = sc->gbls;531break;532case METHOD_GMOU:533changed = MOUSE_CHANGED;534control = "SMOU";535nv = sc->gmou;536break;537case METHOD_GVOL:538case METHOD_MUTE:539changed = VOLUME_CHANGED;540control = "SVOL";541nv = sc->gvol;542break;543default:544return (EINVAL);545}546547if(!nv.exists)548return (EINVAL);549550if (method == METHOD_MUTE) {551if (value == 1)552value = nv.value | VOLUME_MUTE_BIT;553else if (value == 0)554value = nv.value & ~VOLUME_MUTE_BIT;555else556return (EINVAL);557}558559status = acpi_SetInteger(sc->handle, control, value);560if (ACPI_FAILURE(status)) {561device_printf(sc->dev, "Couldn't update %s\n", control);562return (FALSE);563}564565sc->lastValChanged = changed;566return (0);567}568569/*570* Query the get methods to determine what functionality is available571* from the hardware function hotkeys.572*/573static uint8_t574acpi_fujitsu_check_hardware(struct acpi_fujitsu_softc *sc)575{576int val;577578ACPI_SERIAL_ASSERT(fujitsu);579/* save the hotkey bitmask */580if (ACPI_FAILURE(acpi_GetInteger(sc->handle,581sc->gsif.name, &(sc->gsif.value)))) {582sc->gsif.exists = 0;583device_printf(sc->dev, "Couldn't query bitmask value\n");584} else {585sc->gsif.exists = 1;586}587588/* System Volume Level */589if (ACPI_FAILURE(acpi_GetInteger(sc->handle,590sc->gvol.name, &val))) {591sc->gvol.exists = 0;592} else {593sc->gvol.exists = 1;594}595596if (ACPI_FAILURE(acpi_GetInteger(sc->handle,597sc->gbls.name, &val))) {598sc->gbls.exists = 0;599} else {600sc->gbls.exists = 1;601}602603// don't add if we can use the new method604if (sc->gbls.exists || ACPI_FAILURE(acpi_GetInteger(sc->handle,605sc->gbll.name, &val))) {606sc->gbll.exists = 0;607} else {608sc->gbll.exists = 1;609}610611if (ACPI_FAILURE(acpi_GetInteger(sc->handle,612sc->ghks.name, &val))) {613sc->ghks.exists = 0;614} else {615sc->ghks.exists = 1;616}617618if (ACPI_FAILURE(acpi_GetInteger(sc->handle,619sc->gmou.name, &val))) {620sc->gmou.exists = 0;621} else {622sc->gmou.exists = 1;623}624625if (ACPI_FAILURE(acpi_GetInteger(sc->handle,626sc->rbll.name, &val))) {627sc->rbll.exists = 0;628} else {629sc->rbll.exists = 1;630}631632if (ACPI_FAILURE(acpi_GetInteger(sc->handle,633sc->rvol.name, &val))) {634sc->rvol.exists = 0;635} else {636sc->rvol.exists = 1;637}638639return (TRUE);640}641642/*643* Query each of the ACPI control methods that contain information we're644* interested in. We check the return values from the control methods and645* adjust any state variables if they should be adjusted.646*/647static uint8_t648acpi_fujitsu_update(struct acpi_fujitsu_softc *sc)649{650int changed;651struct acpi_softc *acpi_sc;652653acpi_sc = acpi_device_get_parent_softc(sc->dev);654655ACPI_SERIAL_ASSERT(fujitsu);656if(sc->gsif.exists)657changed = sc->gsif.value & acpi_fujitsu_method_get(sc,METHOD_GHKS);658else659changed = 0;660661/* System Volume Level */662if(sc->gvol.exists) {663if (ACPI_FAILURE(acpi_GetInteger(sc->handle,664sc->gvol.name, &(sc->gvol.value)))) {665device_printf(sc->dev, "Couldn't query volume level\n");666return (FALSE);667}668669if (changed & VOLUME_CHANGED) {670sc->bIsMuted =671(uint8_t)((sc->gvol.value & VOLUME_MUTE_BIT) != 0);672673/* Clear the modification bit */674sc->gvol.value &= VOLUME_SETTING_BITS;675676if (sc->bIsMuted) {677acpi_UserNotify("FUJITSU", sc->handle, FN_MUTE);678ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now mute\n");679} else680ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now %d\n",681sc->gvol.value);682683acpi_UserNotify("FUJITSU", sc->handle, FN_VOLUME);684}685}686687/* Internal mouse pointer (eraserhead) */688if(sc->gmou.exists) {689if (ACPI_FAILURE(acpi_GetInteger(sc->handle,690sc->gmou.name, &(sc->gmou.value)))) {691device_printf(sc->dev, "Couldn't query pointer state\n");692return (FALSE);693}694695if (changed & MOUSE_CHANGED) {696sc->bIntPtrEnabled = (uint8_t)(sc->gmou.value & 0x1);697698/* Clear the modification bit */699sc->gmou.value &= MOUSE_SETTING_BITS;700701/* Set the value in case it is not hardware controlled */702acpi_fujitsu_method_set(sc, METHOD_GMOU, sc->gmou.value);703704acpi_UserNotify("FUJITSU", sc->handle, FN_POINTER_ENABLE);705706ACPI_VPRINT(sc->dev, acpi_sc, "Internal pointer is now %s\n",707(sc->bIntPtrEnabled) ? "enabled" : "disabled");708}709}710711/* Screen Brightness Level P8XXX */712if(sc->gbls.exists) {713if (ACPI_FAILURE(acpi_GetInteger(sc->handle,714sc->gbls.name, &(sc->gbls.value)))) {715device_printf(sc->dev, "Couldn't query P8XXX brightness level\n");716return (FALSE);717}718if (changed & BRIGHT_CHANGED) {719/* No state to record here. */720721/* Clear the modification bit */722sc->gbls.value &= BRIGHTNESS_SETTING_BITS;723724/* Set the value in case it is not hardware controlled */725acpi_fujitsu_method_set(sc, METHOD_GBLS, sc->gbls.value);726727acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS);728729ACPI_VPRINT(sc->dev, acpi_sc, "P8XXX Brightness level is now %d\n",730sc->gbls.value);731}732}733734/* Screen Brightness Level */735if(sc->gbll.exists) {736if (ACPI_FAILURE(acpi_GetInteger(sc->handle,737sc->gbll.name, &(sc->gbll.value)))) {738device_printf(sc->dev, "Couldn't query brightness level\n");739return (FALSE);740}741742if (changed & BRIGHT_CHANGED) {743/* No state to record here. */744745/* Clear the modification bit */746sc->gbll.value &= BRIGHTNESS_SETTING_BITS;747748acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS);749750ACPI_VPRINT(sc->dev, acpi_sc, "Brightness level is now %d\n",751sc->gbll.value);752}753}754755sc->lastValChanged = changed;756return (TRUE);757}758759760