Line data Source code
1 : /* $OpenBSD: acpibtn.c,v 1.45 2018/07/01 19:40:49 mlarkin Exp $ */
2 : /*
3 : * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
4 : *
5 : * Permission to use, copy, modify, and distribute this software for any
6 : * purpose with or without fee is hereby granted, provided that the above
7 : * copyright notice and this permission notice appear in all copies.
8 : *
9 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 : */
17 :
18 : #include <sys/param.h>
19 : #include <sys/signalvar.h>
20 : #include <sys/systm.h>
21 : #include <sys/device.h>
22 : #include <sys/malloc.h>
23 :
24 : #include <machine/bus.h>
25 : #include <machine/apmvar.h>
26 :
27 : #include <dev/acpi/acpireg.h>
28 : #include <dev/acpi/acpivar.h>
29 : #include <dev/acpi/acpidev.h>
30 : #include <dev/acpi/amltypes.h>
31 : #include <dev/acpi/dsdt.h>
32 :
33 : #include <sys/sensors.h>
34 :
35 : int acpibtn_match(struct device *, void *, void *);
36 : void acpibtn_attach(struct device *, struct device *, void *);
37 : int acpibtn_notify(struct aml_node *, int, void *);
38 : int acpibtn_activate(struct device *, int);
39 :
40 : struct acpibtn_softc {
41 : struct device sc_dev;
42 :
43 : bus_space_tag_t sc_iot;
44 : bus_space_handle_t sc_ioh;
45 :
46 : struct acpi_softc *sc_acpi;
47 : struct aml_node *sc_devnode;
48 :
49 : struct ksensor sc_sens;
50 : struct ksensordev sc_sensdev;
51 :
52 : int sc_btn_type;
53 : #define ACPIBTN_UNKNOWN 0
54 : #define ACPIBTN_LID 1
55 : #define ACPIBTN_POWER 2
56 : #define ACPIBTN_SLEEP 3
57 : };
58 :
59 : int acpibtn_setpsw(struct acpibtn_softc *, int);
60 :
61 : struct acpi_lid {
62 : struct acpibtn_softc *abl_softc;
63 : SLIST_ENTRY(acpi_lid) abl_link;
64 : };
65 : SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids =
66 : SLIST_HEAD_INITIALIZER(acpibtn_lids);
67 :
68 : struct cfattach acpibtn_ca = {
69 : sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach, NULL,
70 : acpibtn_activate
71 : };
72 :
73 : struct cfdriver acpibtn_cd = {
74 : NULL, "acpibtn", DV_DULL
75 : };
76 :
77 : const char *acpibtn_hids[] = {
78 : ACPI_DEV_LD,
79 : ACPI_DEV_PBD,
80 : ACPI_DEV_SBD,
81 : NULL
82 : };
83 :
84 : /*
85 : * acpibtn_numopenlids
86 : *
87 : * Return the number of _LID devices that are in the "open" state.
88 : * Used to determine if we should go back to sleep/hibernate if we
89 : * woke up with the all the lids still closed for some reason. If
90 : * the machine has no lids, returns -1.
91 : */
92 : int
93 0 : acpibtn_numopenlids(void)
94 : {
95 : struct acpi_lid *lid;
96 0 : int64_t val;
97 : int ct = 0;
98 :
99 : /* If we have no lids ... */
100 0 : if (SLIST_EMPTY(&acpibtn_lids))
101 0 : return (-1);
102 :
103 : /*
104 : * Determine how many lids are open. Assumes _LID evals to
105 : * non-0 or 0, for on / off (which is what the spec says).
106 : */
107 0 : SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
108 0 : if (!aml_evalinteger(lid->abl_softc->sc_acpi,
109 0 : lid->abl_softc->sc_devnode, "_LID", 0, NULL, &val) &&
110 0 : val != 0)
111 0 : ct++;
112 0 : return (ct);
113 0 : }
114 :
115 : int
116 0 : acpibtn_setpsw(struct acpibtn_softc *sc, int psw)
117 : {
118 0 : struct aml_value val;
119 :
120 0 : bzero(&val, sizeof val);
121 0 : val.type = AML_OBJTYPE_INTEGER;
122 0 : val.v_integer = psw;
123 0 : val.length = 1;
124 :
125 0 : return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val,
126 : NULL));
127 0 : }
128 :
129 : void
130 0 : acpibtn_disable_psw(void)
131 : {
132 : struct acpi_lid *lid;
133 :
134 : /* disable _LID for wakeup */
135 0 : SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
136 0 : acpibtn_setpsw(lid->abl_softc, 0);
137 0 : }
138 :
139 : void
140 0 : acpibtn_enable_psw(void)
141 : {
142 : struct acpi_lid *lid;
143 :
144 : /* enable _LID for wakeup */
145 0 : SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
146 0 : acpibtn_setpsw(lid->abl_softc, 1);
147 0 : }
148 :
149 : int
150 0 : acpibtn_match(struct device *parent, void *match, void *aux)
151 : {
152 0 : struct acpi_attach_args *aa = aux;
153 0 : struct cfdata *cf = match;
154 :
155 : /* sanity */
156 0 : return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name));
157 : }
158 :
159 : void
160 0 : acpibtn_attach(struct device *parent, struct device *self, void *aux)
161 : {
162 0 : struct acpibtn_softc *sc = (struct acpibtn_softc *)self;
163 0 : struct acpi_attach_args *aa = aux;
164 : struct acpi_lid *lid;
165 0 : int64_t lid_open = 1;
166 0 : int64_t st;
167 :
168 0 : sc->sc_acpi = (struct acpi_softc *)parent;
169 0 : sc->sc_devnode = aa->aaa_node;
170 :
171 0 : printf(": %s\n", sc->sc_devnode->name);
172 :
173 0 : if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
174 0 : st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
175 0 : if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
176 : (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
177 0 : return;
178 :
179 0 : if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) {
180 0 : sc->sc_btn_type = ACPIBTN_LID;
181 :
182 : /* Set PSW (if present) to disable wake on this LID */
183 0 : (void)acpibtn_setpsw(sc, 0);
184 0 : lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO);
185 0 : lid->abl_softc = sc;
186 0 : SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link);
187 0 : } else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD))
188 0 : sc->sc_btn_type = ACPIBTN_POWER;
189 0 : else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD))
190 0 : sc->sc_btn_type = ACPIBTN_SLEEP;
191 :
192 0 : if (sc->sc_btn_type == ACPIBTN_LID) {
193 0 : strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
194 : sizeof(sc->sc_sensdev.xname));
195 0 : strlcpy(sc->sc_sens.desc, "lid open",
196 : sizeof(sc->sc_sens.desc));
197 0 : sc->sc_sens.type = SENSOR_INDICATOR;
198 0 : sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
199 0 : sensordev_install(&sc->sc_sensdev);
200 :
201 0 : aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
202 : "_LID", 0, NULL, &lid_open);
203 0 : sc->sc_sens.value = lid_open;
204 0 : }
205 :
206 0 : aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify,
207 0 : sc, ACPIDEV_NOPOLL);
208 0 : }
209 :
210 : int
211 0 : acpibtn_notify(struct aml_node *node, int notify_type, void *arg)
212 : {
213 0 : struct acpibtn_softc *sc = arg;
214 : #ifndef SMALL_KERNEL
215 : extern int lid_action;
216 0 : int64_t lid;
217 : #endif
218 :
219 : dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type,
220 : sc->sc_devnode->name);
221 :
222 0 : switch (sc->sc_btn_type) {
223 : case ACPIBTN_LID:
224 : #ifndef SMALL_KERNEL
225 : /*
226 : * Notification of 0x80 for lid opens or closes. We
227 : * need to check the current status by calling the
228 : * _LID method. 0 means the lid is closed and we
229 : * should go to sleep.
230 : */
231 0 : if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
232 : "_LID", 0, NULL, &lid))
233 0 : return (0);
234 0 : sc->sc_sens.value = lid;
235 :
236 0 : if (lid != 0)
237 : break;
238 :
239 0 : switch (lid_action) {
240 : case 1:
241 : goto sleep;
242 : #ifdef HIBERNATE
243 : case 2:
244 : /* Request to go to sleep */
245 0 : if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
246 0 : acpi_addtask(sc->sc_acpi, acpi_sleep_task,
247 0 : sc->sc_acpi, ACPI_SLEEP_HIBERNATE);
248 : break;
249 : #endif
250 : default:
251 : break;
252 : }
253 : #endif /* SMALL_KERNEL */
254 : break;
255 : case ACPIBTN_SLEEP:
256 : #ifndef SMALL_KERNEL
257 0 : switch (notify_type) {
258 : case 0x02:
259 : /* "something" has been taken care of by the system */
260 : break;
261 : case 0x80:
262 : sleep:
263 : /* Request to go to sleep */
264 0 : if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
265 0 : acpi_addtask(sc->sc_acpi, acpi_sleep_task,
266 0 : sc->sc_acpi, ACPI_SLEEP_SUSPEND);
267 : break;
268 : }
269 : #endif /* SMALL_KERNEL */
270 : break;
271 : case ACPIBTN_POWER:
272 0 : if (notify_type == 0x80)
273 0 : acpi_addtask(sc->sc_acpi, acpi_powerdown_task,
274 0 : sc->sc_acpi, 0);
275 : break;
276 : default:
277 0 : printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc),
278 : sc->sc_btn_type);
279 0 : break;
280 : }
281 :
282 0 : return (0);
283 0 : }
284 :
285 : int
286 0 : acpibtn_activate(struct device *self, int act)
287 : {
288 0 : struct acpibtn_softc *sc = (struct acpibtn_softc *)self;
289 0 : int64_t lid_open = 1;
290 :
291 0 : switch (act) {
292 : case DVACT_WAKEUP:
293 0 : switch (sc->sc_btn_type) {
294 : case ACPIBTN_LID:
295 0 : aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
296 : "_LID", 0, NULL, &lid_open);
297 0 : sc->sc_sens.value = lid_open;
298 0 : break;
299 : }
300 : break;
301 : }
302 0 : return (0);
303 0 : }
|