Line data Source code
1 : /* $OpenBSD: acpials.c,v 1.3 2017/03/13 14:44:37 jcs Exp $ */
2 : /*
3 : * Ambient Light Sensor device driver
4 : * ACPI 5.0 spec section 9.2
5 : *
6 : * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
7 : *
8 : * Permission to use, copy, modify, and distribute this software for any
9 : * purpose with or without fee is hereby granted, provided that the above
10 : * copyright notice and this permission notice appear in all copies.
11 : *
12 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 : */
20 :
21 : #include <sys/param.h>
22 : #include <sys/systm.h>
23 : #include <sys/device.h>
24 :
25 : #include <dev/acpi/acpireg.h>
26 : #include <dev/acpi/acpivar.h>
27 : #include <dev/acpi/acpidev.h>
28 : #include <dev/acpi/amltypes.h>
29 : #include <dev/acpi/dsdt.h>
30 :
31 : #include <sys/sensors.h>
32 :
33 : /* #define ACPIALS_DEBUG */
34 :
35 : #ifdef ACPIALS_DEBUG
36 : #define DPRINTF(x) printf x
37 : #else
38 : #define DPRINTF(x)
39 : #endif
40 :
41 : struct acpials_softc {
42 : struct device sc_dev;
43 :
44 : bus_space_tag_t sc_iot;
45 : bus_space_handle_t sc_ioh;
46 :
47 : struct acpi_softc *sc_acpi;
48 : struct aml_node *sc_devnode;
49 :
50 : struct ksensor sc_sensor;
51 : struct ksensordev sc_sensordev;
52 : struct sensor_task *sc_sensor_task;
53 : };
54 :
55 : int acpials_match(struct device *, void *, void *);
56 : void acpials_attach(struct device *, struct device *, void *);
57 : int acpials_read(struct acpials_softc *);
58 : int acpials_notify(struct aml_node *, int, void *);
59 : void acpials_addtask(void *);
60 : void acpials_update(void *, int);
61 :
62 : const struct cfattach acpials_ca = {
63 : sizeof(struct acpials_softc),
64 : acpials_match,
65 : acpials_attach,
66 : };
67 :
68 : struct cfdriver acpials_cd = {
69 : NULL, "acpials", DV_DULL
70 : };
71 :
72 : const char *acpials_hids[] = {
73 : "ACPI0008",
74 : NULL
75 : };
76 :
77 : extern char *hw_vendor;
78 :
79 : int
80 0 : acpials_match(struct device *parent, void *match, void *aux)
81 : {
82 0 : struct acpi_attach_args *aa = aux;
83 0 : struct cfdata *cf = match;
84 :
85 : /*
86 : * Apple hardware will most likely have asmc(4) which also provides an
87 : * illuminance sensor.
88 : */
89 0 : if (hw_vendor != NULL && strncmp(hw_vendor, "Apple", 5) == 0)
90 0 : return 0;
91 :
92 0 : return (acpi_matchhids(aa, acpials_hids, cf->cf_driver->cd_name));
93 0 : }
94 :
95 : void
96 0 : acpials_attach(struct device *parent, struct device *self, void *aux)
97 : {
98 0 : struct acpials_softc *sc = (struct acpials_softc *)self;
99 0 : struct acpi_attach_args *aa = aux;
100 0 : int64_t st;
101 :
102 0 : sc->sc_acpi = (struct acpi_softc *)parent;
103 0 : sc->sc_devnode = aa->aaa_node;
104 :
105 0 : printf(": %s\n", sc->sc_devnode->name);
106 :
107 0 : if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
108 0 : st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
109 0 : if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
110 : (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
111 0 : return;
112 :
113 0 : if (acpials_read(sc))
114 0 : return;
115 :
116 0 : strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
117 : sizeof(sc->sc_sensordev.xname));
118 0 : strlcpy(sc->sc_sensor.desc, "ambient light sensor",
119 : sizeof(sc->sc_sensor.desc));
120 0 : sc->sc_sensor.type = SENSOR_LUX;
121 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
122 :
123 : /*
124 : * aml_register_notify with ACPIDEV_POLL is too slow (10 second
125 : * intervals), so register the task with sensors so we can specify the
126 : * interval, which will then just inject an acpi task and tell it to
127 : * wakeup to handle the task.
128 : */
129 0 : if (!(sc->sc_sensor_task = sensor_task_register(sc, acpials_addtask,
130 : 1))) {
131 0 : printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
132 0 : return;
133 : }
134 :
135 : /*
136 : * But also install an event handler in case AML Notify()s us of any
137 : * large changes - 9.2.7
138 : */
139 0 : aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpials_notify,
140 : sc, ACPIDEV_NOPOLL);
141 :
142 0 : sensordev_install(&sc->sc_sensordev);
143 0 : }
144 :
145 : int
146 0 : acpials_read(struct acpials_softc *sc)
147 : {
148 0 : int64_t ali = 0;
149 :
150 : /* 9.2.2 - "Current ambient light illuminance reading in lux" */
151 0 : if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_ALI", 0, NULL,
152 : &ali))
153 0 : return 1;
154 :
155 0 : sc->sc_sensor.value = (ali * 1000000);
156 :
157 0 : return 0;
158 0 : }
159 :
160 : int
161 0 : acpials_notify(struct aml_node *node, int notify_type, void *arg)
162 : {
163 0 : struct acpials_softc *sc = arg;
164 :
165 : DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
166 :
167 0 : if (notify_type == 0x80)
168 0 : acpials_read(sc);
169 :
170 0 : return 0;
171 : }
172 :
173 : void
174 0 : acpials_addtask(void *arg)
175 : {
176 0 : struct acpials_softc *sc = arg;
177 :
178 0 : acpi_addtask(sc->sc_acpi, acpials_update, sc, 0);
179 0 : acpi_wakeup(sc->sc_acpi);
180 0 : }
181 :
182 : void
183 0 : acpials_update(void *arg0, int arg1)
184 : {
185 0 : struct acpials_softc *sc = arg0;
186 :
187 0 : if (acpials_read(sc) == 0) {
188 : DPRINTF(("%s: %s: %lld\n", sc->sc_dev.dv_xname, __func__,
189 : sc->sc_sensor.value));
190 0 : sc->sc_sensor.flags &= ~SENSOR_FINVALID;
191 0 : } else
192 0 : sc->sc_sensor.flags |= SENSOR_FINVALID;
193 0 : }
|