aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/acpi_support
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpi_support')
-rw-r--r--sys/dev/acpi_support/acpi_aiboost.c338
-rw-r--r--sys/dev/acpi_support/acpi_asus.c1297
-rw-r--r--sys/dev/acpi_support/acpi_fujitsu.c716
-rw-r--r--sys/dev/acpi_support/acpi_ibm.c948
-rw-r--r--sys/dev/acpi_support/acpi_panasonic.c514
-rw-r--r--sys/dev/acpi_support/acpi_sony.c170
-rw-r--r--sys/dev/acpi_support/acpi_toshiba.c566
7 files changed, 4549 insertions, 0 deletions
diff --git a/sys/dev/acpi_support/acpi_aiboost.c b/sys/dev/acpi_support/acpi_aiboost.c
new file mode 100644
index 000000000000..00bf9cb1a34d
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_aiboost.c
@@ -0,0 +1,338 @@
+/*-
+ * Copyright (c) 2006 Takanori Watanabe
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <contrib/dev/acpica/acpi.h>
+#include "acpi_if.h"
+#include <sys/module.h>
+#include <dev/acpica/acpivar.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("AIBOOST")
+
+#define DESCSTRLEN 32
+struct acpi_aiboost_element{
+ ACPI_HANDLE h;
+ uint32_t id;
+ char desc[DESCSTRLEN];
+};
+ACPI_SERIAL_DECL(aiboost, "ACPI AIBOOST");
+/**/
+struct acpi_aiboost_component{
+ unsigned int num;
+ struct acpi_aiboost_element elem[1];
+};
+
+struct acpi_aiboost_softc {
+ int pid;
+ struct acpi_aiboost_component *temp;
+ struct acpi_aiboost_component *volt;
+ struct acpi_aiboost_component *fan;
+};
+
+static int acpi_aiboost_probe(device_t dev);
+static int acpi_aiboost_attach(device_t dev);
+static int acpi_aiboost_detach(device_t dev);
+
+static device_method_t acpi_aiboost_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_aiboost_probe),
+ DEVMETHOD(device_attach, acpi_aiboost_attach),
+ DEVMETHOD(device_detach, acpi_aiboost_detach),
+
+ {0, 0}
+};
+
+static driver_t acpi_aiboost_driver = {
+ "acpi_aiboost",
+ acpi_aiboost_methods,
+ sizeof(struct acpi_aiboost_softc),
+};
+
+static devclass_t acpi_aiboost_devclass;
+
+DRIVER_MODULE(acpi_aiboost, acpi, acpi_aiboost_driver, acpi_aiboost_devclass,
+ 0, 0);
+MODULE_DEPEND(acpi_aiboost, acpi, 1, 1, 1);
+static char *abs_id[] = {"ATK0110", NULL};
+
+/*VSIF, RVLT, SVLT, TSIF, RTMP, STMP FSIF, RFAN, SFAN */
+
+static ACPI_STATUS acpi_aiboost_getcomponent(device_t dev, char *name, struct acpi_aiboost_component **comp)
+{
+ ACPI_BUFFER buf, buf2;
+ ACPI_OBJECT *o,*elem,*subobj;
+ ACPI_STATUS status;
+ struct acpi_aiboost_component *c = NULL;
+
+ int i;
+
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ buf2.Pointer = NULL;
+
+ status = AcpiEvaluateObject(acpi_get_handle(dev), name, NULL, &buf);
+
+ if(ACPI_FAILURE(status))
+ return status;
+
+ o = buf.Pointer;
+ if(o->Type != ACPI_TYPE_PACKAGE)
+ goto error;
+
+ elem = o->Package.Elements;
+ if(elem->Type != ACPI_TYPE_INTEGER)
+ goto error;
+
+ c = malloc(sizeof(struct acpi_aiboost_component)
+ + sizeof(struct acpi_aiboost_element)
+ * (elem->Integer.Value -1),
+ M_DEVBUF, M_ZERO|M_WAITOK);
+ *comp = c;
+ c->num = elem->Integer.Value;
+
+ for(i = 1 ; i < o->Package.Count; i++){
+ elem = &o->Package.Elements[i];
+ if(elem->Type != ACPI_TYPE_ANY){
+ printf("NOREF\n");
+ goto error;
+ }
+ c->elem[ i - 1].h = elem->Reference.Handle;
+
+ buf2.Pointer = NULL;
+ buf2.Length = ACPI_ALLOCATE_BUFFER;
+
+ status = AcpiEvaluateObject(c->elem[i - 1].h, NULL, NULL,
+ &buf2);
+ if(ACPI_FAILURE(status)){
+ printf("FETCH OBJECT\n");
+ goto error;
+ }
+ subobj = buf2.Pointer;
+ if(ACPI_FAILURE(acpi_PkgInt32(subobj,0, &c->elem[i -1].id))){
+ printf("ID FAILED\n");
+ goto error;
+ }
+ status = acpi_PkgStr(subobj, 1, c->elem[i - 1].desc,
+ sizeof(c->elem[i - 1].desc));
+ if(ACPI_FAILURE(status)){
+ if(status == E2BIG){
+ c->elem[i-1].desc[DESCSTRLEN-1] = 0;
+ }else{
+ printf("DESC FAILED %d\n", i-1);
+ goto error;
+ }
+ }
+
+ if(buf2.Pointer)
+ AcpiOsFree(buf2.Pointer);
+ }
+
+ if(buf.Pointer)
+ AcpiOsFree(buf.Pointer);
+
+ return 0;
+
+ error:
+ printf("BAD DATA\n");
+ if(buf.Pointer)
+ AcpiOsFree(buf.Pointer);
+ if(buf2.Pointer)
+ AcpiOsFree(buf2.Pointer);
+ if(c)
+ free(c, M_DEVBUF);
+ return AE_BAD_DATA;
+}
+
+static int
+acpi_aiboost_get_value(ACPI_HANDLE handle, char *path, UINT32 number)
+{
+ ACPI_OBJECT arg1, *ret;
+ ACPI_OBJECT_LIST args;
+ ACPI_BUFFER buf;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ buf.Pointer = 0;
+ int val;
+
+ arg1.Type = ACPI_TYPE_INTEGER;
+ arg1.Integer.Value = number;
+ args.Count = 1;
+ args.Pointer = &arg1;
+
+ if(ACPI_FAILURE(AcpiEvaluateObject(handle, path, &args, &buf))){
+ return -1;
+ }
+
+ ret = buf.Pointer;
+ val = (ret->Type == ACPI_TYPE_INTEGER)? ret->Integer.Value : -1;
+
+ AcpiOsFree(buf.Pointer);
+ return val;
+}
+
+
+static int acpi_aiboost_temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = arg1;
+ int function = oidp->oid_arg2;
+ int error = 0, val;
+ ACPI_SERIAL_BEGIN(aiboost);
+ val = acpi_aiboost_get_value(acpi_get_handle(dev), "RTMP",function );
+ error = sysctl_handle_int(oidp, &val, 0 , req);
+ ACPI_SERIAL_END(aiboost);
+
+ return 0;
+}
+
+static int acpi_aiboost_volt_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = arg1;
+ int function = oidp->oid_arg2;
+ int error = 0, val;
+ ACPI_SERIAL_BEGIN(aiboost);
+ val = acpi_aiboost_get_value(acpi_get_handle(dev), "RVLT", function);
+ error = sysctl_handle_int(oidp, &val, 0 , req);
+ ACPI_SERIAL_END(aiboost);
+
+ return 0;
+}
+
+static int acpi_aiboost_fan_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = arg1;
+ int function = oidp->oid_arg2;
+ int error = 0, val;
+ ACPI_SERIAL_BEGIN(aiboost);
+ val = acpi_aiboost_get_value(acpi_get_handle(dev), "RFAN", function);
+ error = sysctl_handle_int(oidp, &val, 0 , req);
+ ACPI_SERIAL_END(aiboost);
+
+ return 0;
+}
+
+static int
+acpi_aiboost_probe(device_t dev)
+{
+ int ret = ENXIO;
+
+ if (ACPI_ID_PROBE(device_get_parent(dev), dev, abs_id)) {
+ device_set_desc(dev, "ASUStek AIBOOSTER");
+ ret = 0;
+ }
+ return (ret);
+}
+
+static int
+acpi_aiboost_attach(device_t dev)
+{
+ struct acpi_aiboost_softc *sc;
+ char nambuf[]="tempXXX";
+ int i;
+
+ sc = device_get_softc(dev);
+ if(ACPI_FAILURE(acpi_aiboost_getcomponent(dev, "TSIF", &sc->temp)))
+ goto error;
+ for(i= 0; i < sc->temp->num; i++){
+ sprintf(nambuf,"temp%d", i);
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, nambuf,
+ CTLTYPE_INT|CTLFLAG_RD, dev,
+ sc->temp->elem[i].id,
+ acpi_aiboost_temp_sysctl,
+ "I", sc->temp->elem[i].desc);
+ }
+ if(ACPI_FAILURE(acpi_aiboost_getcomponent(dev, "VSIF", &sc->volt)))
+ goto error;
+
+ for(i= 0; i < sc->volt->num; i++){
+ sprintf(nambuf,"volt%d", i);
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, nambuf,
+ CTLTYPE_INT|CTLFLAG_RD, dev,
+ sc->volt->elem[i].id,
+ acpi_aiboost_volt_sysctl,
+ "I", sc->volt->elem[i].desc);
+ }
+
+ if(ACPI_FAILURE(acpi_aiboost_getcomponent(dev, "FSIF", &sc->fan)))
+ goto error;
+
+ for(i= 0; i < sc->fan->num; i++){
+ sprintf(nambuf,"fan%d", i);
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, nambuf,
+ CTLTYPE_INT|CTLFLAG_RD, dev,
+ sc->fan->elem[i].id,
+ acpi_aiboost_fan_sysctl,
+ "I", sc->fan->elem[i].desc);
+ }
+
+
+ return (0);
+ error:
+ return EINVAL;
+}
+
+static int
+acpi_aiboost_detach(device_t dev)
+{
+ struct acpi_aiboost_softc *sc = device_get_softc(dev);
+
+ if(sc->temp)
+ free(sc->temp, M_DEVBUF);
+ if(sc->volt)
+ free(sc->volt, M_DEVBUF);
+ if(sc->fan)
+ free(sc->fan, M_DEVBUF);
+ return (0);
+}
+
+#if 0
+static int
+acpi_aiboost_suspend(device_t dev)
+{
+ struct acpi_aiboost_softc *sc = device_get_softc(dev);
+ return (0);
+}
+
+static int
+acpi_aiboost_resume(device_t dev)
+{
+ return (0);
+}
+#endif
diff --git a/sys/dev/acpi_support/acpi_asus.c b/sys/dev/acpi_support/acpi_asus.c
new file mode 100644
index 000000000000..28f9e7c982c3
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_asus.c
@@ -0,0 +1,1297 @@
+/*-
+ * Copyright (c) 2004, 2005 Philip Paeps <philip@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
+ * recent Asus (and Medion) laptops. Inspired by the acpi4asus project which
+ * implements these features in the Linux kernel.
+ *
+ * <http://sourceforge.net/projects/acpi4asus/>
+ *
+ * Currently should support most features, but could use some more testing.
+ * Particularly the display-switching stuff is a bit hairy. If you have an
+ * Asus laptop which doesn't appear to be supported, or strange things happen
+ * when using this driver, please report to <acpi@FreeBSD.org>.
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/sbuf.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+#include <dev/led/led.h>
+
+/* Methods */
+#define ACPI_ASUS_METHOD_BRN 1
+#define ACPI_ASUS_METHOD_DISP 2
+#define ACPI_ASUS_METHOD_LCD 3
+#define ACPI_ASUS_METHOD_CAMERA 4
+#define ACPI_ASUS_METHOD_CARDRD 5
+#define ACPI_ASUS_METHOD_WLAN 6
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("ASUS")
+
+struct acpi_asus_model {
+ char *name;
+
+ char *bled_set;
+ char *dled_set;
+ char *gled_set;
+ char *mled_set;
+ char *tled_set;
+ char *wled_set;
+
+ char *brn_get;
+ char *brn_set;
+ char *brn_up;
+ char *brn_dn;
+
+ char *lcd_get;
+ char *lcd_set;
+
+ char *disp_get;
+ char *disp_set;
+
+ char *cam_get;
+ char *cam_set;
+
+ char *crd_get;
+ char *crd_set;
+
+ char *wlan_get;
+ char *wlan_set;
+
+ void (*n_func)(ACPI_HANDLE, UINT32, void *);
+
+ char *lcdd;
+ void (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *);
+};
+
+struct acpi_asus_led {
+ struct acpi_asus_softc *sc;
+ struct cdev *cdev;
+ int busy;
+ int state;
+ enum {
+ ACPI_ASUS_LED_BLED,
+ ACPI_ASUS_LED_DLED,
+ ACPI_ASUS_LED_GLED,
+ ACPI_ASUS_LED_MLED,
+ ACPI_ASUS_LED_TLED,
+ ACPI_ASUS_LED_WLED,
+ } type;
+};
+
+struct acpi_asus_softc {
+ device_t dev;
+ ACPI_HANDLE handle;
+ ACPI_HANDLE lcdd_handle;
+
+ struct acpi_asus_model *model;
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+
+ struct acpi_asus_led s_bled;
+ struct acpi_asus_led s_dled;
+ struct acpi_asus_led s_gled;
+ struct acpi_asus_led s_mled;
+ struct acpi_asus_led s_tled;
+ struct acpi_asus_led s_wled;
+
+ int s_brn;
+ int s_disp;
+ int s_lcd;
+ int s_cam;
+ int s_crd;
+ int s_wlan;
+};
+
+static void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify,
+ void *context);
+
+/*
+ * We can identify Asus laptops from the string they return
+ * as a result of calling the ATK0100 'INIT' method.
+ */
+static struct acpi_asus_model acpi_asus_models[] = {
+ {
+ .name = "xxN",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\BKLT",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\ADVG",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A1x",
+ .mled_set = "MLED",
+ .lcd_get = "\\BKLI",
+ .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10",
+ .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E",
+ .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F"
+ },
+ {
+ .name = "A2x",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\BAOF",
+ .lcd_set = "\\Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\INFB",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A3E",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x67)",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A3F",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .bled_set = "BLED",
+ .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x11)",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\SSTE",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A3N",
+ .mled_set = "MLED",
+ .bled_set = "BLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\BKLT",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A4D",
+ .mled_set = "MLED",
+ .brn_up = "\\_SB_.PCI0.SBRG.EC0._Q0E",
+ .brn_dn = "\\_SB_.PCI0.SBRG.EC0._Q0F",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+#ifdef notyet
+ .disp_get = "\\_SB_.PCI0.SBRG.EC0._Q10",
+ .disp_set = "\\_SB_.PCI0.SBRG.EC0._Q11"
+#endif
+ },
+ {
+ .name = "A6V",
+ .bled_set = "BLED",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = NULL,
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "A8SR",
+ .bled_set = "BLED",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = NULL,
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD",
+ .disp_set = "SDSP",
+ .lcdd = "\\_SB.PCI0.P0P1.VGA.LCDD",
+ .lcdd_n_func = acpi_asus_lcdd_notify
+ },
+ {
+ .name = "D1x",
+ .mled_set = "MLED",
+ .lcd_get = "\\GP11",
+ .lcd_set = "\\Q0D",
+ .brn_up = "\\Q0C",
+ .brn_dn = "\\Q0B",
+ .disp_get = "\\INFB",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "G2K",
+ .bled_set = "BLED",
+ .dled_set = "DLED",
+ .gled_set = "GLED",
+ .mled_set = "MLED",
+ .tled_set = "TLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD",
+ .disp_set = "SDSP",
+ },
+ {
+ .name = "L2D",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_up = "\\Q0E",
+ .brn_dn = "\\Q0F",
+ .lcd_get = "\\SGP0",
+ .lcd_set = "\\Q10"
+ },
+ {
+ .name = "L3C",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\GL32",
+ .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10"
+ },
+ {
+ .name = "L3D",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\BKLG",
+ .lcd_set = "\\Q10"
+ },
+ {
+ .name = "L3H",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\_SB.PCI0.PM.PBC",
+ .lcd_set = "EHK",
+ .disp_get = "\\_SB.INFB",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "L4R",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\_SB.PCI0.SBSM.SEO4",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "L5x",
+ .mled_set = "MLED",
+ .tled_set = "TLED",
+ .lcd_get = "\\BAOF",
+ .lcd_set = "\\Q0D",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\INFB",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "L8L"
+ /* Only has hotkeys, apparently */
+ },
+ {
+ .name = "M1A",
+ .mled_set = "MLED",
+ .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E",
+ .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F",
+ .lcd_get = "\\PNOF",
+ .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10"
+ },
+ {
+ .name = "M2E",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\GP06",
+ .lcd_set = "\\Q10"
+ },
+ {
+ .name = "M6N",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .lcd_get = "\\_SB.BKLT",
+ .brn_set = "SPLV",
+ .brn_get = "GPLV",
+ .disp_set = "SDSP",
+ .disp_get = "\\SSTE"
+ },
+ {
+ .name = "M6R",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .lcd_get = "\\_SB.PCI0.SBSM.SEO4",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .disp_get = "\\SSTE",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "S1x",
+ .mled_set = "MLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\PNOF",
+ .lcd_set = "\\_SB.PCI0.PX40.Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV"
+ },
+ {
+ .name = "S2x",
+ .mled_set = "MLED",
+ .lcd_get = "\\BKLI",
+ .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10",
+ .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B",
+ .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A"
+ },
+ {
+ .name = "V6V",
+ .bled_set = "BLED",
+ .tled_set = "TLED",
+ .wled_set = "WLED",
+ .lcd_get = "\\BKLT",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+ {
+ .name = "W5A",
+ .bled_set = "BLED",
+ .lcd_get = "\\BKLT",
+ .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
+ .brn_get = "GPLV",
+ .brn_set = "SPLV",
+ .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD",
+ .disp_set = "SDSP"
+ },
+
+ { .name = NULL }
+};
+
+/*
+ * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
+ * but they can't be probed quite the same way as Asus laptops.
+ */
+static struct acpi_asus_model acpi_samsung_models[] = {
+ {
+ .name = "P30",
+ .wled_set = "WLED",
+ .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68",
+ .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69",
+ .lcd_get = "\\BKLT",
+ .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E"
+ },
+
+ { .name = NULL }
+};
+
+static void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
+
+/*
+ * EeePC have an Asus ASUS010 gadget interface,
+ * but they can't be probed quite the same way as Asus laptops.
+ */
+static struct acpi_asus_model acpi_eeepc_models[] = {
+ {
+ .name = "EEE",
+ .brn_get = "\\_SB.ATKD.PBLG",
+ .brn_set = "\\_SB.ATKD.PBLS",
+ .cam_get = "\\_SB.ATKD.CAMG",
+ .cam_set = "\\_SB.ATKD.CAMS",
+ .crd_set = "\\_SB.ATKD.CRDS",
+ .crd_get = "\\_SB.ATKD.CRDG",
+ .wlan_get = "\\_SB.ATKD.WLDG",
+ .wlan_set = "\\_SB.ATKD.WLDS",
+ .n_func = acpi_asus_eeepc_notify
+ },
+
+ { .name = NULL }
+};
+
+static struct {
+ char *name;
+ char *description;
+ int method;
+ int flags;
+} acpi_asus_sysctls[] = {
+ {
+ .name = "lcd_backlight",
+ .method = ACPI_ASUS_METHOD_LCD,
+ .description = "state of the lcd backlight",
+ .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
+ },
+ {
+ .name = "lcd_brightness",
+ .method = ACPI_ASUS_METHOD_BRN,
+ .description = "brightness of the lcd panel",
+ .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
+ },
+ {
+ .name = "video_output",
+ .method = ACPI_ASUS_METHOD_DISP,
+ .description = "display output state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "camera",
+ .method = ACPI_ASUS_METHOD_CAMERA,
+ .description = "internal camera state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "cardreader",
+ .method = ACPI_ASUS_METHOD_CARDRD,
+ .description = "internal card reader state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "wlan",
+ .method = ACPI_ASUS_METHOD_WLAN,
+ .description = "wireless lan state",
+ .flags = CTLTYPE_INT | CTLFLAG_RW
+ },
+
+ { .name = NULL }
+};
+
+ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
+
+/* Function prototypes */
+static int acpi_asus_probe(device_t dev);
+static int acpi_asus_attach(device_t dev);
+static int acpi_asus_detach(device_t dev);
+
+static void acpi_asus_led(struct acpi_asus_led *led, int state);
+static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
+
+static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
+static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
+static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
+
+static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
+
+static device_method_t acpi_asus_methods[] = {
+ DEVMETHOD(device_probe, acpi_asus_probe),
+ DEVMETHOD(device_attach, acpi_asus_attach),
+ DEVMETHOD(device_detach, acpi_asus_detach),
+
+ { 0, 0 }
+};
+
+static driver_t acpi_asus_driver = {
+ "acpi_asus",
+ acpi_asus_methods,
+ sizeof(struct acpi_asus_softc)
+};
+
+static devclass_t acpi_asus_devclass;
+
+DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
+MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
+
+static int
+acpi_asus_probe(device_t dev)
+{
+ struct acpi_asus_model *model;
+ struct acpi_asus_softc *sc;
+ struct sbuf *sb;
+ ACPI_BUFFER Buf;
+ ACPI_OBJECT Arg, *Obj;
+ ACPI_OBJECT_LIST Args;
+ static char *asus_ids[] = { "ATK0100", "ASUS010", NULL };
+ char *rstr;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (acpi_disabled("asus"))
+ return (ENXIO);
+ rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
+ if (rstr == NULL) {
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->handle = acpi_get_handle(dev);
+
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = 0;
+
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+
+ Buf.Pointer = NULL;
+ Buf.Length = ACPI_ALLOCATE_BUFFER;
+
+ AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
+ Obj = Buf.Pointer;
+
+ /*
+ * The Samsung P30 returns a null-pointer from INIT, we
+ * can identify it from the 'ODEM' string in the DSDT.
+ */
+ if (Obj->String.Pointer == NULL) {
+ ACPI_STATUS status;
+ ACPI_TABLE_HEADER th;
+
+ status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "Unsupported (Samsung?) laptop\n");
+ AcpiOsFree(Buf.Pointer);
+ return (ENXIO);
+ }
+
+ if (strncmp("ODEM", th.OemTableId, 4) == 0) {
+ sc->model = &acpi_samsung_models[0];
+ device_set_desc(dev, "Samsung P30 Laptop Extras");
+ AcpiOsFree(Buf.Pointer);
+ return (0);
+ }
+
+ /* EeePC */
+ if (strncmp("ASUS010", rstr, 7) == 0) {
+ sc->model = &acpi_eeepc_models[0];
+ device_set_desc(dev, "ASUS EeePC");
+ AcpiOsFree(Buf.Pointer);
+ return (0);
+ }
+ }
+
+ sb = sbuf_new_auto();
+ if (sb == NULL)
+ return (ENOMEM);
+
+ /*
+ * Asus laptops are simply identified by name, easy!
+ */
+ for (model = acpi_asus_models; model->name != NULL; model++) {
+ if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
+
+good:
+ sbuf_printf(sb, "Asus %s Laptop Extras",
+ Obj->String.Pointer);
+ sbuf_finish(sb);
+
+ sc->model = model;
+ device_set_desc_copy(dev, sbuf_data(sb));
+
+ sbuf_delete(sb);
+ AcpiOsFree(Buf.Pointer);
+ return (0);
+ }
+
+ /*
+ * Some models look exactly the same as other models, but have
+ * their own ids. If we spot these, set them up with the same
+ * details as the models they're like, possibly dealing with
+ * small differences.
+ *
+ * XXX: there must be a prettier way to do this!
+ */
+ else if (strncmp(model->name, "xxN", 3) == 0 &&
+ (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
+ strncmp(Obj->String.Pointer, "S1N", 3) == 0))
+ goto good;
+ else if (strncmp(model->name, "A1x", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "A1", 2) == 0)
+ goto good;
+ else if (strncmp(model->name, "A2x", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "A2", 2) == 0)
+ goto good;
+ else if (strncmp(model->name, "A3F", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "A6F", 3) == 0)
+ goto good;
+ else if (strncmp(model->name, "D1x", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "D1", 2) == 0)
+ goto good;
+ else if (strncmp(model->name, "L3H", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "L2E", 3) == 0)
+ goto good;
+ else if (strncmp(model->name, "L5x", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "L5", 2) == 0)
+ goto good;
+ else if (strncmp(model->name, "M2E", 3) == 0 &&
+ (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
+ strncmp(Obj->String.Pointer, "L4E", 3) == 0))
+ goto good;
+ else if (strncmp(model->name, "S1x", 3) == 0 &&
+ (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
+ strncmp(Obj->String.Pointer, "S1", 2) == 0))
+ goto good;
+ else if (strncmp(model->name, "S2x", 3) == 0 &&
+ (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
+ strncmp(Obj->String.Pointer, "S2", 2) == 0))
+ goto good;
+
+ /* L2B is like L3C but has no lcd_get method */
+ else if (strncmp(model->name, "L3C", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
+ model->lcd_get = NULL;
+ goto good;
+ }
+
+ /* A3G is like M6R but with a different lcd_get method */
+ else if (strncmp(model->name, "M6R", 3) == 0 &&
+ strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
+ model->lcd_get = "\\BLFG";
+ goto good;
+ }
+
+ /* M2N and W1N are like xxN with added WLED */
+ else if (strncmp(model->name, "xxN", 3) == 0 &&
+ (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
+ strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
+ model->wled_set = "WLED";
+ goto good;
+ }
+
+ /* M5N and S5N are like xxN without MLED */
+ else if (strncmp(model->name, "xxN", 3) == 0 &&
+ (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
+ strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
+ model->mled_set = NULL;
+ goto good;
+ }
+ }
+
+ sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
+ sbuf_finish(sb);
+
+ device_printf(dev, sbuf_data(sb));
+
+ sbuf_delete(sb);
+ AcpiOsFree(Buf.Pointer);
+
+ return (ENXIO);
+}
+
+static int
+acpi_asus_attach(device_t dev)
+{
+ struct acpi_asus_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+ acpi_sc = acpi_device_get_parent_softc(dev);
+
+ /* Build sysctl tree */
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
+ OID_AUTO, "asus", CTLFLAG_RD, 0, "");
+
+ /* Hook up nodes */
+ for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
+ if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
+ continue;
+
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ acpi_asus_sysctls[i].name,
+ acpi_asus_sysctls[i].flags,
+ sc, i, acpi_asus_sysctl, "I",
+ acpi_asus_sysctls[i].description);
+ }
+
+ /* Attach leds */
+ if (sc->model->bled_set) {
+ sc->s_bled.busy = 0;
+ sc->s_bled.sc = sc;
+ sc->s_bled.type = ACPI_ASUS_LED_BLED;
+ sc->s_bled.cdev =
+ led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
+ "bled", 1);
+ }
+
+ if (sc->model->dled_set) {
+ sc->s_dled.busy = 0;
+ sc->s_dled.sc = sc;
+ sc->s_dled.type = ACPI_ASUS_LED_DLED;
+ sc->s_dled.cdev =
+ led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
+ }
+
+ if (sc->model->gled_set) {
+ sc->s_gled.busy = 0;
+ sc->s_gled.sc = sc;
+ sc->s_gled.type = ACPI_ASUS_LED_GLED;
+ sc->s_gled.cdev =
+ led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
+ }
+
+ if (sc->model->mled_set) {
+ sc->s_mled.busy = 0;
+ sc->s_mled.sc = sc;
+ sc->s_mled.type = ACPI_ASUS_LED_MLED;
+ sc->s_mled.cdev =
+ led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
+ }
+
+ if (sc->model->tled_set) {
+ sc->s_tled.busy = 0;
+ sc->s_tled.sc = sc;
+ sc->s_tled.type = ACPI_ASUS_LED_TLED;
+ sc->s_tled.cdev =
+ led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
+ "tled", 1);
+ }
+
+ if (sc->model->wled_set) {
+ sc->s_wled.busy = 0;
+ sc->s_wled.sc = sc;
+ sc->s_wled.type = ACPI_ASUS_LED_WLED;
+ sc->s_wled.cdev =
+ led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
+ "wled", 1);
+ }
+
+ /* Activate hotkeys */
+ AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
+
+ /* Handle notifies */
+ if (sc->model->n_func == NULL)
+ sc->model->n_func = acpi_asus_notify;
+
+ AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
+ sc->model->n_func, dev);
+
+ /* Find and hook the 'LCDD' object */
+ if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
+ ACPI_STATUS res;
+
+ sc->lcdd_handle = NULL;
+ res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
+ NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
+ if (ACPI_SUCCESS(res)) {
+ AcpiInstallNotifyHandler((sc->lcdd_handle),
+ ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
+ } else {
+ printf("%s: unable to find LCD device '%s'\n",
+ __func__, sc->model->lcdd);
+ }
+ }
+
+ return (0);
+}
+
+static int
+acpi_asus_detach(device_t dev)
+{
+ struct acpi_asus_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+
+ /* Turn the lights off */
+ if (sc->model->bled_set)
+ led_destroy(sc->s_bled.cdev);
+
+ if (sc->model->dled_set)
+ led_destroy(sc->s_dled.cdev);
+
+ if (sc->model->gled_set)
+ led_destroy(sc->s_gled.cdev);
+
+ if (sc->model->mled_set)
+ led_destroy(sc->s_mled.cdev);
+
+ if (sc->model->tled_set)
+ led_destroy(sc->s_tled.cdev);
+
+ if (sc->model->wled_set)
+ led_destroy(sc->s_wled.cdev);
+
+ /* Remove notify handler */
+ AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
+ acpi_asus_notify);
+
+ if (sc->lcdd_handle) {
+ KASSERT(sc->model->lcdd_n_func != NULL,
+ ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
+ AcpiRemoveNotifyHandler((sc->lcdd_handle),
+ ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
+ }
+
+ /* Free sysctl tree */
+ sysctl_ctx_free(&sc->sysctl_ctx);
+
+ return (0);
+}
+
+static void
+acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
+{
+ struct acpi_asus_softc *sc;
+ char *method;
+ int state;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = led->sc;
+
+ switch (led->type) {
+ case ACPI_ASUS_LED_BLED:
+ method = sc->model->bled_set;
+ state = led->state;
+ break;
+ case ACPI_ASUS_LED_DLED:
+ method = sc->model->dled_set;
+ state = led->state;
+ break;
+ case ACPI_ASUS_LED_GLED:
+ method = sc->model->gled_set;
+ state = led->state + 1; /* 1: off, 2: on */
+ break;
+ case ACPI_ASUS_LED_MLED:
+ method = sc->model->mled_set;
+ state = !led->state; /* inverted */
+ break;
+ case ACPI_ASUS_LED_TLED:
+ method = sc->model->tled_set;
+ state = led->state;
+ break;
+ case ACPI_ASUS_LED_WLED:
+ method = sc->model->wled_set;
+ state = led->state;
+ break;
+ default:
+ printf("acpi_asus_led: invalid LED type %d\n",
+ (int)led->type);
+ return;
+ }
+
+ acpi_SetInteger(sc->handle, method, state);
+ led->busy = 0;
+}
+
+static void
+acpi_asus_led(struct acpi_asus_led *led, int state)
+{
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (led->busy)
+ return;
+
+ led->busy = 1;
+ led->state = state;
+
+ AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
+}
+
+static int
+acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_asus_softc *sc;
+ int arg;
+ int error = 0;
+ int function;
+ int method;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_asus_softc *)oidp->oid_arg1;
+ function = oidp->oid_arg2;
+ method = acpi_asus_sysctls[function].method;
+
+ ACPI_SERIAL_BEGIN(asus);
+ arg = acpi_asus_sysctl_get(sc, method);
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+
+ /* Sanity check */
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /* Update */
+ error = acpi_asus_sysctl_set(sc, method, arg);
+
+out:
+ ACPI_SERIAL_END(asus);
+ return (error);
+}
+
+static int
+acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
+{
+ int val = 0;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(asus);
+
+ switch (method) {
+ case ACPI_ASUS_METHOD_BRN:
+ val = sc->s_brn;
+ break;
+ case ACPI_ASUS_METHOD_DISP:
+ val = sc->s_disp;
+ break;
+ case ACPI_ASUS_METHOD_LCD:
+ val = sc->s_lcd;
+ break;
+ case ACPI_ASUS_METHOD_CAMERA:
+ val = sc->s_cam;
+ break;
+ case ACPI_ASUS_METHOD_CARDRD:
+ val = sc->s_crd;
+ break;
+ case ACPI_ASUS_METHOD_WLAN:
+ val = sc->s_wlan;
+ break;
+ }
+
+ return (val);
+}
+
+static int
+acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
+{
+ ACPI_STATUS status = AE_OK;
+ ACPI_OBJECT_LIST acpiargs;
+ ACPI_OBJECT acpiarg[1];
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(asus);
+
+ acpiargs.Count = 1;
+ acpiargs.Pointer = acpiarg;
+ acpiarg[0].Type = ACPI_TYPE_INTEGER;
+ acpiarg[0].Integer.Value = arg;
+
+ switch (method) {
+ case ACPI_ASUS_METHOD_BRN:
+ if (arg < 0 || arg > 15)
+ return (EINVAL);
+
+ if (sc->model->brn_set)
+ status = acpi_SetInteger(sc->handle,
+ sc->model->brn_set, arg);
+ else {
+ while (arg != 0) {
+ status = AcpiEvaluateObject(sc->handle,
+ (arg > 0) ? sc->model->brn_up :
+ sc->model->brn_dn, NULL, NULL);
+ (arg > 0) ? arg-- : arg++;
+ }
+ }
+
+ if (ACPI_SUCCESS(status))
+ sc->s_brn = arg;
+
+ break;
+ case ACPI_ASUS_METHOD_DISP:
+ if (arg < 0 || arg > 7)
+ return (EINVAL);
+
+ status = acpi_SetInteger(sc->handle,
+ sc->model->disp_set, arg);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_disp = arg;
+
+ break;
+ case ACPI_ASUS_METHOD_LCD:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ if (strncmp(sc->model->name, "L3H", 3) != 0)
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->lcd_set, NULL, NULL);
+ else
+ status = acpi_SetInteger(sc->handle,
+ sc->model->lcd_set, 0x7);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_lcd = arg;
+
+ break;
+ case ACPI_ASUS_METHOD_CAMERA:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->cam_set, &acpiargs, NULL);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_cam = arg;
+ break;
+ case ACPI_ASUS_METHOD_CARDRD:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->crd_set, &acpiargs, NULL);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_crd = arg;
+ break;
+ case ACPI_ASUS_METHOD_WLAN:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->wlan_set, &acpiargs, NULL);
+
+ if (ACPI_SUCCESS(status))
+ sc->s_wlan = arg;
+ break;
+ }
+
+ return (0);
+}
+
+static int
+acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
+{
+ ACPI_STATUS status;
+
+ switch (method) {
+ case ACPI_ASUS_METHOD_BRN:
+ if (sc->model->brn_get) {
+ /* GPLV/SPLV models */
+ status = acpi_GetInteger(sc->handle,
+ sc->model->brn_get, &sc->s_brn);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ } else if (sc->model->brn_up) {
+ /* Relative models */
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->brn_up, NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return (FALSE);
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->brn_dn, NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return (FALSE);
+
+ return (TRUE);
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_DISP:
+ if (sc->model->disp_get) {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->disp_get, &sc->s_disp);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_LCD:
+ if (sc->model->lcd_get) {
+ if (strncmp(sc->model->name, "G2K", 3) == 0) {
+ ACPI_BUFFER Buf;
+ ACPI_OBJECT Arg, Obj;
+ ACPI_OBJECT_LIST Args;
+
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = 0x11;
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+ Buf.Length = sizeof(Obj);
+ Buf.Pointer = &Obj;
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->lcd_get, &Args, &Buf);
+ if (ACPI_SUCCESS(status) &&
+ Obj.Type == ACPI_TYPE_INTEGER) {
+ sc->s_lcd = Obj.Integer.Value;
+ return (TRUE);
+ }
+ } else if (strncmp(sc->model->name, "L3H", 3) == 0) {
+ ACPI_BUFFER Buf;
+ ACPI_OBJECT Arg[2], Obj;
+ ACPI_OBJECT_LIST Args;
+
+ /* L3H is a bit special */
+ Arg[0].Type = ACPI_TYPE_INTEGER;
+ Arg[0].Integer.Value = 0x02;
+ Arg[1].Type = ACPI_TYPE_INTEGER;
+ Arg[1].Integer.Value = 0x03;
+
+ Args.Count = 2;
+ Args.Pointer = Arg;
+
+ Buf.Length = sizeof(Obj);
+ Buf.Pointer = &Obj;
+
+ status = AcpiEvaluateObject(sc->handle,
+ sc->model->lcd_get, &Args, &Buf);
+ if (ACPI_SUCCESS(status) &&
+ Obj.Type == ACPI_TYPE_INTEGER) {
+ sc->s_lcd = Obj.Integer.Value >> 8;
+ return (TRUE);
+ }
+ } else {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->lcd_get, &sc->s_lcd);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_CAMERA:
+ if (sc->model->cam_get) {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->cam_get, &sc->s_cam);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_CARDRD:
+ if (sc->model->crd_get) {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->crd_get, &sc->s_crd);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ return (FALSE);
+ case ACPI_ASUS_METHOD_WLAN:
+ if (sc->model->wlan_get) {
+ status = acpi_GetInteger(sc->handle,
+ sc->model->wlan_get, &sc->s_wlan);
+ if (ACPI_SUCCESS(status))
+ return (TRUE);
+ }
+ return (FALSE);
+ }
+ return (FALSE);
+}
+
+static void
+acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_asus_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc((device_t)context);
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_BEGIN(asus);
+ if ((notify & ~0x10) <= 15) {
+ sc->s_brn = notify & ~0x10;
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
+ } else if ((notify & ~0x20) <= 15) {
+ sc->s_brn = notify & ~0x20;
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
+ } else if (notify == 0x33) {
+ sc->s_lcd = 1;
+ ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
+ } else if (notify == 0x34) {
+ sc->s_lcd = 0;
+ ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
+ } else if (notify == 0x86) {
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
+ } else if (notify == 0x87) {
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
+ } else {
+ /* Notify devd(8) */
+ acpi_UserNotify("ASUS", h, notify);
+ }
+ ACPI_SERIAL_END(asus);
+}
+
+static void
+acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_asus_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc((device_t)context);
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_BEGIN(asus);
+ switch (notify) {
+ case 0x87:
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
+ break;
+ case 0x86:
+ acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
+ break;
+ }
+ ACPI_SERIAL_END(asus);
+}
+
+static void
+acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_asus_softc *sc;
+ struct acpi_softc *acpi_sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc((device_t)context);
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_BEGIN(asus);
+ if ((notify & ~0x20) <= 15) {
+ sc->s_brn = notify & ~0x20;
+ ACPI_VPRINT(sc->dev, acpi_sc,
+ "Brightness increased/decreased\n");
+ } else {
+ /* Notify devd(8) */
+ acpi_UserNotify("ASUS-Eee", h, notify);
+ }
+ ACPI_SERIAL_END(asus);
+}
diff --git a/sys/dev/acpi_support/acpi_fujitsu.c b/sys/dev/acpi_support/acpi_fujitsu.c
new file mode 100644
index 000000000000..5de365a060ce
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_fujitsu.c
@@ -0,0 +1,716 @@
+/*-
+ * Copyright (c) 2002 Sean Bullington <seanATstalker.org>
+ * 2003-2006 Anish Mistry <amistry@am-productions.biz>
+ * 2004 Mark Santcroos <marks@ripe.net>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+/* Hooks for the ACPI CA debugging infrastructure */
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("Fujitsu")
+
+/* Change and update bits for the hotkeys */
+#define VOLUME_MUTE_BIT 0x40000000
+
+/* Values of settings */
+#define GENERAL_SETTING_BITS 0x0fffffff
+#define MOUSE_SETTING_BITS GENERAL_SETTING_BITS
+#define VOLUME_SETTING_BITS GENERAL_SETTING_BITS
+#define BRIGHTNESS_SETTING_BITS GENERAL_SETTING_BITS
+
+/* Possible state changes */
+/*
+ * These are NOT arbitrary values. They are the
+ * GHKS return value from the device that says which
+ * hotkey is active. They should match up with a bit
+ * from the GSIF bitmask.
+ */
+#define BRIGHT_CHANGED 0x01
+#define VOLUME_CHANGED 0x04
+#define MOUSE_CHANGED 0x08
+/*
+ * It is unknown which hotkey this bit is supposed to indicate, but
+ * according to values from GSIF this is a valid flag.
+ */
+#define UNKNOWN_CHANGED 0x10
+
+/* sysctl values */
+#define FN_MUTE 0
+#define FN_POINTER_ENABLE 1
+#define FN_LCD_BRIGHTNESS 2
+#define FN_VOLUME 3
+
+/* Methods */
+#define METHOD_GBLL 1
+#define METHOD_GMOU 2
+#define METHOD_GVOL 3
+#define METHOD_MUTE 4
+#define METHOD_RBLL 5
+#define METHOD_RVOL 6
+#define METHOD_GSIF 7
+#define METHOD_GHKS 8
+
+/* Notify event */
+#define ACPI_NOTIFY_STATUS_CHANGED 0x80
+
+/*
+ * Holds a control method name and its associated integer value.
+ * Only used for no-argument control methods which return a value.
+ */
+struct int_nameval {
+ char *name;
+ int value;
+ int exists;
+};
+
+/*
+ * Driver extension for the FUJITSU ACPI driver.
+ */
+struct acpi_fujitsu_softc {
+ device_t dev;
+ ACPI_HANDLE handle;
+
+ /* Control methods */
+ struct int_nameval _sta, /* unused */
+ gbll, /* brightness */
+ ghks, /* hotkey selector */
+ gbuf, /* unused (buffer?) */
+ gmou, /* mouse */
+ gsif, /* function key bitmask */
+ gvol, /* volume */
+ rbll, /* number of brightness levels (radix) */
+ rvol; /* number of volume levels (radix) */
+
+ /* State variables */
+ uint8_t bIsMuted; /* Is volume muted */
+ uint8_t bIntPtrEnabled; /* Is internal ptr enabled */
+ uint32_t lastValChanged; /* The last value updated */
+
+ /* sysctl tree */
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+};
+
+/* Driver entry point forward declarations. */
+static int acpi_fujitsu_probe(device_t dev);
+static int acpi_fujitsu_attach(device_t dev);
+static int acpi_fujitsu_detach(device_t dev);
+static int acpi_fujitsu_suspend(device_t dev);
+static int acpi_fujitsu_resume(device_t dev);
+
+static void acpi_fujitsu_notify_status_changed(void *arg);
+static void acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context);
+static int acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS);
+
+/* Utility function declarations */
+static uint8_t acpi_fujitsu_update(struct acpi_fujitsu_softc *sc);
+static uint8_t acpi_fujitsu_init(struct acpi_fujitsu_softc *sc);
+static uint8_t acpi_fujitsu_check_hardware(struct acpi_fujitsu_softc *sc);
+
+/* Driver/Module specific structure definitions. */
+static device_method_t acpi_fujitsu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_fujitsu_probe),
+ DEVMETHOD(device_attach, acpi_fujitsu_attach),
+ DEVMETHOD(device_detach, acpi_fujitsu_detach),
+ DEVMETHOD(device_suspend, acpi_fujitsu_suspend),
+ DEVMETHOD(device_resume, acpi_fujitsu_resume),
+ {0, 0}
+};
+
+static driver_t acpi_fujitsu_driver = {
+ "acpi_fujitsu",
+ acpi_fujitsu_methods,
+ sizeof(struct acpi_fujitsu_softc),
+};
+
+/* Prototype for function hotkeys for getting/setting a value. */
+static int acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method);
+static int acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value);
+
+static char *fujitsu_ids[] = { "FUJ02B1", NULL };
+
+ACPI_SERIAL_DECL(fujitsu, "Fujitsu Function Hotkeys");
+
+/* sysctl names and function calls */
+static struct {
+ char *name;
+ int method;
+ char *description;
+} sysctl_table[] = {
+ {
+ .name = "mute",
+ .method = METHOD_MUTE,
+ .description = "Speakers/headphones mute status"
+ },
+ {
+ .name = "pointer_enable",
+ .method = METHOD_GMOU,
+ .description = "Enable and disable the internal pointer"
+ },
+ {
+ .name = "lcd_brightness",
+ .method = METHOD_GBLL,
+ .description = "Brightness level of the LCD panel"
+ },
+ {
+ .name = "volume",
+ .method = METHOD_GVOL,
+ .description = "Speakers/headphones volume level"
+ },
+ {
+ .name = "volume_radix",
+ .method = METHOD_RVOL,
+ .description = "Number of volume level steps"
+ },
+ {
+ .name = "lcd_brightness_radix",
+ .method = METHOD_RBLL,
+ .description = "Number of brightness level steps"
+ },
+
+ { NULL, 0, NULL }
+};
+
+static devclass_t acpi_fujitsu_devclass;
+DRIVER_MODULE(acpi_fujitsu, acpi, acpi_fujitsu_driver,
+ acpi_fujitsu_devclass, 0, 0);
+MODULE_DEPEND(acpi_fujitsu, acpi, 1, 1, 1);
+MODULE_VERSION(acpi_fujitsu, 1);
+
+static int
+acpi_fujitsu_probe(device_t dev)
+{
+ char *name;
+ char buffer[64];
+
+ name = ACPI_ID_PROBE(device_get_parent(dev), dev, fujitsu_ids);
+ if (acpi_disabled("fujitsu") || name == NULL ||
+ device_get_unit(dev) > 1)
+ return (ENXIO);
+
+ sprintf(buffer, "Fujitsu Function Hotkeys %s", name);
+ device_set_desc_copy(dev, buffer);
+
+ return (0);
+}
+
+static int
+acpi_fujitsu_attach(device_t dev)
+{
+ struct acpi_fujitsu_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->handle = acpi_get_handle(dev);
+
+ /* Install notification handler */
+ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_fujitsu_notify_handler, sc);
+
+ /* Snag our default values for the hotkeys / hotkey states. */
+ ACPI_SERIAL_BEGIN(fujitsu);
+ if (!acpi_fujitsu_init(sc))
+ device_printf(dev, "Couldn't initialize hotkey states!\n");
+ ACPI_SERIAL_END(fujitsu);
+
+ return (0);
+}
+
+/*
+ * Called when the system is being suspended, simply
+ * set an event to be signalled when we wake up.
+ */
+static int
+acpi_fujitsu_suspend(device_t dev)
+{
+
+ return (0);
+}
+
+static int
+acpi_fujitsu_resume(device_t dev)
+{
+ struct acpi_fujitsu_softc *sc;
+ ACPI_STATUS status;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * The pointer needs to be re-enabled for
+ * some revisions of the P series (2120).
+ */
+ ACPI_SERIAL_BEGIN(fujitsu);
+
+ if(sc->gmou.exists) {
+ status = acpi_SetInteger(sc->handle, "SMOU", 1);
+ if (ACPI_FAILURE(status))
+ device_printf(sc->dev, "Couldn't enable pointer\n");
+ }
+ ACPI_SERIAL_END(fujitsu);
+
+ return (0);
+}
+
+static void
+acpi_fujitsu_notify_status_changed(void *arg)
+{
+ struct acpi_fujitsu_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_fujitsu_softc *)arg;
+
+ /*
+ * Since our notify function is called, we know something has
+ * happened. So the only reason for acpi_fujitsu_update to fail
+ * is if we can't find what has changed or an error occurs.
+ */
+ ACPI_SERIAL_BEGIN(fujitsu);
+ acpi_fujitsu_update(sc);
+ ACPI_SERIAL_END(fujitsu);
+}
+
+
+static void
+acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context)
+{
+ struct acpi_fujitsu_softc *sc;
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
+
+ sc = (struct acpi_fujitsu_softc *)context;
+
+ switch (notify) {
+ case ACPI_NOTIFY_STATUS_CHANGED:
+ AcpiOsExecute(OSL_NOTIFY_HANDLER,
+ acpi_fujitsu_notify_status_changed, sc);
+ break;
+ default:
+ /* unknown notification value */
+ break;
+ }
+}
+
+static int
+acpi_fujitsu_detach(device_t dev)
+{
+ struct acpi_fujitsu_softc *sc;
+
+ sc = device_get_softc(dev);
+ AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_fujitsu_notify_handler);
+
+ sysctl_ctx_free(&sc->sysctl_ctx);
+
+ return (0);
+}
+
+/*
+ * Initializes the names of the ACPI control methods and grabs
+ * the current state of all of the ACPI hotkeys into the softc.
+ */
+static uint8_t
+acpi_fujitsu_init(struct acpi_fujitsu_softc *sc)
+{
+ struct acpi_softc *acpi_sc;
+ int i, exists;
+
+ ACPI_SERIAL_ASSERT(fujitsu);
+
+ /* Setup all of the names for each control method */
+ sc->_sta.name = "_STA";
+ sc->gbll.name = "GBLL";
+ sc->ghks.name = "GHKS";
+ sc->gmou.name = "GMOU";
+ sc->gsif.name = "GSIF";
+ sc->gvol.name = "GVOL";
+ sc->ghks.name = "GHKS";
+ sc->gsif.name = "GSIF";
+ sc->rbll.name = "RBLL";
+ sc->rvol.name = "RVOL";
+
+ /* Determine what hardware functionality is available */
+ acpi_fujitsu_check_hardware(sc);
+
+ /* Build the sysctl tree */
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
+ OID_AUTO, "fujitsu", CTLFLAG_RD, 0, "");
+
+ for (i = 0; sysctl_table[i].name != NULL; i++) {
+ exists = 0;
+ switch(sysctl_table[i].method) {
+ case METHOD_GMOU:
+ exists = sc->gmou.exists;
+ break;
+ case METHOD_GBLL:
+ exists = sc->gbll.exists;
+ break;
+ case METHOD_GVOL:
+ case METHOD_MUTE:
+ exists = sc->gvol.exists;
+ break;
+ case METHOD_RVOL:
+ exists = sc->rvol.exists;
+ break;
+ case METHOD_RBLL:
+ exists = sc->rbll.exists;
+ break;
+ default:
+ /* Allow by default */
+ exists = 1;
+ break;
+ }
+ if(!exists)
+ continue;
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ sysctl_table[i].name,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
+ sc, i, acpi_fujitsu_sysctl, "I",
+ sysctl_table[i].description);
+ }
+
+
+ /* Set the hotkeys to their initial states */
+ if (!acpi_fujitsu_update(sc)) {
+ device_printf(sc->dev, "Couldn't init hotkey states\n");
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+static int
+acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_fujitsu_softc *sc;
+ int method;
+ int arg;
+ int function_num, error = 0;
+
+ sc = (struct acpi_fujitsu_softc *)oidp->oid_arg1;
+ function_num = oidp->oid_arg2;
+ method = sysctl_table[function_num].method;
+
+ ACPI_SERIAL_BEGIN(fujitsu);
+
+ /* Get the current value */
+ arg = acpi_fujitsu_method_get(sc, method);
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /* Update the value */
+ error = acpi_fujitsu_method_set(sc, method, arg);
+
+out:
+ ACPI_SERIAL_END(fujitsu);
+ return (error);
+}
+
+static int
+acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method)
+{
+ struct int_nameval nv;
+ ACPI_STATUS status;
+
+ ACPI_SERIAL_ASSERT(fujitsu);
+
+ switch (method) {
+ case METHOD_GBLL:
+ nv = sc->gbll;
+ break;
+ case METHOD_GMOU:
+ nv = sc->gmou;
+ break;
+ case METHOD_GVOL:
+ case METHOD_MUTE:
+ nv = sc->gvol;
+ break;
+ case METHOD_GHKS:
+ nv = sc->ghks;
+ break;
+ case METHOD_GSIF:
+ nv = sc->gsif;
+ break;
+ case METHOD_RBLL:
+ nv = sc->rbll;
+ break;
+ case METHOD_RVOL:
+ nv = sc->rvol;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ if(!nv.exists)
+ return (EINVAL);
+
+ status = acpi_GetInteger(sc->handle, nv.name, &nv.value);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->dev, "Couldn't query method (%s)\n", nv.name);
+ return (FALSE);
+ }
+
+ if (method == METHOD_MUTE) {
+ sc->bIsMuted = (uint8_t)((nv.value & VOLUME_MUTE_BIT) != 0);
+ return (sc->bIsMuted);
+ }
+
+ nv.value &= GENERAL_SETTING_BITS;
+ return (nv.value);
+}
+
+static int
+acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value)
+{
+ struct int_nameval nv;
+ ACPI_STATUS status;
+ char *control;
+ int changed;
+
+ ACPI_SERIAL_ASSERT(fujitsu);
+
+ switch (method) {
+ case METHOD_GBLL:
+ changed = BRIGHT_CHANGED;
+ control = "SBLL";
+ nv = sc->gbll;
+ break;
+ case METHOD_GMOU:
+ changed = MOUSE_CHANGED;
+ control = "SMOU";
+ nv = sc->gmou;
+ break;
+ case METHOD_GVOL:
+ case METHOD_MUTE:
+ changed = VOLUME_CHANGED;
+ control = "SVOL";
+ nv = sc->gvol;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if(!nv.exists)
+ return (EINVAL);
+
+ if (method == METHOD_MUTE) {
+ if (value == 1)
+ value = nv.value | VOLUME_MUTE_BIT;
+ else if (value == 0)
+ value = nv.value & ~VOLUME_MUTE_BIT;
+ else
+ return (EINVAL);
+ }
+
+ status = acpi_SetInteger(sc->handle, control, value);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->dev, "Couldn't update %s\n", control);
+ return (FALSE);
+ }
+
+ sc->lastValChanged = changed;
+ return (0);
+}
+
+/*
+ * Query the get methods to determine what functionality is available
+ * from the hardware function hotkeys.
+ */
+static uint8_t
+acpi_fujitsu_check_hardware(struct acpi_fujitsu_softc *sc)
+{
+ int val;
+ struct acpi_softc *acpi_sc;
+
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_ASSERT(fujitsu);
+ /* save the hotkey bitmask */
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gsif.name, &(sc->gsif.value)))) {
+ sc->gsif.exists = 0;
+ device_printf(sc->dev, "Couldn't query bitmask value\n");
+ } else {
+ sc->gsif.exists = 1;
+ }
+
+ /* System Volume Level */
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gvol.name, &val))) {
+ sc->gvol.exists = 0;
+ } else {
+ sc->gvol.exists = 1;
+ }
+
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gbll.name, &val))) {
+ sc->gbll.exists = 0;
+ } else {
+ sc->gbll.exists = 1;
+ }
+
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->ghks.name, &val))) {
+ sc->ghks.exists = 0;
+ } else {
+ sc->ghks.exists = 1;
+ }
+
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gmou.name, &val))) {
+ sc->gmou.exists = 0;
+ } else {
+ sc->gmou.exists = 1;
+ }
+
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->rbll.name, &val))) {
+ sc->rbll.exists = 0;
+ } else {
+ sc->rbll.exists = 1;
+ }
+
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->rvol.name, &val))) {
+ sc->rvol.exists = 0;
+ } else {
+ sc->rvol.exists = 1;
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Query each of the ACPI control methods that contain information we're
+ * interested in. We check the return values from the control methods and
+ * adjust any state variables if they should be adjusted.
+ */
+static uint8_t
+acpi_fujitsu_update(struct acpi_fujitsu_softc *sc)
+{
+ int changed;
+ struct acpi_softc *acpi_sc;
+
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_ASSERT(fujitsu);
+ if(sc->gsif.exists)
+ changed = sc->gsif.value & acpi_fujitsu_method_get(sc,METHOD_GHKS);
+ else
+ changed = 0;
+
+ /* System Volume Level */
+ if(sc->gvol.exists) {
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gvol.name, &(sc->gvol.value)))) {
+ device_printf(sc->dev, "Couldn't query volume level\n");
+ return (FALSE);
+ }
+
+ if (changed & VOLUME_CHANGED) {
+ sc->bIsMuted =
+ (uint8_t)((sc->gvol.value & VOLUME_MUTE_BIT) != 0);
+
+ /* Clear the modification bit */
+ sc->gvol.value &= VOLUME_SETTING_BITS;
+
+ if (sc->bIsMuted) {
+ acpi_UserNotify("FUJITSU", sc->handle, FN_MUTE);
+ ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now mute\n");
+ } else
+ ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now %d\n",
+ sc->gvol.value);
+
+ acpi_UserNotify("FUJITSU", sc->handle, FN_VOLUME);
+ }
+ }
+
+ /* Internal mouse pointer (eraserhead) */
+ if(sc->gmou.exists) {
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gmou.name, &(sc->gmou.value)))) {
+ device_printf(sc->dev, "Couldn't query pointer state\n");
+ return (FALSE);
+ }
+
+ if (changed & MOUSE_CHANGED) {
+ sc->bIntPtrEnabled = (uint8_t)(sc->gmou.value & 0x1);
+
+ /* Clear the modification bit */
+ sc->gmou.value &= MOUSE_SETTING_BITS;
+
+ acpi_UserNotify("FUJITSU", sc->handle, FN_POINTER_ENABLE);
+
+ ACPI_VPRINT(sc->dev, acpi_sc, "Internal pointer is now %s\n",
+ (sc->bIntPtrEnabled) ? "enabled" : "disabled");
+ }
+ }
+
+ /* Screen Brightness Level */
+ if(sc->gbll.exists) {
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ sc->gbll.name, &(sc->gbll.value)))) {
+ device_printf(sc->dev, "Couldn't query brightness level\n");
+ return (FALSE);
+ }
+
+ if (changed & BRIGHT_CHANGED) {
+ /* No state to record here. */
+
+ /* Clear the modification bit */
+ sc->gbll.value &= BRIGHTNESS_SETTING_BITS;
+
+ acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS);
+
+ ACPI_VPRINT(sc->dev, acpi_sc, "Brightness level is now %d\n",
+ sc->gbll.value);
+ }
+ }
+
+ sc->lastValChanged = changed;
+ return (TRUE);
+}
diff --git a/sys/dev/acpi_support/acpi_ibm.c b/sys/dev/acpi_support/acpi_ibm.c
new file mode 100644
index 000000000000..ca67716ed8d6
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_ibm.c
@@ -0,0 +1,948 @@
+/*-
+ * Copyright (c) 2004 Takanori Watanabe
+ * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops.
+ * Inspired by the ibm-acpi and tpb projects which implement these features
+ * on Linux.
+ *
+ * acpi-ibm: <http://ibm-acpi.sourceforge.net/>
+ * tpb: <http://www.nongnu.org/tpb/>
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <machine/cpufunc.h>
+#include <contrib/dev/acpica/acpi.h>
+#include "acpi_if.h"
+#include <sys/module.h>
+#include <dev/acpica/acpivar.h>
+#include <dev/led/led.h>
+#include <sys/sysctl.h>
+#include <isa/rtc.h>
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("IBM")
+
+/* Internal methods */
+#define ACPI_IBM_METHOD_EVENTS 1
+#define ACPI_IBM_METHOD_EVENTMASK 2
+#define ACPI_IBM_METHOD_HOTKEY 3
+#define ACPI_IBM_METHOD_BRIGHTNESS 4
+#define ACPI_IBM_METHOD_VOLUME 5
+#define ACPI_IBM_METHOD_MUTE 6
+#define ACPI_IBM_METHOD_THINKLIGHT 7
+#define ACPI_IBM_METHOD_BLUETOOTH 8
+#define ACPI_IBM_METHOD_WLAN 9
+#define ACPI_IBM_METHOD_FANSPEED 10
+#define ACPI_IBM_METHOD_FANLEVEL 11
+#define ACPI_IBM_METHOD_FANSTATUS 12
+#define ACPI_IBM_METHOD_THERMAL 13
+
+/* Hotkeys/Buttons */
+#define IBM_RTC_HOTKEY1 0x64
+#define IBM_RTC_MASK_HOME (1 << 0)
+#define IBM_RTC_MASK_SEARCH (1 << 1)
+#define IBM_RTC_MASK_MAIL (1 << 2)
+#define IBM_RTC_MASK_WLAN (1 << 5)
+#define IBM_RTC_HOTKEY2 0x65
+#define IBM_RTC_MASK_THINKPAD (1 << 3)
+#define IBM_RTC_MASK_ZOOM (1 << 5)
+#define IBM_RTC_MASK_VIDEO (1 << 6)
+#define IBM_RTC_MASK_HIBERNATE (1 << 7)
+#define IBM_RTC_THINKLIGHT 0x66
+#define IBM_RTC_MASK_THINKLIGHT (1 << 4)
+#define IBM_RTC_SCREENEXPAND 0x67
+#define IBM_RTC_MASK_SCREENEXPAND (1 << 5)
+#define IBM_RTC_BRIGHTNESS 0x6c
+#define IBM_RTC_MASK_BRIGHTNESS (1 << 5)
+#define IBM_RTC_VOLUME 0x6e
+#define IBM_RTC_MASK_VOLUME (1 << 7)
+
+/* Embedded Controller registers */
+#define IBM_EC_BRIGHTNESS 0x31
+#define IBM_EC_MASK_BRI 0x7
+#define IBM_EC_VOLUME 0x30
+#define IBM_EC_MASK_VOL 0xf
+#define IBM_EC_MASK_MUTE (1 << 6)
+#define IBM_EC_FANSTATUS 0x2F
+#define IBM_EC_MASK_FANLEVEL 0x3f
+#define IBM_EC_MASK_FANDISENGAGED (1 << 6)
+#define IBM_EC_MASK_FANSTATUS (1 << 7)
+#define IBM_EC_FANSPEED 0x84
+
+/* CMOS Commands */
+#define IBM_CMOS_VOLUME_DOWN 0
+#define IBM_CMOS_VOLUME_UP 1
+#define IBM_CMOS_VOLUME_MUTE 2
+#define IBM_CMOS_BRIGHTNESS_UP 4
+#define IBM_CMOS_BRIGHTNESS_DOWN 5
+
+/* ACPI methods */
+#define IBM_NAME_KEYLIGHT "KBLT"
+#define IBM_NAME_WLAN_BT_GET "GBDC"
+#define IBM_NAME_WLAN_BT_SET "SBDC"
+#define IBM_NAME_MASK_BT (1 << 1)
+#define IBM_NAME_MASK_WLAN (1 << 2)
+#define IBM_NAME_THERMAL_GET "TMP7"
+#define IBM_NAME_THERMAL_UPDT "UPDT"
+
+#define IBM_NAME_EVENTS_STATUS_GET "DHKC"
+#define IBM_NAME_EVENTS_MASK_GET "DHKN"
+#define IBM_NAME_EVENTS_STATUS_SET "MHKC"
+#define IBM_NAME_EVENTS_MASK_SET "MHKM"
+#define IBM_NAME_EVENTS_GET "MHKP"
+#define IBM_NAME_EVENTS_AVAILMASK "MHKA"
+
+#define ABS(x) (((x) < 0)? -(x) : (x))
+
+struct acpi_ibm_softc {
+ device_t dev;
+ ACPI_HANDLE handle;
+
+ /* Embedded controller */
+ device_t ec_dev;
+ ACPI_HANDLE ec_handle;
+
+ /* CMOS */
+ ACPI_HANDLE cmos_handle;
+
+ /* Fan status */
+ ACPI_HANDLE fan_handle;
+ int fan_levels;
+
+ /* Keylight commands and states */
+ ACPI_HANDLE light_handle;
+ int light_cmd_on;
+ int light_cmd_off;
+ int light_val;
+ int light_get_supported;
+ int light_set_supported;
+
+ /* led(4) interface */
+ struct cdev *led_dev;
+ int led_busy;
+ int led_state;
+
+ int wlan_bt_flags;
+ int thermal_updt_supported;
+
+ unsigned int events_availmask;
+ unsigned int events_initialmask;
+ int events_mask_supported;
+ int events_enable;
+
+ struct sysctl_ctx_list *sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+};
+
+static struct {
+ char *name;
+ int method;
+ char *description;
+ int access;
+} acpi_ibm_sysctls[] = {
+ {
+ .name = "events",
+ .method = ACPI_IBM_METHOD_EVENTS,
+ .description = "ACPI events enable",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "eventmask",
+ .method = ACPI_IBM_METHOD_EVENTMASK,
+ .description = "ACPI eventmask",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "hotkey",
+ .method = ACPI_IBM_METHOD_HOTKEY,
+ .description = "Key Status",
+ .access = CTLTYPE_INT | CTLFLAG_RD
+ },
+ {
+ .name = "lcd_brightness",
+ .method = ACPI_IBM_METHOD_BRIGHTNESS,
+ .description = "LCD Brightness",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "volume",
+ .method = ACPI_IBM_METHOD_VOLUME,
+ .description = "Volume",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "mute",
+ .method = ACPI_IBM_METHOD_MUTE,
+ .description = "Mute",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "thinklight",
+ .method = ACPI_IBM_METHOD_THINKLIGHT,
+ .description = "Thinklight enable",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "bluetooth",
+ .method = ACPI_IBM_METHOD_BLUETOOTH,
+ .description = "Bluetooth enable",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "wlan",
+ .method = ACPI_IBM_METHOD_WLAN,
+ .description = "WLAN enable",
+ .access = CTLTYPE_INT | CTLFLAG_RD
+ },
+ {
+ .name = "fan_speed",
+ .method = ACPI_IBM_METHOD_FANSPEED,
+ .description = "Fan speed",
+ .access = CTLTYPE_INT | CTLFLAG_RD
+ },
+ {
+ .name = "fan_level",
+ .method = ACPI_IBM_METHOD_FANLEVEL,
+ .description = "Fan level",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+ {
+ .name = "fan",
+ .method = ACPI_IBM_METHOD_FANSTATUS,
+ .description = "Fan enable",
+ .access = CTLTYPE_INT | CTLFLAG_RW
+ },
+
+ { NULL, 0, NULL, 0 }
+};
+
+ACPI_SERIAL_DECL(ibm, "ACPI IBM extras");
+
+static int acpi_ibm_probe(device_t dev);
+static int acpi_ibm_attach(device_t dev);
+static int acpi_ibm_detach(device_t dev);
+
+static void ibm_led(void *softc, int onoff);
+static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused);
+
+static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method);
+static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method);
+static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val);
+
+static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val);
+static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS);
+static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context);
+
+static device_method_t acpi_ibm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_ibm_probe),
+ DEVMETHOD(device_attach, acpi_ibm_attach),
+ DEVMETHOD(device_detach, acpi_ibm_detach),
+
+ {0, 0}
+};
+
+static driver_t acpi_ibm_driver = {
+ "acpi_ibm",
+ acpi_ibm_methods,
+ sizeof(struct acpi_ibm_softc),
+};
+
+static devclass_t acpi_ibm_devclass;
+
+DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass,
+ 0, 0);
+MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
+static char *ibm_ids[] = {"IBM0057", "IBM0068", NULL};
+
+static void
+ibm_led(void *softc, int onoff)
+{
+ struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ if (sc->led_busy)
+ return;
+
+ sc->led_busy = 1;
+ sc->led_state = onoff;
+
+ AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc);
+}
+
+static void
+ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused)
+{
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ ACPI_SERIAL_BEGIN(ibm);
+ acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state);
+ ACPI_SERIAL_END(ibm);
+
+ sc->led_busy = 0;
+}
+
+static int
+acpi_ibm_probe(device_t dev)
+{
+ if (acpi_disabled("ibm") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL ||
+ device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "IBM ThinkPad ACPI Extras");
+
+ return (0);
+}
+
+static int
+acpi_ibm_attach(device_t dev)
+{
+ struct acpi_ibm_softc *sc;
+ devclass_t ec_devclass;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->handle = acpi_get_handle(dev);
+
+ /* Look for the first embedded controller */
+ if (!(ec_devclass = devclass_find ("acpi_ec"))) {
+ if (bootverbose)
+ device_printf(dev, "Couldn't find acpi_ec devclass\n");
+ return (EINVAL);
+ }
+ if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
+ if (bootverbose)
+ device_printf(dev, "Couldn't find acpi_ec device\n");
+ return (EINVAL);
+ }
+ sc->ec_handle = acpi_get_handle(sc->ec_dev);
+
+ ACPI_SERIAL_BEGIN(ibm);
+
+ /* Get the sysctl tree */
+ sc->sysctl_ctx = device_get_sysctl_ctx(dev);
+ sc->sysctl_tree = device_get_sysctl_tree(dev);
+
+ /* Look for event mask and hook up the nodes */
+ sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
+ IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
+
+ if (sc->events_mask_supported) {
+ SYSCTL_ADD_INT(sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "initialmask", CTLFLAG_RD,
+ &sc->events_initialmask, 0, "Initial eventmask");
+
+ /* The availmask is the bitmask of supported events */
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
+ sc->events_availmask = 0xffffffff;
+
+ SYSCTL_ADD_INT(sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "availmask", CTLFLAG_RD,
+ &sc->events_availmask, 0, "Mask of supported events");
+ }
+
+ /* Hook up proc nodes */
+ for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
+ if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method))
+ continue;
+
+ SYSCTL_ADD_PROC(sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access,
+ sc, i, acpi_ibm_sysctl, "I",
+ acpi_ibm_sysctls[i].description);
+ }
+
+ /* Hook up thermal node */
+ if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) {
+ SYSCTL_ADD_PROC(sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "thermal", CTLTYPE_STRING | CTLFLAG_RD,
+ sc, 0, acpi_ibm_thermal_sysctl, "I",
+ "Thermal zones");
+ }
+
+ ACPI_SERIAL_END(ibm);
+
+ /* Handle notifies */
+ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_ibm_notify, dev);
+
+ /* Hook up light to led(4) */
+ if (sc->light_set_supported)
+ sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val);
+
+ return (0);
+}
+
+static int
+acpi_ibm_detach(device_t dev)
+{
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
+
+ struct acpi_ibm_softc *sc = device_get_softc(dev);
+
+ /* Disable events and restore eventmask */
+ ACPI_SERIAL_BEGIN(ibm);
+ acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0);
+ acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask);
+ ACPI_SERIAL_END(ibm);
+
+ AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify);
+
+ if (sc->led_dev != NULL)
+ led_destroy(sc->led_dev);
+
+ return (0);
+}
+
+static int
+acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val)
+{
+ ACPI_OBJECT arg[2];
+ ACPI_OBJECT_LIST args;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(ibm);
+
+ args.Count = 2;
+ args.Pointer = arg;
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[1].Type = ACPI_TYPE_INTEGER;
+
+ for (int i = 0; i < 32; ++i) {
+ arg[0].Integer.Value = i+1;
+ arg[1].Integer.Value = (((1 << i) & val) != 0);
+ status = AcpiEvaluateObject(sc->handle,
+ IBM_NAME_EVENTS_MASK_SET, &args, NULL);
+
+ if (ACPI_FAILURE(status))
+ return (status);
+ }
+
+ return (0);
+}
+
+static int
+acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_ibm_softc *sc;
+ int arg;
+ int error = 0;
+ int function;
+ int method;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
+ function = oidp->oid_arg2;
+ method = acpi_ibm_sysctls[function].method;
+
+ ACPI_SERIAL_BEGIN(ibm);
+ arg = acpi_ibm_sysctl_get(sc, method);
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+
+ /* Sanity check */
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /* Update */
+ error = acpi_ibm_sysctl_set(sc, method, arg);
+
+out:
+ ACPI_SERIAL_END(ibm);
+ return (error);
+}
+
+static int
+acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method)
+{
+ ACPI_INTEGER val_ec;
+ int val = 0, key;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(ibm);
+
+ switch (method) {
+ case ACPI_IBM_METHOD_EVENTS:
+ acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val);
+ break;
+
+ case ACPI_IBM_METHOD_EVENTMASK:
+ if (sc->events_mask_supported)
+ acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val);
+ break;
+
+ case ACPI_IBM_METHOD_HOTKEY:
+ /*
+ * Construct the hotkey as a bitmask as illustrated below.
+ * Note that whenever a key was pressed, the respecting bit
+ * toggles and nothing else changes.
+ * +--+--+-+-+-+-+-+-+-+-+-+-+
+ * |11|10|9|8|7|6|5|4|3|2|1|0|
+ * +--+--+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | | |
+ * | | | | | | | | | | | +- Home Button
+ * | | | | | | | | | | +--- Search Button
+ * | | | | | | | | | +----- Mail Button
+ * | | | | | | | | +------- Thinkpad Button
+ * | | | | | | | +--------- Zoom (Fn + Space)
+ * | | | | | | +----------- WLAN Button
+ * | | | | | +------------- Video Button
+ * | | | | +--------------- Hibernate Button
+ * | | | +----------------- Thinklight Button
+ * | | +------------------- Screen expand (Fn + F8)
+ * | +--------------------- Brightness
+ * +------------------------ Volume/Mute
+ */
+ key = rtcin(IBM_RTC_HOTKEY1);
+ val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key;
+ key = rtcin(IBM_RTC_HOTKEY2);
+ val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key;
+ val |= (IBM_RTC_MASK_ZOOM & key) >> 1;
+ key = rtcin(IBM_RTC_THINKLIGHT);
+ val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
+ key = rtcin(IBM_RTC_SCREENEXPAND);
+ val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
+ key = rtcin(IBM_RTC_BRIGHTNESS);
+ val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5;
+ key = rtcin(IBM_RTC_VOLUME);
+ val |= (IBM_RTC_MASK_VOLUME & key) << 4;
+ break;
+
+ case ACPI_IBM_METHOD_BRIGHTNESS:
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
+ val = val_ec & IBM_EC_MASK_BRI;
+ break;
+
+ case ACPI_IBM_METHOD_VOLUME:
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
+ val = val_ec & IBM_EC_MASK_VOL;
+ break;
+
+ case ACPI_IBM_METHOD_MUTE:
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
+ val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
+ break;
+
+ case ACPI_IBM_METHOD_THINKLIGHT:
+ if (sc->light_get_supported)
+ acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val);
+ else
+ val = sc->light_val;
+ break;
+
+ case ACPI_IBM_METHOD_BLUETOOTH:
+ acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
+ sc->wlan_bt_flags = val;
+ val = ((val & IBM_NAME_MASK_BT) != 0);
+ break;
+
+ case ACPI_IBM_METHOD_WLAN:
+ acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
+ sc->wlan_bt_flags = val;
+ val = ((val & IBM_NAME_MASK_WLAN) != 0);
+ break;
+
+ case ACPI_IBM_METHOD_FANSPEED:
+ if (sc->fan_handle) {
+ if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val)))
+ val = -1;
+ }
+ else {
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2);
+ val = val_ec;
+ }
+ break;
+
+ case ACPI_IBM_METHOD_FANLEVEL:
+ /*
+ * The IBM_EC_FANSTATUS register works as follows:
+ * Bit 0-5 indicate the level at which the fan operates. Only
+ * values between 0 and 7 have an effect. Everything
+ * above 7 is treated the same as level 7
+ * Bit 6 overrides the fan speed limit if set to 1
+ * Bit 7 indicates at which mode the fan operates:
+ * manual (0) or automatic (1)
+ */
+ if (!sc->fan_handle) {
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
+ val = val_ec & IBM_EC_MASK_FANLEVEL;
+ }
+ break;
+
+ case ACPI_IBM_METHOD_FANSTATUS:
+ if (!sc->fan_handle) {
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
+ val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS;
+ }
+ else
+ val = -1;
+ break;
+ }
+
+ return (val);
+}
+
+static int
+acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
+{
+ int val, step;
+ ACPI_INTEGER val_ec;
+ ACPI_OBJECT Arg;
+ ACPI_OBJECT_LIST Args;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(ibm);
+
+ switch (method) {
+ case ACPI_IBM_METHOD_EVENTS:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg);
+ if (ACPI_FAILURE(status))
+ return (status);
+ if (sc->events_mask_supported)
+ return acpi_ibm_eventmask_set(sc, sc->events_availmask);
+ break;
+
+ case ACPI_IBM_METHOD_EVENTMASK:
+ if (sc->events_mask_supported)
+ return acpi_ibm_eventmask_set(sc, arg);
+ break;
+
+ case ACPI_IBM_METHOD_BRIGHTNESS:
+ if (arg < 0 || arg > 7)
+ return (EINVAL);
+
+ if (sc->cmos_handle) {
+ /* Read the current brightness */
+ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
+ if (ACPI_FAILURE(status))
+ return (status);
+ val = val_ec & IBM_EC_MASK_BRI;
+
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN;
+
+ step = (arg > val) ? 1 : -1;
+ for (int i = val; i != arg; i += step) {
+ status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
+ if (ACPI_FAILURE(status))
+ break;
+ }
+ }
+ return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
+ break;
+
+ case ACPI_IBM_METHOD_VOLUME:
+ if (arg < 0 || arg > 14)
+ return (EINVAL);
+
+ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
+ if (ACPI_FAILURE(status))
+ return (status);
+
+ if (sc->cmos_handle) {
+ val = val_ec & IBM_EC_MASK_VOL;
+
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN;
+
+ step = (arg > val) ? 1 : -1;
+ for (int i = val; i != arg; i += step) {
+ status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
+ if (ACPI_FAILURE(status))
+ break;
+ }
+ }
+ return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1);
+ break;
+
+ case ACPI_IBM_METHOD_MUTE:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
+ if (ACPI_FAILURE(status))
+ return (status);
+
+ if (sc->cmos_handle) {
+ val = val_ec & IBM_EC_MASK_VOL;
+
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
+
+ status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
+ if (ACPI_FAILURE(status))
+ break;
+ }
+ return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1);
+ break;
+
+ case ACPI_IBM_METHOD_THINKLIGHT:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ if (sc->light_set_supported) {
+ Args.Count = 1;
+ Args.Pointer = &Arg;
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
+
+ status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL);
+ if (ACPI_SUCCESS(status))
+ sc->light_val = arg;
+ return (status);
+ }
+ break;
+
+ case ACPI_IBM_METHOD_BLUETOOTH:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
+ return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
+ break;
+
+ case ACPI_IBM_METHOD_FANLEVEL:
+ if (arg < 0 || arg > 7)
+ return (EINVAL);
+
+ if (!sc->fan_handle) {
+ /* Read the current fanstatus */
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
+ val = val_ec & (~IBM_EC_MASK_FANLEVEL);
+
+ return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1);
+ }
+ break;
+
+ case ACPI_IBM_METHOD_FANSTATUS:
+ if (arg < 0 || arg > 1)
+ return (EINVAL);
+
+ if (!sc->fan_handle) {
+ /* Read the current fanstatus */
+ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
+
+ return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS,
+ (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+static int
+acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
+{
+ int dummy;
+ ACPI_OBJECT_TYPE cmos_t;
+ ACPI_HANDLE ledb_handle;
+
+ switch (method) {
+ case ACPI_IBM_METHOD_EVENTS:
+ /* Events are disabled by default */
+ return (TRUE);
+
+ case ACPI_IBM_METHOD_EVENTMASK:
+ return (sc->events_mask_supported);
+
+ case ACPI_IBM_METHOD_HOTKEY:
+ case ACPI_IBM_METHOD_BRIGHTNESS:
+ case ACPI_IBM_METHOD_VOLUME:
+ case ACPI_IBM_METHOD_MUTE:
+ /* EC is required here, which was aready checked before */
+ return (TRUE);
+
+ case ACPI_IBM_METHOD_THINKLIGHT:
+ sc->cmos_handle = NULL;
+ sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
+ sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val));
+
+ if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) ||
+ ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) ||
+ ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) &&
+ ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
+ cmos_t == ACPI_TYPE_METHOD) {
+ sc->light_cmd_on = 0x0c;
+ sc->light_cmd_off = 0x0d;
+ sc->cmos_handle = sc->light_handle;
+ }
+ else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) {
+ sc->light_cmd_on = 1;
+ sc->light_cmd_off = 0;
+ }
+ else
+ sc->light_handle = NULL;
+
+ sc->light_set_supported = (sc->light_handle &&
+ ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle)));
+
+ if (sc->light_get_supported)
+ return (TRUE);
+
+ if (sc->light_set_supported) {
+ sc->light_val = 0;
+ return (TRUE);
+ }
+
+ return (FALSE);
+
+ case ACPI_IBM_METHOD_BLUETOOTH:
+ case ACPI_IBM_METHOD_WLAN:
+ if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy)))
+ return (TRUE);
+ return (FALSE);
+
+ case ACPI_IBM_METHOD_FANSPEED:
+ /*
+ * Some models report the fan speed in levels from 0-7
+ * Newer models report it contiguously
+ */
+ sc->fan_levels =
+ (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) ||
+ ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle)));
+ return (TRUE);
+
+ case ACPI_IBM_METHOD_FANLEVEL:
+ case ACPI_IBM_METHOD_FANSTATUS:
+ /*
+ * Fan status is only supported on those models,
+ * which report fan RPM contiguously, not in levels
+ */
+ if (sc->fan_levels)
+ return (FALSE);
+ return (TRUE);
+
+ case ACPI_IBM_METHOD_THERMAL:
+ if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) {
+ sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy));
+ return (TRUE);
+ }
+ return (FALSE);
+ }
+ return (FALSE);
+}
+
+static int
+acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_ibm_softc *sc;
+ int error = 0;
+ char temp_cmd[] = "TMP0";
+ int temp[8];
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
+
+ ACPI_SERIAL_BEGIN(ibm);
+
+ for (int i = 0; i < 8; ++i) {
+ temp_cmd[3] = '0' + i;
+
+ /*
+ * The TMPx methods seem to return +/- 128 or 0
+ * when the respecting sensor is not available
+ */
+ if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
+ &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0)
+ temp[i] = -1;
+ else if (sc->thermal_updt_supported)
+ /* Temperature is reported in tenth of Kelvin */
+ temp[i] = (temp[i] - 2732 + 5) / 10;
+ }
+
+ error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req);
+
+ ACPI_SERIAL_END(ibm);
+ return (error);
+}
+
+static void
+acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ int event, arg, type;
+ device_t dev = context;
+ struct acpi_ibm_softc *sc = device_get_softc(dev);
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
+
+ if (notify != 0x80)
+ device_printf(dev, "Unknown notify\n");
+
+ for (;;) {
+ acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event);
+
+ if (event == 0)
+ break;
+
+
+ type = (event >> 12) & 0xf;
+ arg = event & 0xfff;
+ switch (type) {
+ case 1:
+ if (!(sc->events_availmask & (1 << (arg - 1)))) {
+ device_printf(dev, "Unknown key %d\n", arg);
+ break;
+ }
+
+ /* Notify devd(8) */
+ acpi_UserNotify("IBM", h, (arg & 0xff));
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/sys/dev/acpi_support/acpi_panasonic.c b/sys/dev/acpi_support/acpi_panasonic.c
new file mode 100644
index 000000000000..f922233092a3
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_panasonic.c
@@ -0,0 +1,514 @@
+/*-
+ * Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
+ * Copyright (c) 2004 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/power.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("Panasonic")
+
+/* Debug */
+#undef ACPI_PANASONIC_DEBUG
+
+/* Operations */
+#define HKEY_SET 0
+#define HKEY_GET 1
+
+/* Functions */
+#define HKEY_REG_LCD_BRIGHTNESS_MAX_AC 0x02
+#define HKEY_REG_LCD_BRIGHTNESS_MIN_AC 0x03
+#define HKEY_REG_LCD_BRIGHTNESS_AC 0x04
+#define HKEY_REG_LCD_BRIGHTNESS_MAX_DC 0x05
+#define HKEY_REG_LCD_BRIGHTNESS_MIN_DC 0x06
+#define HKEY_REG_LCD_BRIGHTNESS_DC 0x07
+#define HKEY_REG_SOUND_MUTE 0x08
+
+/* Field definitions */
+#define HKEY_LCD_BRIGHTNESS_BITS 4
+#define HKEY_LCD_BRIGHTNESS_DIV ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1)
+
+struct acpi_panasonic_softc {
+ device_t dev;
+ ACPI_HANDLE handle;
+
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+
+ eventhandler_tag power_evh;
+};
+
+/* Prototype for HKEY functions for getting/setting a value. */
+typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *);
+
+static int acpi_panasonic_probe(device_t dev);
+static int acpi_panasonic_attach(device_t dev);
+static int acpi_panasonic_detach(device_t dev);
+static int acpi_panasonic_shutdown(device_t dev);
+static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS);
+static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index);
+static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index,
+ ACPI_INTEGER val);
+static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc,
+ ACPI_HANDLE h, UINT32 *arg);
+static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc,
+ ACPI_HANDLE h, UINT32 key);
+static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify,
+ void *context);
+static void acpi_panasonic_power_profile(void *arg);
+
+static hkey_fn_t hkey_lcd_brightness_max;
+static hkey_fn_t hkey_lcd_brightness_min;
+static hkey_fn_t hkey_lcd_brightness;
+static hkey_fn_t hkey_sound_mute;
+ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras");
+
+/* Table of sysctl names and HKEY functions to call. */
+static struct {
+ char *name;
+ hkey_fn_t *handler;
+} sysctl_table[] = {
+ /* name, handler */
+ {"lcd_brightness_max", hkey_lcd_brightness_max},
+ {"lcd_brightness_min", hkey_lcd_brightness_min},
+ {"lcd_brightness", hkey_lcd_brightness},
+ {"sound_mute", hkey_sound_mute},
+ {NULL, NULL}
+};
+
+static device_method_t acpi_panasonic_methods[] = {
+ DEVMETHOD(device_probe, acpi_panasonic_probe),
+ DEVMETHOD(device_attach, acpi_panasonic_attach),
+ DEVMETHOD(device_detach, acpi_panasonic_detach),
+ DEVMETHOD(device_shutdown, acpi_panasonic_shutdown),
+
+ {0, 0}
+};
+
+static driver_t acpi_panasonic_driver = {
+ "acpi_panasonic",
+ acpi_panasonic_methods,
+ sizeof(struct acpi_panasonic_softc),
+};
+
+static devclass_t acpi_panasonic_devclass;
+
+DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver,
+ acpi_panasonic_devclass, 0, 0);
+MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1);
+
+static int
+acpi_panasonic_probe(device_t dev)
+{
+ static char *mat_ids[] = { "MAT0019", NULL };
+
+ if (acpi_disabled("panasonic") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL ||
+ device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Panasonic Notebook Hotkeys");
+ return (0);
+}
+
+static int
+acpi_panasonic_attach(device_t dev)
+{
+ struct acpi_panasonic_softc *sc;
+ struct acpi_softc *acpi_sc;
+ ACPI_STATUS status;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->handle = acpi_get_handle(dev);
+
+ acpi_sc = acpi_device_get_parent_softc(dev);
+
+ /* Build sysctl tree */
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
+ "panasonic", CTLFLAG_RD, 0, "");
+ for (i = 0; sysctl_table[i].name != NULL; i++) {
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ sysctl_table[i].name,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
+ sc, i, acpi_panasonic_sysctl, "I", "");
+ }
+
+#if 0
+ /* Activate hotkeys */
+ status = AcpiEvaluateObject(sc->handle, "", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "enable FN keys failed\n");
+ sysctl_ctx_free(&sc->sysctl_ctx);
+ return (ENXIO);
+ }
+#endif
+
+ /* Handle notifies */
+ status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_panasonic_notify, sc);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "couldn't install notify handler - %s\n",
+ AcpiFormatException(status));
+ sysctl_ctx_free(&sc->sysctl_ctx);
+ return (ENXIO);
+ }
+
+ /* Install power profile event handler */
+ sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change,
+ acpi_panasonic_power_profile, sc->handle, 0);
+
+ return (0);
+}
+
+static int
+acpi_panasonic_detach(device_t dev)
+{
+ struct acpi_panasonic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Remove power profile event handler */
+ EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh);
+
+ /* Remove notify handler */
+ AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_panasonic_notify);
+
+ /* Free sysctl tree */
+ sysctl_ctx_free(&sc->sysctl_ctx);
+
+ return (0);
+}
+
+static int
+acpi_panasonic_shutdown(device_t dev)
+{
+ struct acpi_panasonic_softc *sc;
+ int mute;
+
+ /* Mute the main audio during reboot to prevent static burst to speaker. */
+ sc = device_get_softc(dev);
+ mute = 1;
+ hkey_sound_mute(sc->handle, HKEY_SET, &mute);
+ return (0);
+}
+
+static int
+acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_panasonic_softc *sc;
+ UINT32 arg;
+ int function, error;
+ hkey_fn_t *handler;
+
+ sc = (struct acpi_panasonic_softc *)oidp->oid_arg1;
+ function = oidp->oid_arg2;
+ handler = sysctl_table[function].handler;
+
+ /* Get the current value from the appropriate function. */
+ ACPI_SERIAL_BEGIN(panasonic);
+ error = handler(sc->handle, HKEY_GET, &arg);
+ if (error != 0)
+ goto out;
+
+ /* Send the current value to the user and return if no new value. */
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /* Set the new value via the appropriate function. */
+ error = handler(sc->handle, HKEY_SET, &arg);
+
+out:
+ ACPI_SERIAL_END(panasonic);
+ return (error);
+}
+
+static ACPI_INTEGER
+acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index)
+{
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *res;
+ ACPI_INTEGER ret;
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ ret = -1;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ buf.Pointer = NULL;
+ AcpiEvaluateObject(h, "SINF", NULL, &buf);
+ res = (ACPI_OBJECT *)buf.Pointer;
+ if (res->Type == ACPI_TYPE_PACKAGE)
+ ret = res->Package.Elements[index].Integer.Value;
+ AcpiOsFree(buf.Pointer);
+
+ return (ret);
+}
+
+static void
+acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, ACPI_INTEGER val)
+{
+ ACPI_OBJECT_LIST args;
+ ACPI_OBJECT obj[2];
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ obj[0].Type = ACPI_TYPE_INTEGER;
+ obj[0].Integer.Value = index;
+ obj[1].Type = ACPI_TYPE_INTEGER;
+ obj[1].Integer.Value = val;
+ args.Count = 2;
+ args.Pointer = obj;
+ AcpiEvaluateObject(h, "SSET", &args, NULL);
+}
+
+static int
+hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val)
+{
+ int reg;
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
+ HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC;
+
+ switch (op) {
+ case HKEY_SET:
+ return (EPERM);
+ break;
+ case HKEY_GET:
+ *val = acpi_panasonic_sinf(h, reg);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val)
+{
+ int reg;
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
+ HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC;
+
+ switch (op) {
+ case HKEY_SET:
+ return (EPERM);
+ break;
+ case HKEY_GET:
+ *val = acpi_panasonic_sinf(h, reg);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val)
+{
+ int reg;
+ UINT32 max, min;
+
+ reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
+ HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC;
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ switch (op) {
+ case HKEY_SET:
+ hkey_lcd_brightness_max(h, HKEY_GET, &max);
+ hkey_lcd_brightness_min(h, HKEY_GET, &min);
+ if (*val < min || *val > max)
+ return (EINVAL);
+ acpi_panasonic_sset(h, reg, *val);
+ break;
+ case HKEY_GET:
+ *val = acpi_panasonic_sinf(h, reg);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val)
+{
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ switch (op) {
+ case HKEY_SET:
+ if (*val != 0 && *val != 1)
+ return (EINVAL);
+ acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val);
+ break;
+ case HKEY_GET:
+ *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
+ UINT32 *arg)
+{
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *res;
+ ACPI_INTEGER val;
+ int status;
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ status = ENXIO;
+
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ buf.Pointer = NULL;
+ AcpiEvaluateObject(h, "HINF", NULL, &buf);
+ res = (ACPI_OBJECT *)buf.Pointer;
+ if (res->Type != ACPI_TYPE_INTEGER) {
+ device_printf(sc->dev, "HINF returned non-integer\n");
+ goto end;
+ }
+ val = res->Integer.Value;
+#ifdef ACPI_PANASONIC_DEBUG
+ device_printf(sc->dev, "%s button Fn+F%d\n",
+ (val & 0x80) ? "Pressed" : "Released",
+ (int)(val & 0x7f));
+#endif
+ if ((val & 0x7f) > 0 && (val & 0x7f) < 11) {
+ *arg = val;
+ status = 0;
+ }
+end:
+ if (buf.Pointer)
+ AcpiOsFree(buf.Pointer);
+
+ return (status);
+}
+
+static void
+acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
+ UINT32 key)
+{
+ struct acpi_softc *acpi_sc;
+ int arg, max, min;
+
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+
+ ACPI_SERIAL_ASSERT(panasonic);
+ switch (key) {
+ case 1:
+ /* Decrease LCD brightness. */
+ hkey_lcd_brightness_max(h, HKEY_GET, &max);
+ hkey_lcd_brightness_min(h, HKEY_GET, &min);
+ hkey_lcd_brightness(h, HKEY_GET, &arg);
+ arg -= max / HKEY_LCD_BRIGHTNESS_DIV;
+ if (arg < min)
+ arg = min;
+ else if (arg > max)
+ arg = max;
+ hkey_lcd_brightness(h, HKEY_SET, &arg);
+ break;
+ case 2:
+ /* Increase LCD brightness. */
+ hkey_lcd_brightness_max(h, HKEY_GET, &max);
+ hkey_lcd_brightness_min(h, HKEY_GET, &min);
+ hkey_lcd_brightness(h, HKEY_GET, &arg);
+ arg += max / HKEY_LCD_BRIGHTNESS_DIV;
+ if (arg < min)
+ arg = min;
+ else if (arg > max)
+ arg = max;
+ hkey_lcd_brightness(h, HKEY_SET, &arg);
+ break;
+ case 4:
+ /* Toggle sound mute. */
+ hkey_sound_mute(h, HKEY_GET, &arg);
+ if (arg)
+ arg = 0;
+ else
+ arg = 1;
+ hkey_sound_mute(h, HKEY_SET, &arg);
+ break;
+ case 7:
+ /* Suspend. */
+ acpi_event_sleep_button_sleep(acpi_sc);
+ break;
+ }
+}
+
+static void
+acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_panasonic_softc *sc;
+ UINT32 key = 0;
+
+ sc = (struct acpi_panasonic_softc *)context;
+
+ switch (notify) {
+ case 0x80:
+ ACPI_SERIAL_BEGIN(panasonic);
+ if (acpi_panasonic_hkey_event(sc, h, &key) == 0) {
+ acpi_panasonic_hkey_action(sc, h, key);
+ acpi_UserNotify("Panasonic", h, (uint8_t)key);
+ }
+ ACPI_SERIAL_END(panasonic);
+ break;
+ default:
+ device_printf(sc->dev, "unknown notify: %#x\n", notify);
+ break;
+ }
+}
+
+static void
+acpi_panasonic_power_profile(void *arg)
+{
+ ACPI_HANDLE handle;
+ UINT32 brightness;
+
+ handle = (ACPI_HANDLE)arg;
+
+ /* Reset current brightness according to new power state. */
+ ACPI_SERIAL_BEGIN(panasonic);
+ hkey_lcd_brightness(handle, HKEY_GET, &brightness);
+ hkey_lcd_brightness(handle, HKEY_SET, &brightness);
+ ACPI_SERIAL_END(panasonic);
+}
diff --git a/sys/dev/acpi_support/acpi_sony.c b/sys/dev/acpi_support/acpi_sony.c
new file mode 100644
index 000000000000..88195c2d7836
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_sony.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2004 Takanori Watanabe
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <contrib/dev/acpica/acpi.h>
+#include "acpi_if.h"
+#include <sys/module.h>
+#include <dev/acpica/acpivar.h>
+#include <sys/sysctl.h>
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("Sony")
+
+#define ACPI_SONY_GET_BRIGHTNESS "GBRT"
+#define ACPI_SONY_SET_BRIGHTNESS "SBRT"
+#define ACPI_SONY_GET_PID "GPID"
+
+/*
+ * SNY5001
+ * [GS]BRT [GS]PBR [GS]CTR [GS]PCR [GS]CMI [CDPW GCDP]? GWDP PWAK PWRN
+ *
+ */
+
+struct acpi_sony_softc {
+ int pid;
+};
+static struct acpi_sony_name_list
+{
+ char *nodename;
+ char *getmethod;
+ char *setmethod;
+ char *comment;
+} acpi_sony_oids[] = {
+ { "brightness", "GBRT", "SBRT", "Display Brightness"},
+ { "ctr", "GCTR", "SCTR", "??"},
+ { "pcr", "GPCR", "SPCR", "???"},
+#if 0
+ { "cmi", "GCMI", "SCMI", "????"},
+#endif
+ { "wdp", "GWDP", NULL, "?????"},
+ { "cdp", "GCDP", "CDPW", "CD Power"}, /*shares [\GL03]&0x8 flag*/
+ { "azp", "GAZP", "AZPW", "Audio Power"},
+ { NULL, NULL, NULL }
+};
+
+static int acpi_sony_probe(device_t dev);
+static int acpi_sony_attach(device_t dev);
+static int acpi_sony_detach(device_t dev);
+static int sysctl_acpi_sony_gen_handler(SYSCTL_HANDLER_ARGS);
+
+static device_method_t acpi_sony_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_sony_probe),
+ DEVMETHOD(device_attach, acpi_sony_attach),
+ DEVMETHOD(device_detach, acpi_sony_detach),
+
+ {0, 0}
+};
+
+static driver_t acpi_sony_driver = {
+ "acpi_sony",
+ acpi_sony_methods,
+ sizeof(struct acpi_sony_softc),
+};
+
+static devclass_t acpi_sony_devclass;
+
+DRIVER_MODULE(acpi_sony, acpi, acpi_sony_driver, acpi_sony_devclass,
+ 0, 0);
+MODULE_DEPEND(acpi_sony, acpi, 1, 1, 1);
+static char *sny_id[] = {"SNY5001", NULL};
+
+static int
+acpi_sony_probe(device_t dev)
+{
+ int ret = ENXIO;
+
+ if (ACPI_ID_PROBE(device_get_parent(dev), dev, sny_id)) {
+ device_set_desc(dev, "Sony notebook controller");
+ ret = 0;
+ }
+ return (ret);
+}
+
+static int
+acpi_sony_attach(device_t dev)
+{
+ struct acpi_sony_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ acpi_GetInteger(acpi_get_handle(dev), ACPI_SONY_GET_PID, &sc->pid);
+ device_printf(dev, "PID %x\n", sc->pid);
+ for (i = 0 ; acpi_sony_oids[i].nodename != NULL; i++){
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ i, acpi_sony_oids[i].nodename , CTLTYPE_INT |
+ ((acpi_sony_oids[i].setmethod)? CTLFLAG_RW: CTLFLAG_RD),
+ dev, i, sysctl_acpi_sony_gen_handler, "I",
+ acpi_sony_oids[i].comment);
+ }
+ return (0);
+}
+
+static int
+acpi_sony_detach(device_t dev)
+{
+ return (0);
+}
+
+#if 0
+static int
+acpi_sony_suspend(device_t dev)
+{
+ struct acpi_sony_softc *sc = device_get_softc(dev);
+ return (0);
+}
+
+static int
+acpi_sony_resume(device_t dev)
+{
+ return (0);
+}
+#endif
+
+static int
+sysctl_acpi_sony_gen_handler(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = arg1;
+ int function = oidp->oid_arg2;
+ int error = 0, val;
+
+ acpi_GetInteger(acpi_get_handle(dev),
+ acpi_sony_oids[function].getmethod, &val);
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error || !req->newptr || !acpi_sony_oids[function].setmethod)
+ return (error);
+ acpi_SetInteger(acpi_get_handle(dev),
+ acpi_sony_oids[function].setmethod, val);
+ return (0);
+}
diff --git a/sys/dev/acpi_support/acpi_toshiba.c b/sys/dev/acpi_support/acpi_toshiba.c
new file mode 100644
index 000000000000..d15fe7e4ae4a
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_toshiba.c
@@ -0,0 +1,566 @@
+/*-
+ * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("Toshiba")
+
+/*
+ * Toshiba HCI interface definitions
+ *
+ * HCI is Toshiba's "Hardware Control Interface" which is supposed to
+ * be uniform across all their models. Ideally we would just call
+ * dedicated ACPI methods instead of using this primitive interface.
+ * However, the ACPI methods seem to be incomplete in some areas (for
+ * example they allow setting, but not reading, the LCD brightness
+ * value), so this is still useful.
+ */
+
+#define METHOD_HCI "GHCI"
+#define METHOD_HCI_ENABLE "ENAB"
+#define METHOD_VIDEO "DSSX"
+
+/* Operations */
+#define HCI_SET 0xFF00
+#define HCI_GET 0xFE00
+
+/* Return codes */
+#define HCI_SUCCESS 0x0000
+#define HCI_FAILURE 0x1000
+#define HCI_NOT_SUPPORTED 0x8000
+#define HCI_EMPTY 0x8C00
+
+/* Functions */
+#define HCI_REG_LCD_BACKLIGHT 0x0002
+#define HCI_REG_FAN 0x0004
+#define HCI_REG_SYSTEM_EVENT 0x0016
+#define HCI_REG_VIDEO_OUTPUT 0x001C
+#define HCI_REG_HOTKEY_EVENT 0x001E
+#define HCI_REG_LCD_BRIGHTNESS 0x002A
+#define HCI_REG_CPU_SPEED 0x0032
+
+/* Field definitions */
+#define HCI_FAN_SHIFT 7
+#define HCI_LCD_BRIGHTNESS_BITS 3
+#define HCI_LCD_BRIGHTNESS_SHIFT (16 - HCI_LCD_BRIGHTNESS_BITS)
+#define HCI_LCD_BRIGHTNESS_MAX ((1 << HCI_LCD_BRIGHTNESS_BITS) - 1)
+#define HCI_VIDEO_OUTPUT_FLAG 0x0100
+#define HCI_VIDEO_OUTPUT_LCD 0x1
+#define HCI_VIDEO_OUTPUT_CRT 0x2
+#define HCI_VIDEO_OUTPUT_TV 0x4
+#define HCI_CPU_SPEED_BITS 3
+#define HCI_CPU_SPEED_SHIFT (16 - HCI_CPU_SPEED_BITS)
+#define HCI_CPU_SPEED_MAX ((1 << HCI_CPU_SPEED_BITS) - 1)
+
+/* Key press/release events. */
+#define FN_F1_PRESS 0x013B
+#define FN_F1_RELEASE 0x01BB
+#define FN_F2_PRESS 0x013C
+#define FN_F2_RELEASE 0x01BC
+#define FN_F3_PRESS 0x013D
+#define FN_F3_RELEASE 0x01BD
+#define FN_F4_PRESS 0x013E
+#define FN_F4_RELEASE 0x01BE
+#define FN_F5_PRESS 0x013F
+#define FN_F5_RELEASE 0x01BF
+#define FN_F6_PRESS 0x0140
+#define FN_F6_RELEASE 0x01C0
+#define FN_F7_PRESS 0x0141
+#define FN_F7_RELEASE 0x01C1
+#define FN_F8_PRESS 0x0142
+#define FN_F8_RELEASE 0x01C2
+#define FN_F9_PRESS 0x0143
+#define FN_F9_RELEASE 0x01C3
+#define FN_BS_PRESS 0x010E
+#define FN_BS_RELEASE 0x018E
+#define FN_ESC_PRESS 0x0101
+#define FN_ESC_RELEASE 0x0181
+#define FN_KNJ_PRESS 0x0129
+#define FN_KNJ_RELEASE 0x01A9
+
+/* HCI register definitions. */
+#define HCI_WORDS 6 /* Number of registers */
+#define HCI_REG_AX 0 /* Operation, then return value */
+#define HCI_REG_BX 1 /* Function */
+#define HCI_REG_CX 2 /* Argument (in or out) */
+#define HCI_REG_DX 3 /* Unused? */
+#define HCI_REG_SI 4 /* Unused? */
+#define HCI_REG_DI 5 /* Unused? */
+
+struct acpi_toshiba_softc {
+ device_t dev;
+ ACPI_HANDLE handle;
+ ACPI_HANDLE video_handle;
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+};
+
+/* Prototype for HCI functions for getting/setting a value. */
+typedef int hci_fn_t(ACPI_HANDLE, int, UINT32 *);
+
+static int acpi_toshiba_probe(device_t dev);
+static int acpi_toshiba_attach(device_t dev);
+static int acpi_toshiba_detach(device_t dev);
+static int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS);
+static hci_fn_t hci_force_fan;
+static hci_fn_t hci_video_output;
+static hci_fn_t hci_lcd_brightness;
+static hci_fn_t hci_lcd_backlight;
+static hci_fn_t hci_cpu_speed;
+static int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg);
+static void hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h,
+ UINT32 key);
+static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify,
+ void *context);
+static int acpi_toshiba_video_probe(device_t dev);
+static int acpi_toshiba_video_attach(device_t dev);
+
+ACPI_SERIAL_DECL(toshiba, "ACPI Toshiba Extras");
+
+/* Table of sysctl names and HCI functions to call. */
+static struct {
+ char *name;
+ hci_fn_t *handler;
+} sysctl_table[] = {
+ /* name, handler */
+ {"force_fan", hci_force_fan},
+ {"video_output", hci_video_output},
+ {"lcd_brightness", hci_lcd_brightness},
+ {"lcd_backlight", hci_lcd_backlight},
+ {"cpu_speed", hci_cpu_speed},
+ {NULL, NULL}
+};
+
+static device_method_t acpi_toshiba_methods[] = {
+ DEVMETHOD(device_probe, acpi_toshiba_probe),
+ DEVMETHOD(device_attach, acpi_toshiba_attach),
+ DEVMETHOD(device_detach, acpi_toshiba_detach),
+
+ {0, 0}
+};
+
+static driver_t acpi_toshiba_driver = {
+ "acpi_toshiba",
+ acpi_toshiba_methods,
+ sizeof(struct acpi_toshiba_softc),
+};
+
+static devclass_t acpi_toshiba_devclass;
+DRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass,
+ 0, 0);
+MODULE_DEPEND(acpi_toshiba, acpi, 1, 1, 1);
+
+static device_method_t acpi_toshiba_video_methods[] = {
+ DEVMETHOD(device_probe, acpi_toshiba_video_probe),
+ DEVMETHOD(device_attach, acpi_toshiba_video_attach),
+
+ {0, 0}
+};
+
+static driver_t acpi_toshiba_video_driver = {
+ "acpi_toshiba_video",
+ acpi_toshiba_video_methods,
+ 0,
+};
+
+static devclass_t acpi_toshiba_video_devclass;
+DRIVER_MODULE(acpi_toshiba_video, acpi, acpi_toshiba_video_driver,
+ acpi_toshiba_video_devclass, 0, 0);
+MODULE_DEPEND(acpi_toshiba_video, acpi, 1, 1, 1);
+
+static int enable_fn_keys = 1;
+TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys);
+
+/*
+ * HID Model
+ * -------------------------------------
+ * TOS6200 Libretto L Series
+ * Dynabook Satellite 2455
+ * Dynabook SS 3500
+ * TOS6207 Dynabook SS2110 Series
+ * TOS6208 SPA40
+ */
+static int
+acpi_toshiba_probe(device_t dev)
+{
+ static char *tosh_ids[] = { "TOS6200", "TOS6207", "TOS6208", NULL };
+
+ if (acpi_disabled("toshiba") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, tosh_ids) == NULL ||
+ device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Toshiba HCI Extras");
+ return (0);
+}
+
+static int
+acpi_toshiba_attach(device_t dev)
+{
+ struct acpi_toshiba_softc *sc;
+ struct acpi_softc *acpi_sc;
+ ACPI_STATUS status;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->handle = acpi_get_handle(dev);
+
+ acpi_sc = acpi_device_get_parent_softc(dev);
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
+ "toshiba", CTLFLAG_RD, 0, "");
+
+ for (i = 0; sysctl_table[i].name != NULL; i++) {
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ sysctl_table[i].name,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
+ sc, i, acpi_toshiba_sysctl, "I", "");
+ }
+
+ if (enable_fn_keys != 0) {
+ status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE,
+ NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "enable FN keys failed\n");
+ sysctl_ctx_free(&sc->sysctl_ctx);
+ return (ENXIO);
+ }
+ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_toshiba_notify, sc);
+ }
+
+ return (0);
+}
+
+static int
+acpi_toshiba_detach(device_t dev)
+{
+ struct acpi_toshiba_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (enable_fn_keys != 0) {
+ AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_toshiba_notify);
+ }
+ sysctl_ctx_free(&sc->sysctl_ctx);
+
+ return (0);
+}
+
+static int
+acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_toshiba_softc *sc;
+ UINT32 arg;
+ int function, error = 0;
+ hci_fn_t *handler;
+
+ sc = (struct acpi_toshiba_softc *)oidp->oid_arg1;
+ function = oidp->oid_arg2;
+ handler = sysctl_table[function].handler;
+
+ /* Get the current value from the appropriate function. */
+ ACPI_SERIAL_BEGIN(toshiba);
+ error = handler(sc->handle, HCI_GET, &arg);
+ if (error != 0)
+ goto out;
+
+ /* Send the current value to the user and return if no new value. */
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /* Set the new value via the appropriate function. */
+ error = handler(sc->handle, HCI_SET, &arg);
+
+out:
+ ACPI_SERIAL_END(toshiba);
+ return (error);
+}
+
+static int
+hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state)
+{
+ int ret;
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ if (op == HCI_SET) {
+ if (*state < 0 || *state > 1)
+ return (EINVAL);
+ *state <<= HCI_FAN_SHIFT;
+ }
+ ret = hci_call(h, op, HCI_REG_FAN, state);
+ if (ret == 0 && op == HCI_GET)
+ *state >>= HCI_FAN_SHIFT;
+ return (ret);
+}
+
+static int
+hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output)
+{
+ int ret;
+ ACPI_STATUS status;
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ if (op == HCI_SET) {
+ if (*video_output < 1 || *video_output > 7)
+ return (EINVAL);
+ if (h == NULL)
+ return (ENXIO);
+ *video_output |= HCI_VIDEO_OUTPUT_FLAG;
+ status = acpi_SetInteger(h, METHOD_VIDEO, *video_output);
+ if (ACPI_SUCCESS(status))
+ ret = 0;
+ else
+ ret = ENXIO;
+ } else {
+ ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output);
+ if (ret == 0)
+ *video_output &= 0xff;
+ }
+
+ return (ret);
+}
+
+static int
+hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness)
+{
+ int ret;
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ if (op == HCI_SET) {
+ if (*brightness < 0 || *brightness > HCI_LCD_BRIGHTNESS_MAX)
+ return (EINVAL);
+ *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
+ }
+ ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness);
+ if (ret == 0 && op == HCI_GET)
+ *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
+ return (ret);
+}
+
+static int
+hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight)
+{
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ if (op == HCI_SET) {
+ if (*backlight < 0 || *backlight > 1)
+ return (EINVAL);
+ }
+ return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight));
+}
+
+static int
+hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed)
+{
+ int ret;
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ if (op == HCI_SET) {
+ if (*speed < 0 || *speed > HCI_CPU_SPEED_MAX)
+ return (EINVAL);
+ *speed <<= HCI_CPU_SPEED_SHIFT;
+ }
+ ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed);
+ if (ret == 0 && op == HCI_GET)
+ *speed >>= HCI_CPU_SPEED_SHIFT;
+ return (ret);
+}
+
+static int
+hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg)
+{
+ ACPI_OBJECT_LIST args;
+ ACPI_BUFFER results;
+ ACPI_OBJECT obj[HCI_WORDS];
+ ACPI_OBJECT *res;
+ int status, i, ret;
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ status = ENXIO;
+
+ for (i = 0; i < HCI_WORDS; i++) {
+ obj[i].Type = ACPI_TYPE_INTEGER;
+ obj[i].Integer.Value = 0;
+ }
+ obj[HCI_REG_AX].Integer.Value = op;
+ obj[HCI_REG_BX].Integer.Value = function;
+ if (op == HCI_SET)
+ obj[HCI_REG_CX].Integer.Value = *arg;
+
+ args.Count = HCI_WORDS;
+ args.Pointer = obj;
+ results.Pointer = NULL;
+ results.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results)))
+ goto end;
+ res = (ACPI_OBJECT *)results.Pointer;
+ if (!ACPI_PKG_VALID(res, HCI_WORDS)) {
+ printf("toshiba: invalid package!\n");
+ return (ENXIO);
+ }
+
+ acpi_PkgInt32(res, HCI_REG_AX, &ret);
+ if (ret == HCI_SUCCESS) {
+ if (op == HCI_GET)
+ acpi_PkgInt32(res, HCI_REG_CX, arg);
+ status = 0;
+ } else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET &&
+ ret == HCI_NOT_SUPPORTED) {
+ /*
+ * Sometimes system events are disabled without us requesting
+ * it. This workaround attempts to re-enable them.
+ *
+ * XXX This call probably shouldn't be recursive. Queueing
+ * a task via AcpiOsQueueForExecution() might be better.
+ */
+ i = 1;
+ hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i);
+ }
+
+end:
+ if (results.Pointer != NULL)
+ AcpiOsFree(results.Pointer);
+
+ return (status);
+}
+
+/*
+ * Perform a few actions based on the keypress. Users can extend this
+ * functionality by reading the keystrokes we send to devd(8).
+ */
+static void
+hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key)
+{
+ UINT32 arg;
+
+ ACPI_SERIAL_ASSERT(toshiba);
+ switch (key) {
+ case FN_F6_RELEASE:
+ /* Decrease LCD brightness. */
+ hci_lcd_brightness(h, HCI_GET, &arg);
+ if (arg-- == 0)
+ arg = 0;
+ else
+ hci_lcd_brightness(h, HCI_SET, &arg);
+ break;
+ case FN_F7_RELEASE:
+ /* Increase LCD brightness. */
+ hci_lcd_brightness(h, HCI_GET, &arg);
+ if (arg++ == 7)
+ arg = 7;
+ else
+ hci_lcd_brightness(h, HCI_SET, &arg);
+ break;
+ case FN_F5_RELEASE:
+ /* Cycle through video outputs. */
+ hci_video_output(h, HCI_GET, &arg);
+ arg = (arg + 1) % 7;
+ hci_video_output(sc->video_handle, HCI_SET, &arg);
+ break;
+ case FN_F8_RELEASE:
+ /* Toggle LCD backlight. */
+ hci_lcd_backlight(h, HCI_GET, &arg);
+ arg = (arg != 0) ? 0 : 1;
+ hci_lcd_backlight(h, HCI_SET, &arg);
+ break;
+ case FN_ESC_RELEASE:
+ /* Toggle forcing fan on. */
+ hci_force_fan(h, HCI_GET, &arg);
+ arg = (arg != 0) ? 0 : 1;
+ hci_force_fan(h, HCI_SET, &arg);
+ break;
+ }
+}
+
+static void
+acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+ struct acpi_toshiba_softc *sc;
+ UINT32 key;
+
+ sc = (struct acpi_toshiba_softc *)context;
+
+ if (notify == 0x80) {
+ ACPI_SERIAL_BEGIN(toshiba);
+ while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) {
+ hci_key_action(sc, h, key);
+ acpi_UserNotify("TOSHIBA", h, (uint8_t)key);
+ }
+ ACPI_SERIAL_END(toshiba);
+ } else
+ device_printf(sc->dev, "unknown notify: 0x%x\n", notify);
+}
+
+/*
+ * Toshiba video pseudo-device to provide the DSSX method.
+ *
+ * HID Model
+ * -------------------------------------
+ * TOS6201 Libretto L Series
+ */
+static int
+acpi_toshiba_video_probe(device_t dev)
+{
+ static char *vid_ids[] = { "TOS6201", NULL };
+
+ if (acpi_disabled("toshiba") ||
+ ACPI_ID_PROBE(device_get_parent(dev), dev, vid_ids) == NULL ||
+ device_get_unit(dev) != 0)
+ return (ENXIO);
+
+ device_quiet(dev);
+ device_set_desc(dev, "Toshiba Video");
+ return (0);
+}
+
+static int
+acpi_toshiba_video_attach(device_t dev)
+{
+ struct acpi_toshiba_softc *sc;
+
+ sc = devclass_get_softc(acpi_toshiba_devclass, 0);
+ if (sc == NULL)
+ return (ENXIO);
+ sc->video_handle = acpi_get_handle(dev);
+ return (0);
+}