diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index e5a2f4cbc3f1..972200daf005 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -4,6 +4,7 @@ MAN= aac.4 \ acpi.4 \ ${_acpi_asus.4} \ + ${_acpi_asus_wmi.4} \ ${_acpi_dock.4} \ ${_acpi_fujitsu.4} \ ${_acpi_hp.4} \ @@ -698,6 +699,7 @@ MLINKS+=zyd.4 if_zyd.4 .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" _acpi_asus.4= acpi_asus.4 +_acpi_asus_wmi.4= acpi_asus_wmi.4 _acpi_dock.4= acpi_dock.4 _acpi_fujitsu.4=acpi_fujitsu.4 _acpi_hp.4= acpi_hp.4 diff --git a/share/man/man4/acpi_asus.4 b/share/man/man4/acpi_asus.4 index f3b99daf5227..1921adc771a0 100644 --- a/share/man/man4/acpi_asus.4 +++ b/share/man/man4/acpi_asus.4 @@ -157,6 +157,7 @@ Defaults for these variables can be set in which is parsed at boot-time. .Sh SEE ALSO .Xr acpi 4 , +.Xr acpi_asus_wmi 4 , .Xr acpi_video 4 , .Xr sysctl.conf 5 , .Xr sysctl 8 diff --git a/share/man/man4/acpi_asus_wmi.4 b/share/man/man4/acpi_asus_wmi.4 new file mode 100644 index 000000000000..bbc565f06aae --- /dev/null +++ b/share/man/man4/acpi_asus_wmi.4 @@ -0,0 +1,90 @@ +.\" +.\" Copyright (c) 2012 Alexander Motin +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd July 2, 2012 +.Dt ACPI_ASUS_WMI 4 +.Os +.Sh NAME +.Nm acpi_asus_wmi +.Nd Asus Laptop WMI Extras +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device acpi_asus_wmi" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +acpi_asus_wmi_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for the extra WMI-controlled gadgets, such as hotkeys +and leds, found on Asus laptops. +It allows one to use the +.Xr sysctl 8 +interface to manipulate the brightness of the LCD panel and keyboard backlight, +power on/off different internal components, such as WiFi, Bluetooth, camera, +cardreader, etc, read some sensors. +Hotkey events are passed to +.Xr devd 8 +for easy handling in userspace with the default configuration in +.Pa /etc/devd/asus.conf . +Some hotkey events, such as keyboard backlight and touchpad control, are +handled inside the driver. +.Sh SYSCTL VARIABLES +The following sysctls are currently implemented: +.Bl -tag -width indent +.It Va dev.acpi_asus_wmi.0.handle_keys +Specifies whether driver should handle some harwdare keys, such as keyboard +backlight, internally. +.El +.Pp +Number of other variables under the same sysctl branch are model-specific. +.Pp +Defaults for these variables can be set in +.Xr sysctl.conf 5 , +which is parsed at boot-time. +.Sh SEE ALSO +.Xr acpi 4 , +.Xr acpi_asus 4 , +.Xr acpi_video 4 , +.Xr sysctl.conf 5 , +.Xr devd 8 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 10.0 . +.Sh AUTHORS +.An Alexander Motib Aq mav@FreeBSD.org . diff --git a/sys/conf/files b/sys/conf/files index 9bc5f2c10d82..c303e03004dd 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -576,6 +576,7 @@ dev/aac/aac_linux.c optional aac compat_linux dev/aac/aac_pci.c optional aac pci dev/acpi_support/acpi_wmi.c optional acpi_wmi acpi dev/acpi_support/acpi_asus.c optional acpi_asus acpi +dev/acpi_support/acpi_asus_wmi.c optional acpi_asus_wmi acpi dev/acpi_support/acpi_fujitsu.c optional acpi_fujitsu acpi dev/acpi_support/acpi_hp.c optional acpi_hp acpi dev/acpi_support/acpi_ibm.c optional acpi_ibm acpi diff --git a/sys/dev/acpi_support/acpi_asus_wmi.c b/sys/dev/acpi_support/acpi_asus_wmi.c new file mode 100644 index 000000000000..9e2a49809de2 --- /dev/null +++ b/sys/dev/acpi_support/acpi_asus_wmi.c @@ -0,0 +1,651 @@ +/*- + * Copyright (c) 2012 Alexander Motin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_acpi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "acpi_wmi_if.h" + +#define _COMPONENT ACPI_OEM +ACPI_MODULE_NAME("ASUS-WMI") + +#define ACPI_ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" +#define ACPI_ASUS_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" +#define ACPI_EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" + +/* WMI Methods */ +#define ASUS_WMI_METHODID_SPEC 0x43455053 +#define ASUS_WMI_METHODID_SFUN 0x4E554653 +#define ASUS_WMI_METHODID_DSTS 0x53544344 +#define ASUS_WMI_METHODID_DSTS2 0x53545344 +#define ASUS_WMI_METHODID_DEVS 0x53564544 +#define ASUS_WMI_METHODID_INIT 0x54494E49 +#define ASUS_WMI_METHODID_HKEY 0x59454B48 + +#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE + +/* Wireless */ +#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 +#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 +#define ASUS_WMI_DEVID_CWAP 0x00010003 +#define ASUS_WMI_DEVID_WLAN 0x00010011 +#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 +#define ASUS_WMI_DEVID_GPS 0x00010015 +#define ASUS_WMI_DEVID_WIMAX 0x00010017 +#define ASUS_WMI_DEVID_WWAN3G 0x00010019 +#define ASUS_WMI_DEVID_UWB 0x00010021 + +/* LEDs */ +#define ASUS_WMI_DEVID_LED1 0x00020011 +#define ASUS_WMI_DEVID_LED2 0x00020012 +#define ASUS_WMI_DEVID_LED3 0x00020013 +#define ASUS_WMI_DEVID_LED4 0x00020014 +#define ASUS_WMI_DEVID_LED5 0x00020015 +#define ASUS_WMI_DEVID_LED6 0x00020016 + +/* Backlight and Brightness */ +#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 +#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 +#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 +#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 + +/* Misc */ +#define ASUS_WMI_DEVID_CAMERA 0x00060013 +#define ASUS_WMI_DEVID_CARDREADER 0x00080013 +#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 +#define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 +#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 +#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 +#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 + +/* DSTS masks */ +#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 +#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 +#define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 +#define ASUS_WMI_DSTS_USER_BIT 0x00020000 +#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 +#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF +#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 + + +struct acpi_asus_wmi_softc { + device_t dev; + device_t wmi_dev; + const char *notify_guid; + struct sysctl_ctx_list *sysctl_ctx; + struct sysctl_oid *sysctl_tree; + int dsts_id; + int handle_keys; +}; + +static struct { + char *name; + int dev_id; + char *description; + int access; +} acpi_asus_wmi_sysctls[] = { + { + .name = "hw_switch", + .dev_id = ASUS_WMI_DEVID_HW_SWITCH, + .description = "hw_switch", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "wireless_led", + .dev_id = ASUS_WMI_DEVID_WIRELESS_LED, + .description = "Wireless LED control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "cwap", + .dev_id = ASUS_WMI_DEVID_CWAP, + .description = "Alt+F2 function", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "wlan", + .dev_id = ASUS_WMI_DEVID_WLAN, + .description = "WLAN power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "bluetooth", + .dev_id = ASUS_WMI_DEVID_BLUETOOTH, + .description = "Bluetooth power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "gps", + .dev_id = ASUS_WMI_DEVID_GPS, + .description = "GPS power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "wimax", + .dev_id = ASUS_WMI_DEVID_WIMAX, + .description = "WiMAX power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "wwan3g", + .dev_id = ASUS_WMI_DEVID_WWAN3G, + .description = "WWAN-3G power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "uwb", + .dev_id = ASUS_WMI_DEVID_UWB, + .description = "UWB power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "led1", + .dev_id = ASUS_WMI_DEVID_LED1, + .description = "LED1 control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "led2", + .dev_id = ASUS_WMI_DEVID_LED2, + .description = "LED2 control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "led3", + .dev_id = ASUS_WMI_DEVID_LED3, + .description = "LED3 control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "led4", + .dev_id = ASUS_WMI_DEVID_LED4, + .description = "LED4 control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "led5", + .dev_id = ASUS_WMI_DEVID_LED5, + .description = "LED5 control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "led6", + .dev_id = ASUS_WMI_DEVID_LED6, + .description = "LED6 control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "backlight", + .dev_id = ASUS_WMI_DEVID_BACKLIGHT, + .description = "LCD backlight on/off control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "brightness", + .dev_id = ASUS_WMI_DEVID_BRIGHTNESS, + .description = "LCD backlight brightness control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "kbd_backlight", + .dev_id = ASUS_WMI_DEVID_KBD_BACKLIGHT, + .description = "Keyboard backlight brightness control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "light_sensor", + .dev_id = ASUS_WMI_DEVID_LIGHT_SENSOR, + .description = "Ambient light sensor", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "camera", + .dev_id = ASUS_WMI_DEVID_CAMERA, + .description = "Camera power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "cardreader", + .dev_id = ASUS_WMI_DEVID_CARDREADER, + .description = "Cardreader power control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "touchpad", + .dev_id = ASUS_WMI_DEVID_TOUCHPAD, + .description = "Touchpad control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "touchpad_led", + .dev_id = ASUS_WMI_DEVID_TOUCHPAD_LED, + .description = "Touchpad LED control", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { + .name = "themperature", + .dev_id = ASUS_WMI_DEVID_THERMAL_CTRL, + .description = "Temperature (C)", + .access = CTLTYPE_INT | CTLFLAG_RD + }, + { + .name = "fan_speed", + .dev_id = ASUS_WMI_DEVID_FAN_CTRL, + .description = "Fan speed (0-3)", + .access = CTLTYPE_INT | CTLFLAG_RD + }, + { + .name = "processor_state", + .dev_id = ASUS_WMI_DEVID_PROCESSOR_STATE, + .description = "Processor state", + .access = CTLTYPE_INT | CTLFLAG_RW + }, + { NULL, 0, NULL, 0 } +}; + +ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device"); + +static void acpi_asus_wmi_identify(driver_t *driver, device_t parent); +static int acpi_asus_wmi_probe(device_t dev); +static int acpi_asus_wmi_attach(device_t dev); +static int acpi_asus_wmi_detach(device_t dev); + +static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, + int arg, int oldarg); +static int acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id); +static int acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method, + UINT32 arg0, UINT32 arg1, UINT32 *retval); +static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc, + UINT32 dev_id, UINT32 *retval); +static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc, + UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval); +static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context); + +static device_method_t acpi_asus_wmi_methods[] = { + DEVMETHOD(device_identify, acpi_asus_wmi_identify), + DEVMETHOD(device_probe, acpi_asus_wmi_probe), + DEVMETHOD(device_attach, acpi_asus_wmi_attach), + DEVMETHOD(device_detach, acpi_asus_wmi_detach), + {0, 0} +}; + +static driver_t acpi_asus_wmi_driver = { + "acpi_asus_wmi", + acpi_asus_wmi_methods, + sizeof(struct acpi_asus_wmi_softc), +}; + +static devclass_t acpi_asus_wmi_devclass; + +DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver, + acpi_asus_wmi_devclass, 0, 0); +MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1); +MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1); + +static void +acpi_asus_wmi_identify(driver_t *driver, device_t parent) +{ + + /* Don't do anything if driver is disabled. */ + if (acpi_disabled("asus_wmi")) + return; + + /* Add only a single device instance. */ + if (device_find_child(parent, "acpi_asus_wmi", -1) != NULL) + return; + + /* Check management GUID to see whether system is compatible. */ + if (!ACPI_WMI_PROVIDES_GUID_STRING(parent, + ACPI_ASUS_WMI_MGMT_GUID)) + return; + + if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", -1) == NULL) + device_printf(parent, "add acpi_asus_wmi child failed\n"); +} + +static int +acpi_asus_wmi_probe(device_t dev) +{ + + if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev), + ACPI_ASUS_WMI_MGMT_GUID)) + return (EINVAL); + device_set_desc(dev, "ASUS WMI device"); + return (0); +} + +static int +acpi_asus_wmi_attach(device_t dev) +{ + struct acpi_asus_wmi_softc *sc; + UINT32 val; + int dev_id, i; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + + sc = device_get_softc(dev); + sc->dev = dev; + sc->wmi_dev = device_get_parent(dev); + sc->handle_keys = 1; + + /* Check management GUID. */ + if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, + ACPI_ASUS_WMI_MGMT_GUID)) { + device_printf(dev, + "WMI device does not provide the ASUS management GUID\n"); + return (EINVAL); + } + + /* Find proper DSTS method. */ + sc->dsts_id = ASUS_WMI_METHODID_DSTS; +next: + for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) { + dev_id = acpi_asus_wmi_sysctls[i].dev_id; + if (acpi_wpi_asus_get_devstate(sc, dev_id, &val)) + continue; + break; + } + if (acpi_asus_wmi_sysctls[i].name == NULL) { + if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) { + sc->dsts_id = ASUS_WMI_METHODID_DSTS2; + goto next; + } else { + device_printf(dev, "Can not detect DSTS method ID\n"); + return (EINVAL); + } + } + + /* Find proper and attach to notufy GUID. */ + if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, + ACPI_ASUS_WMI_EVENT_GUID)) + sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID; + else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, + ACPI_EEEPC_WMI_EVENT_GUID)) + sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID; + else + sc->notify_guid = NULL; + if (sc->notify_guid != NULL) { + if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev, + sc->notify_guid, acpi_asus_wmi_notify, dev)) + sc->notify_guid = NULL; + } + if (sc->notify_guid == NULL) + device_printf(dev, "Could not install event handler!\n"); + + /* Initialize. */ + if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, + ASUS_WMI_METHODID_INIT, 0, 0, &val) && bootverbose) + device_printf(dev, "Initialization: %#x\n", val); + if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, + ASUS_WMI_METHODID_SPEC, 0, 0x9, &val) && bootverbose) + device_printf(dev, "WMI BIOS version: %d.%d\n", + val >> 16, val & 0xFF); + if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, + ASUS_WMI_METHODID_SFUN, 0, 0, &val) && bootverbose) + device_printf(dev, "SFUN value: %#x\n", val); + + ACPI_SERIAL_BEGIN(asus_wmi); + + sc->sysctl_ctx = device_get_sysctl_ctx(dev); + sc->sysctl_tree = device_get_sysctl_tree(dev); + SYSCTL_ADD_INT(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "handle_keys", CTLFLAG_RW, &sc->handle_keys, + 0, "Handle some hardware keys inside the driver"); + for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) { + dev_id = acpi_asus_wmi_sysctls[i].dev_id; + if (acpi_wpi_asus_get_devstate(sc, dev_id, &val)) + continue; + switch (dev_id) { + case ASUS_WMI_DEVID_THERMAL_CTRL: + case ASUS_WMI_DEVID_PROCESSOR_STATE: + case ASUS_WMI_DEVID_FAN_CTRL: + case ASUS_WMI_DEVID_BRIGHTNESS: + if (val == 0) + continue; + break; + default: + if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0) + continue; + break; + } + + SYSCTL_ADD_PROC(sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + acpi_asus_wmi_sysctls[i].name, + acpi_asus_wmi_sysctls[i].access, + sc, i, acpi_asus_wmi_sysctl, "I", + acpi_asus_wmi_sysctls[i].description); + } + ACPI_SERIAL_END(asus_wmi); + + return (0); +} + +static int +acpi_asus_wmi_detach(device_t dev) +{ + struct acpi_asus_wmi_softc *sc = device_get_softc(dev); + + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + + if (sc->notify_guid) + ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid); + + return (0); +} + +static int +acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_asus_wmi_softc *sc; + int arg; + int oldarg; + int error = 0; + int function; + int dev_id; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1; + function = oidp->oid_arg2; + dev_id = acpi_asus_wmi_sysctls[function].dev_id; + + ACPI_SERIAL_BEGIN(asus_wmi); + arg = acpi_asus_wmi_sysctl_get(sc, dev_id); + oldarg = arg; + error = sysctl_handle_int(oidp, &arg, 0, req); + if (!error && req->newptr != NULL) + error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg); + ACPI_SERIAL_END(asus_wmi); + + return (error); +} + +static int +acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id) +{ + UINT32 val = 0; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(asus_wmi); + + acpi_wpi_asus_get_devstate(sc, dev_id, &val); + + switch(dev_id) { + case ASUS_WMI_DEVID_THERMAL_CTRL: + val = (val - 2732 + 5) / 10; + break; + case ASUS_WMI_DEVID_PROCESSOR_STATE: + case ASUS_WMI_DEVID_FAN_CTRL: + break; + case ASUS_WMI_DEVID_BRIGHTNESS: + val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK; + break; + case ASUS_WMI_DEVID_KBD_BACKLIGHT: + val &= 0x7; + break; + default: + if (val & ASUS_WMI_DSTS_UNKNOWN_BIT) + val = -1; + else + val = !!(val & ASUS_WMI_DSTS_STATUS_BIT); + break; + } + + return (val); +} + +static int +acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg) +{ + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(asus_wmi); + + switch(dev_id) { + case ASUS_WMI_DEVID_KBD_BACKLIGHT: + arg = min(0x7, arg); + if (arg != 0) + arg |= 0x80; + break; + } + + acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL); + + return (0); +} + +static __inline void +acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) { + if (buf && buf->Pointer) { + AcpiOsFree(buf->Pointer); + } +} + +static void +acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context) +{ + device_t dev = context; + ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); + UINT32 val; + int code = 0; + + struct acpi_asus_wmi_softc *sc = device_get_softc(dev); + ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL }; + ACPI_OBJECT *obj; + ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response); + obj = (ACPI_OBJECT*) response.Pointer; + if (obj && obj->Type == ACPI_TYPE_INTEGER) { + code = obj->Integer.Value; + acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT, + code); + } + if (code && sc->handle_keys) { + /* Keyboard backlight control. */ + if (code == 0xc4 || code == 0xc5) { + acpi_wpi_asus_get_devstate(sc, + ASUS_WMI_DEVID_KBD_BACKLIGHT, &val); + val &= 0x7; + if (code == 0xc4) { + if (val < 0x7) + val++; + } else if (val > 0) + val--; + if (val != 0) + val |= 0x80; + acpi_wpi_asus_set_devstate(sc, + ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL); + } + /* Touchpad control. */ + if (code == 0x6b) { + acpi_wpi_asus_get_devstate(sc, + ASUS_WMI_DEVID_TOUCHPAD, &val); + val = !(val & 1); + acpi_wpi_asus_set_devstate(sc, + ASUS_WMI_DEVID_TOUCHPAD, val, NULL); + } + } + acpi_asus_wmi_free_buffer(&response); +} + +static int +acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method, + UINT32 arg0, UINT32 arg1, UINT32 *retval) +{ + UINT32 params[2] = { arg0, arg1 }; + UINT32 result; + ACPI_OBJECT *obj; + ACPI_BUFFER in = { sizeof(params), ¶ms }; + ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; + + if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, + ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) { + acpi_asus_wmi_free_buffer(&out); + return (-EINVAL); + } + obj = out.Pointer; + if (obj && obj->Type == ACPI_TYPE_INTEGER) + result = (UINT32) obj->Integer.Value; + else + result = 0; + acpi_asus_wmi_free_buffer(&out); + if (retval) + *retval = result; + return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0); +} + +static int +acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc, + UINT32 dev_id, UINT32 *retval) +{ + + return (acpi_asus_wmi_evaluate_method(sc->wmi_dev, + sc->dsts_id, dev_id, 0, retval)); +} + +static int +acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc, + UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval) +{ + + return (acpi_asus_wmi_evaluate_method(sc->wmi_dev, + ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval)); +} diff --git a/sys/modules/acpi/Makefile b/sys/modules/acpi/Makefile index 9390064811bd..04c6dfce79f2 100644 --- a/sys/modules/acpi/Makefile +++ b/sys/modules/acpi/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -SUBDIR= acpi_asus acpi_fujitsu acpi_hp acpi_ibm \ +SUBDIR= acpi_asus acpi_asus_wmi acpi_fujitsu acpi_hp acpi_ibm \ acpi_panasonic acpi_sony acpi_toshiba acpi_video \ acpi_dock acpi_wmi aibs diff --git a/sys/modules/acpi/acpi_asus_wmi/Makefile b/sys/modules/acpi/acpi_asus_wmi/Makefile new file mode 100644 index 000000000000..a5ff32a22efb --- /dev/null +++ b/sys/modules/acpi/acpi_asus_wmi/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/acpi_support + +KMOD= acpi_asus_wmi +CFLAGS+=-I${.CURDIR}/../../../dev/acpi_support +SRCS= acpi_asus_wmi.c opt_acpi.h acpi_if.h acpi_wmi_if.h device_if.h bus_if.h + +.include