--- /dev/null
+.\" $OpenBSD: acpials.4,v 1.1 2016/07/30 16:25:04 jcs Exp $
+.\"
+.\" Copyright (c) 2016 joshua stein <jcs@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and 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.
+.\"
+.Dd $Mdocdate: July 30 2016 $
+.Dt ACPIALS 4
+.Os
+.Sh NAME
+.Nm acpials
+.Nd ACPI ambient light sensor
+.Sh SYNOPSIS
+.Cd "acpials* at acpi?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports ACPI ambient light sensor devices and provides
+information about the ambient light environment in which the system is
+currently operating.
+Ambient light illuminance is read every second and can be monitored
+using
+.Xr sysctl 8
+or
+.Xr sensorsd 8 .
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 6.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An joshua stein Aq Mt jcs@openbsd.org .
--- /dev/null
+/* $OpenBSD: acpials.c,v 1.1 2016/07/30 16:25:04 jcs Exp $ */
+/*
+ * Ambient Light Sensor device driver
+ * ACPI 5.0 spec section 9.2
+ *
+ * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
+ *
+ * Permission to use, copy, modify, and 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/device.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 <sys/sensors.h>
+
+/* #define ACPIALS_DEBUG */
+
+#ifdef ACPIALS_DEBUG
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+struct acpials_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 ksensor sc_sensor;
+ struct ksensordev sc_sensordev;
+ struct sensor_task *sc_sensor_task;
+};
+
+int acpials_match(struct device *, void *, void *);
+void acpials_attach(struct device *, struct device *, void *);
+int acpials_read(struct acpials_softc *);
+int acpials_notify(struct aml_node *, int, void *);
+void acpials_addtask(void *);
+void acpials_update(void *, int);
+
+struct cfattach acpials_ca = {
+ sizeof(struct acpials_softc),
+ acpials_match,
+ acpials_attach,
+};
+
+struct cfdriver acpials_cd = {
+ NULL, "acpials", DV_DULL
+};
+
+const char *acpials_hids[] = {
+ "ACPI0008",
+};
+
+int
+acpials_match(struct device *parent, void *match, void *aux)
+{
+ struct acpi_attach_args *aa = aux;
+ struct cfdata *cf = match;
+
+ return (acpi_matchhids(aa, acpials_hids, cf->cf_driver->cd_name));
+}
+
+void
+acpials_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct acpials_softc *sc = (struct acpials_softc *)self;
+ struct acpi_attach_args *aa = aux;
+ int64_t st;
+
+ sc->sc_acpi = (struct acpi_softc *)parent;
+ sc->sc_devnode = aa->aaa_node;
+
+ printf(": %s\n", sc->sc_devnode->name);
+
+ if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
+ st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
+ if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
+ (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
+ return;
+
+ if (acpials_read(sc))
+ return;
+
+ strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
+ sizeof(sc->sc_sensordev.xname));
+ strlcpy(sc->sc_sensor.desc, "ambient light sensor",
+ sizeof(sc->sc_sensor.desc));
+ sc->sc_sensor.type = SENSOR_LUX;
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
+
+ /*
+ * aml_register_notify with ACPIDEV_POLL is too slow (10 second
+ * intervals), so register the task with sensors so we can specify the
+ * interval, which will then just inject an acpi task and tell it to
+ * wakeup to handle the task.
+ */
+ if (!(sc->sc_sensor_task = sensor_task_register(sc, acpials_addtask,
+ 1))) {
+ printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /*
+ * But also install an event handler in case AML Notify()s us of any
+ * large changes - 9.2.7
+ */
+ aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpials_notify,
+ sc, ACPIDEV_NOPOLL);
+
+ sensordev_install(&sc->sc_sensordev);
+}
+
+int
+acpials_read(struct acpials_softc *sc)
+{
+ int64_t ali = 0;
+
+ /* 9.2.2 - "Current ambient light illuminance reading in lux" */
+ if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_ALI", 0, NULL,
+ &ali))
+ return 1;
+
+ sc->sc_sensor.value = (ali * 1000000);
+
+ return 0;
+}
+
+int
+acpials_notify(struct aml_node *node, int notify_type, void *arg)
+{
+ struct acpials_softc *sc = arg;
+
+ DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
+
+ if (notify_type == 0x80)
+ acpials_read(sc);
+
+ return 0;
+}
+
+void
+acpials_addtask(void *arg)
+{
+ struct acpials_softc *sc = arg;
+
+ acpi_addtask(sc->sc_acpi, acpials_update, sc, 0);
+ acpi_wakeup(sc->sc_acpi);
+}
+
+void
+acpials_update(void *arg0, int arg1)
+{
+ struct acpials_softc *sc = arg0;
+
+ if (acpials_read(sc) == 0) {
+ DPRINTF(("%s: %s: %lld\n", sc->sc_dev.dv_xname, __func__,
+ sc->sc_sensor.value));
+ sc->sc_sensor.flags &= ~SENSOR_FINVALID;
+ } else
+ sc->sc_sensor.flags |= SENSOR_FINVALID;
+}