--- /dev/null
+/* $OpenBSD: acpisony.c,v 1.1 2010/07/26 11:29:23 pirofti Exp $ */
+/*
+ * Copyright (c) 2010 Paul Irofti <pirofti@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.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 <machine/apmvar.h>
+
+int acpisony_match(struct device *, void *, void *);
+void acpisony_attach(struct device *, struct device *, void *);
+int acpisony_activate(struct device *, int);
+int acpisony_notify(struct aml_node *, int, void *);
+
+#ifdef ACPISONY_DEBUG
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+/* Notifications */
+#define SONY_NOTIFY_FN_KEY 0x90
+
+#define SONY_NOTIFY_BRIGHTNESS_DOWN_PRESSED 0x85
+#define SONY_NOTIFY_BRIGHTNESS_DOWN_RELEASED 0x05
+#define SONY_NOTIFY_BRIGHTNESS_UP_PRESSED 0x86
+#define SONY_NOTIFY_BRIGHTNESS_UP_RELEASED 0x06
+
+#define SONY_NOTIFY_DISPLAY_SWITCH_PRESSED 0x87
+#define SONY_NOTIFY_DISPLAY_SWITCH_RELEASED 0x07
+
+#define SONY_NOTIFY_ZOOM_OUT_PRESSED 0x89
+#define SONY_NOTIFY_ZOOM_OUT_RELEASED 0x09
+
+#define SONY_NOTIFY_ZOOM_IN_PRESSED 0x8a
+#define SONY_NOTIFY_ZOOM_IN_RELEASED 0x0a
+
+#define SONY_NOTIFY_SUSPEND_PRESSED 0x8c
+#define SONY_NOTIFY_SUSPEND_RELEASED 0x0c
+
+struct acpisony_softc {
+ struct device sc_dev;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ struct acpi_softc *sc_acpi;
+ struct aml_node *sc_devnode;
+};
+
+struct cfattach acpisony_ca = {
+ sizeof(struct acpisony_softc), acpisony_match, acpisony_attach,
+ NULL, acpisony_activate
+};
+
+struct cfdriver acpisony_cd = {
+ NULL, "acpisony", DV_DULL
+};
+
+void acpisony_notify_setup(struct acpisony_softc *);
+int acpisony_set_hotkey(struct acpisony_softc *, int, int);
+int acpisony_find_offset(struct acpisony_softc *, int);
+
+void acpisony_brightness_down(struct acpisony_softc *);
+int acpisony_get_brightness(struct acpisony_softc *);
+void acpisony_set_brightness(struct acpisony_softc *, int);
+
+int
+acpisony_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
+acpisony_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct acpisony_softc *sc = (struct acpisony_softc *)self;
+ struct acpi_attach_args *aa = aux;
+
+ sc->sc_acpi = (struct acpi_softc *)parent;
+ sc->sc_devnode = aa->aaa_node;
+
+ printf(": %s\n", sc->sc_devnode->name);
+
+ /* Setup the notification masks */
+ acpisony_notify_setup(sc);
+
+ aml_register_notify(sc->sc_devnode, aa->aaa_dev,
+ acpisony_notify, sc, ACPIDEV_NOPOLL);
+}
+
+int
+acpisony_activate(struct device *self, int act)
+{
+ struct acpisony_softc *sc = (struct acpisony_softc *)self;
+
+ switch (act) {
+ case DVACT_RESUME:
+ acpisony_notify_setup(sc);
+ break;
+ }
+ return 0;
+}
+
+int
+acpisony_notify(struct aml_node *node, int notify, void *arg)
+{
+ struct acpisony_softc *sc = arg;
+ int val, key = 0;
+
+ if (notify == SONY_NOTIFY_FN_KEY) {
+ notify -= 0x90;
+ DPRINTF(("notify = %X", notify));
+
+ if (notify == acpisony_find_offset(sc, 0x100)) {
+ DPRINTF(("key = 0x100\n"));
+ key = 0x100;
+ }
+ if (notify == acpisony_find_offset(sc, 0x127)) {
+ DPRINTF(("key = 0x127\n"));
+ key = 0x127;
+ }
+
+ if (key) {
+ val = acpisony_set_hotkey(sc, key, 0x200);
+ if (val < 0) {
+ printf("returned val = %X", val);
+ return 1;
+ }
+ notify = val & 0xff;
+
+ DPRINTF(("Treat %X events, notify %X\n", key, notify));
+ } else
+ DPRINTF(("rfkill update, notify %X\n", notify));
+ }
+
+ switch (notify) {
+ case SONY_NOTIFY_BRIGHTNESS_DOWN_PRESSED:
+ DPRINTF(("br-down-pressed\n"));
+ acpisony_brightness_down(sc);
+ break;
+ case SONY_NOTIFY_BRIGHTNESS_DOWN_RELEASED:
+ DPRINTF(("br-down-released\n"));
+ break;
+ case SONY_NOTIFY_BRIGHTNESS_UP_PRESSED:
+ DPRINTF(("br-up-pressed\n"));
+ break;
+ case SONY_NOTIFY_BRIGHTNESS_UP_RELEASED:
+ DPRINTF(("br-up-released\n"));
+ break;
+ case SONY_NOTIFY_DISPLAY_SWITCH_PRESSED:
+ DPRINTF(("display-pressed\n"));
+ break;
+ case SONY_NOTIFY_DISPLAY_SWITCH_RELEASED:
+ DPRINTF(("display-released\n"));
+ break;
+ case SONY_NOTIFY_ZOOM_IN_PRESSED:
+ DPRINTF(("zoom-in-pressed\n"));
+ break;
+ case SONY_NOTIFY_ZOOM_IN_RELEASED:
+ DPRINTF(("zoom-in-released\n"));
+ break;
+ case SONY_NOTIFY_ZOOM_OUT_PRESSED:
+ DPRINTF(("zoom-out-pressed\n"));
+ break;
+ case SONY_NOTIFY_ZOOM_OUT_RELEASED:
+ DPRINTF(("zoom-out-released\n"));
+ break;
+ case SONY_NOTIFY_SUSPEND_PRESSED:
+ DPRINTF(("suspend-pressed\n"));
+#ifndef SMALL_KERNEL
+ if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) {
+ sc->sc_acpi->sc_sleepmode = ACPI_STATE_S3;
+ acpi_wakeup(sc->sc_acpi);
+ }
+#endif
+ break;
+ case SONY_NOTIFY_SUSPEND_RELEASED:
+ DPRINTF(("suspend-released\n"));
+ break;
+ default:
+ printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
+ break;
+ }
+
+ return 0;
+}
+
+void
+acpisony_notify_setup(struct acpisony_softc *sc)
+{
+ struct aml_value arg;
+
+ bzero(&arg, sizeof(arg));
+ arg.type = AML_OBJTYPE_INTEGER;
+
+ arg.v_integer = 1;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "ECON", 1, &arg, NULL);
+
+ /* Enable all events */
+ arg.v_integer = 0xffff;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN02", 1, &arg, NULL);
+
+ /* Enable hotkeys */
+ arg.v_integer = 0x04;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN02", 1, &arg, NULL);
+ arg.v_integer = 0x02;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, NULL);
+ arg.v_integer = 0x10;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN02", 1, &arg, NULL);
+ arg.v_integer = 0x00;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, NULL);
+ arg.v_integer = 0x02;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN03", 1, &arg, NULL);
+ arg.v_integer = 0x101;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, NULL);
+}
+
+int
+acpisony_find_offset(struct acpisony_softc *sc, int key)
+{
+ struct aml_value arg, res;
+ int val;
+
+ bzero(&arg, sizeof(arg));
+ arg.type = AML_OBJTYPE_INTEGER;
+
+ for (arg.v_integer = 0x20; arg.v_integer < 0x30; arg.v_integer++) {
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN00", 1, &arg, &res);
+ val = aml_val2int(&res);
+ aml_freevalue(&res);
+ if (val == key) {
+ DPRINTF(("Matched key %X\n", val));
+ return arg.v_integer - 0x20;
+ }
+ }
+
+ return -1;
+}
+
+int
+acpisony_set_hotkey(struct acpisony_softc *sc, int key, int val)
+{
+ int off, rc = -1;
+ struct aml_value res, arg;
+
+ bzero(&arg, sizeof(arg));
+ arg.type = AML_OBJTYPE_INTEGER;
+
+ off = acpisony_find_offset(sc, key);
+ DPRINTF(("off = %X\n", off));
+ if (off < 0)
+ return rc;
+
+ arg.v_integer = off | val;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, &res);
+ rc = aml_val2int(&res);
+ aml_freevalue(&res);
+
+ return rc;
+}
+
+void
+acpisony_brightness_down(struct acpisony_softc *sc)
+{
+ int val;
+
+ val = acpisony_get_brightness(sc);
+ DPRINTF(("current value = %X", val));
+ if (val > 0)
+ val--;
+ else
+ val = 0;
+ DPRINTF(("next value = %X", val));
+ acpisony_set_brightness(sc, val);
+}
+
+int
+acpisony_get_brightness(struct acpisony_softc *sc)
+{
+ struct aml_value res;
+ int val;
+
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "GBRT", 0, NULL, &res);
+ val = aml_val2int(&res);
+ aml_freevalue(&res);
+
+ return val;
+}
+
+void
+acpisony_set_brightness(struct acpisony_softc *sc, int level)
+{
+ struct aml_value arg;
+
+ bzero(&arg, sizeof(arg));
+ arg.type = AML_OBJTYPE_INTEGER;
+ arg.v_integer = level;
+ aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBRT", 1, &arg, NULL);
+ aml_freevalue(&arg);
+}