summaryrefslogtreecommitdiff
path: root/sys/dev/acpi/acpiasus.c
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2008-04-24 13:57:50 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2008-04-24 13:57:50 +0000
commit5d3c7a90f0cfb3687465f662bc29e2019ebed1c5 (patch)
tree54e5f048648055973f82b487ae5e7e64d35f3379 /sys/dev/acpi/acpiasus.c
parent66b2507b3752825201c63607db8ba87d3abfe991 (diff)
Introduce acpiasus(4), a driver for the ACPI based hotkeys found in many
ASUS laptops (including the ASUS EeePC) - largely based on NetBSD's asus(4) driver. On the ASUS EeePC this allows us to enable/disable wireless, change screen brightness and use the volume keys. ok jsg@, weingart@
Diffstat (limited to 'sys/dev/acpi/acpiasus.c')
-rw-r--r--sys/dev/acpi/acpiasus.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/sys/dev/acpi/acpiasus.c b/sys/dev/acpi/acpiasus.c
new file mode 100644
index 00000000000..78e23f2bc02
--- /dev/null
+++ b/sys/dev/acpi/acpiasus.c
@@ -0,0 +1,214 @@
+/* $OpenBSD: acpiasus.c,v 1.1 2008/04/24 13:57:49 jsing Exp $ */
+/* $NetBSD: asus_acpi.c,v 1.2.2.2 2008/04/03 12:42:37 mjf Exp $ */
+
+/*-
+ * Copyright (c) 2007, 2008 Jared D. McNeill <jmcneill@invisible.ca>
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jared D. McNeill.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * ASUS ACPI hotkeys driver.
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+#include <sys/workq.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#include "audio.h"
+#include "wskbd.h"
+
+struct acpiasus_softc {
+ struct device sc_dev;
+
+ struct acpi_softc *sc_acpi;
+ struct aml_node *sc_devnode;
+
+ void *sc_powerhook;
+};
+
+#define ASUS_NOTIFY_WirelessSwitch 0x10
+#define ASUS_NOTIFY_BrightnessLow 0x20
+#define ASUS_NOTIFY_BrightnessHigh 0x2f
+#define ASUS_NOTIFY_DisplayCycle 0x30
+#define ASUS_NOTIFY_TaskSwitch 0x12
+#define ASUS_NOTIFY_VolumeMute 0x13
+#define ASUS_NOTIFY_VolumeDown 0x14
+#define ASUS_NOTIFY_VolumeUp 0x15
+
+#define ASUS_SDSP_LCD 0x01
+#define ASUS_SDSP_CRT 0x02
+#define ASUS_SDSP_TV 0x04
+#define ASUS_SDSP_DVI 0x08
+#define ASUS_SDSP_ALL \
+ (ASUS_SDSP_LCD | ASUS_SDSP_CRT | ASUS_SDSP_TV | ASUS_SDSP_DVI)
+
+int acpiasus_match(struct device *, void *, void *);
+void acpiasus_attach(struct device *, struct device *, void *);
+void acpiasus_init(struct device *);
+int acpiasus_notify(struct aml_node *, int, void *);
+void acpiasus_power(int, void *);
+
+#if NAUDIO > 0 && NWSKBD > 0
+extern int wskbd_set_mixervolume(long dir);
+#endif
+
+struct cfattach acpiasus_ca = {
+ sizeof(struct acpiasus_softc), acpiasus_match, acpiasus_attach
+};
+
+struct cfdriver acpiasus_cd = {
+ NULL, "acpiasus", DV_DULL
+};
+
+int
+acpiasus_match(struct device *parent, void *match, void *aux)
+{
+ struct acpi_attach_args *aa = aux;
+ struct cfdata *cf = match;
+
+ if (aa->aaa_name == NULL ||
+ strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
+ aa->aaa_table != NULL)
+ return 0;
+
+ return 1;
+}
+
+void
+acpiasus_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
+ struct acpi_attach_args *aa = aux;
+
+ sc->sc_acpi = (struct acpi_softc *)parent;
+ sc->sc_devnode = aa->aaa_node->child;
+
+ sc->sc_powerhook = powerhook_establish(acpiasus_power, sc);
+
+ printf("\n");
+
+ acpiasus_init(self);
+
+ aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev,
+ acpiasus_notify, sc, ACPIDEV_NOPOLL);
+}
+
+void
+acpiasus_init(struct device *self)
+{
+ struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
+ struct aml_value cmd;
+ struct aml_value ret;
+
+ cmd.type = AML_OBJTYPE_INTEGER;
+ cmd.v_integer = 0x40; /* Disable ASL display switching. */
+
+ if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "INIT", 1, &cmd, &ret))
+ printf("%s: no INIT\n", DEVNAME(sc));
+}
+
+int
+acpiasus_notify(struct aml_node *node, int notify, void *arg)
+{
+ struct acpiasus_softc *sc = arg;
+
+ if (notify >= ASUS_NOTIFY_BrightnessLow &&
+ notify <= ASUS_NOTIFY_BrightnessHigh) {
+#if ACPIASUS_DEBUG
+ printf("%s: brightness %d percent\n", DEVNAME(sc),
+ (notify & 0xf) * 100 / 0xf);
+#endif
+ return 0;
+ }
+
+ switch (notify) {
+ case ASUS_NOTIFY_WirelessSwitch: /* Handled by AML. */
+ break;
+ case ASUS_NOTIFY_TaskSwitch:
+ break;
+ case ASUS_NOTIFY_DisplayCycle:
+ break;
+#if NAUDIO > 0 && NWSKBD > 0
+ case ASUS_NOTIFY_VolumeMute:
+ workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
+ (void *)(long)0, NULL);
+ break;
+ case ASUS_NOTIFY_VolumeDown:
+ workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
+ (void *)(long)-1, NULL);
+ break;
+ case ASUS_NOTIFY_VolumeUp:
+ workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
+ (void *)(long)1, NULL);
+ break;
+#else
+ case ASUS_NOTIFY_VolumeMute:
+ case ASUS_NOTIFY_VolumeDown:
+ case ASUS_NOTIFY_VolumeUp:
+ break;
+#endif
+ default:
+ printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
+ break;
+ }
+
+ return 0;
+}
+
+void
+acpiasus_power(int why, void *arg)
+{
+ struct acpiasus_softc *sc = (struct acpiasus_softc *)arg;
+ struct aml_value cmd;
+ struct aml_value ret;
+
+ switch (why) {
+ case PWR_STANDBY:
+ case PWR_SUSPEND:
+ break;
+ case PWR_RESUME:
+ acpiasus_init(arg);
+
+ cmd.type = AML_OBJTYPE_INTEGER;
+ cmd.v_integer = ASUS_SDSP_LCD;
+
+ if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SDSP", 1,
+ &cmd, &ret))
+ printf("%s: no SDSP\n", DEVNAME(sc));
+ break;
+ }
+}