xref: /freebsd/sys/dev/acpica/acpi_button.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 /*-
2  * Copyright (c) 2000 Mitsaru IWASAKI <iwasaki@jp.freebsd.org>
3  * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
4  * Copyright (c) 2000 BSDi
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include "opt_acpi.h"
31 #include "opt_evdev.h"
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 
37 #include <contrib/dev/acpica/include/acpi.h>
38 #include <contrib/dev/acpica/include/accommon.h>
39 
40 #include <dev/acpica/acpivar.h>
41 
42 #ifdef EVDEV_SUPPORT
43 #include <dev/evdev/input.h>
44 #include <dev/evdev/evdev.h>
45 #endif
46 
47 /* Hooks for the ACPI CA debugging infrastructure */
48 #define _COMPONENT	ACPI_BUTTON
49 ACPI_MODULE_NAME("BUTTON")
50 
51 struct acpi_button_softc {
52     device_t	button_dev;
53     ACPI_HANDLE	button_handle;
54     enum { ACPI_POWER_BUTTON, ACPI_SLEEP_BUTTON } button_type;
55     bool	fixed;
56 #ifdef EVDEV_SUPPORT
57     struct evdev_dev *button_evdev;
58 #endif
59 };
60 
61 #define		ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP	0x80
62 #define		ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP	0x02
63 
64 static int	acpi_button_probe(device_t dev);
65 static int	acpi_button_attach(device_t dev);
66 static int	acpi_button_suspend(device_t dev);
67 static int	acpi_button_resume(device_t dev);
68 static void 	acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify,
69 					   void *context);
70 static ACPI_STATUS
71 		acpi_button_fixed_handler(void *context);
72 static void	acpi_button_notify_sleep(void *arg);
73 static void	acpi_button_notify_wakeup(void *arg);
74 
75 static char *btn_ids[] = {
76     "PNP0C0C", "ACPI_FPB", "PNP0C0E", "ACPI_FSB",
77     NULL
78 };
79 
80 static device_method_t acpi_button_methods[] = {
81     /* Device interface */
82     DEVMETHOD(device_probe,	acpi_button_probe),
83     DEVMETHOD(device_attach,	acpi_button_attach),
84     DEVMETHOD(device_suspend,	acpi_button_suspend),
85     DEVMETHOD(device_shutdown,	acpi_button_suspend),
86     DEVMETHOD(device_resume,	acpi_button_resume),
87     DEVMETHOD_END
88 };
89 
90 static driver_t acpi_button_driver = {
91     "acpi_button",
92     acpi_button_methods,
93     sizeof(struct acpi_button_softc),
94 };
95 
96 DRIVER_MODULE(acpi_button, acpi, acpi_button_driver, 0, 0);
97 MODULE_DEPEND(acpi_button, acpi, 1, 1, 1);
98 
99 static int
100 acpi_button_probe(device_t dev)
101 {
102     struct acpi_button_softc *sc;
103     char *str;
104     int rv;
105 
106     if (acpi_disabled("button"))
107 	return (ENXIO);
108     rv = ACPI_ID_PROBE(device_get_parent(dev), dev, btn_ids, &str);
109     if (rv > 0)
110 	return (ENXIO);
111 
112     sc = device_get_softc(dev);
113     if (strcmp(str, "PNP0C0C") == 0) {
114 	device_set_desc(dev, "Power Button");
115 	sc->button_type = ACPI_POWER_BUTTON;
116     } else if (strcmp(str, "ACPI_FPB") == 0) {
117 	device_set_desc(dev, "Power Button (fixed)");
118 	sc->button_type = ACPI_POWER_BUTTON;
119 	sc->fixed = true;
120     } else if (strcmp(str, "PNP0C0E") == 0) {
121 	device_set_desc(dev, "Sleep Button");
122 	sc->button_type = ACPI_SLEEP_BUTTON;
123     } else if (strcmp(str, "ACPI_FSB") == 0) {
124 	device_set_desc(dev, "Sleep Button (fixed)");
125 	sc->button_type = ACPI_SLEEP_BUTTON;
126 	sc->fixed = true;
127     }
128 
129     return (rv);
130 }
131 
132 static int
133 acpi_button_attach(device_t dev)
134 {
135     struct acpi_prw_data	prw;
136     struct acpi_button_softc	*sc;
137     ACPI_STATUS			status;
138     int event;
139 
140     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
141 
142     sc = device_get_softc(dev);
143     sc->button_dev = dev;
144     sc->button_handle = acpi_get_handle(dev);
145     event = (sc->button_type == ACPI_SLEEP_BUTTON) ?
146 	    ACPI_EVENT_SLEEP_BUTTON : ACPI_EVENT_POWER_BUTTON;
147 
148 #ifdef EVDEV_SUPPORT
149     sc->button_evdev = evdev_alloc();
150     evdev_set_name(sc->button_evdev, device_get_desc(dev));
151     evdev_set_phys(sc->button_evdev, device_get_nameunit(dev));
152     evdev_set_id(sc->button_evdev, BUS_HOST, 0, 0, 1);
153     evdev_support_event(sc->button_evdev, EV_SYN);
154     evdev_support_event(sc->button_evdev, EV_KEY);
155     evdev_support_key(sc->button_evdev,
156 	(sc->button_type == ACPI_SLEEP_BUTTON) ? KEY_SLEEP : KEY_POWER);
157 
158     if (evdev_register(sc->button_evdev))
159         return (ENXIO);
160 #endif
161 
162     /*
163      * Install the new handler.  We could remove any fixed handlers added
164      * from the FADT once we have a duplicate from the AML but some systems
165      * only return events on one or the other so we have to keep both.
166      */
167     if (sc->fixed) {
168 	AcpiClearEvent(event);
169 	status = AcpiInstallFixedEventHandler(event,
170 			acpi_button_fixed_handler, sc);
171     } else {
172 	/*
173 	 * If a system does not get lid events, it may make sense to change
174 	 * the type to ACPI_ALL_NOTIFY.  Some systems generate both a wake
175 	 * and runtime notify in that case though.
176 	 */
177 	status = AcpiInstallNotifyHandler(sc->button_handle,
178 			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, sc);
179     }
180     if (ACPI_FAILURE(status)) {
181 	device_printf(sc->button_dev, "couldn't install notify handler - %s\n",
182 		      AcpiFormatException(status));
183 	return_VALUE (ENXIO);
184     }
185 
186     /* Enable the GPE for wake/runtime. */
187     acpi_wake_set_enable(dev, 1);
188     if (acpi_parse_prw(sc->button_handle, &prw) == 0)
189 	AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit);
190 
191     return_VALUE (0);
192 }
193 
194 static int
195 acpi_button_suspend(device_t dev)
196 {
197     return (0);
198 }
199 
200 static int
201 acpi_button_resume(device_t dev)
202 {
203     return (0);
204 }
205 
206 static void
207 acpi_button_notify_sleep(void *arg)
208 {
209     struct acpi_button_softc	*sc;
210     struct acpi_softc		*acpi_sc;
211 
212     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
213 
214     sc = (struct acpi_button_softc *)arg;
215     acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
216     if (acpi_sc == NULL)
217 	return_VOID;
218 
219     acpi_UserNotify("Button", sc->button_handle, sc->button_type);
220 
221     switch (sc->button_type) {
222     case ACPI_POWER_BUTTON:
223 	ACPI_VPRINT(sc->button_dev, acpi_sc, "power button pressed\n");
224 	acpi_event_power_button_sleep(acpi_sc);
225 	break;
226     case ACPI_SLEEP_BUTTON:
227 	ACPI_VPRINT(sc->button_dev, acpi_sc, "sleep button pressed\n");
228 	acpi_event_sleep_button_sleep(acpi_sc);
229 	break;
230     default:
231 	break;		/* unknown button type */
232     }
233 }
234 
235 static void
236 acpi_button_notify_wakeup(void *arg)
237 {
238     struct acpi_button_softc	*sc;
239     struct acpi_softc		*acpi_sc;
240 
241     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
242 
243     sc = (struct acpi_button_softc *)arg;
244     acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
245     if (acpi_sc == NULL)
246 	return_VOID;
247 
248     acpi_UserNotify("Button", sc->button_handle, sc->button_type);
249 
250     switch (sc->button_type) {
251     case ACPI_POWER_BUTTON:
252 	ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by power button\n");
253 	acpi_event_power_button_wake(acpi_sc);
254 	break;
255     case ACPI_SLEEP_BUTTON:
256 	ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by sleep button\n");
257 	acpi_event_sleep_button_wake(acpi_sc);
258 	break;
259     default:
260 	break;		/* unknown button type */
261     }
262 }
263 
264 static void
265 acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
266 {
267     struct acpi_button_softc	*sc;
268 #ifdef EVDEV_SUPPORT
269     uint16_t key;
270 #endif
271 
272     ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
273 
274     sc = (struct acpi_button_softc *)context;
275     switch (notify) {
276     case ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP:
277 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_button_notify_sleep, sc);
278 	break;
279     case ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP:
280 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_button_notify_wakeup, sc);
281 	break;
282     default:
283 	device_printf(sc->button_dev, "unknown notify %#x\n", notify);
284 	break;
285     }
286 
287 #ifdef EVDEV_SUPPORT
288     key = (sc->button_type == ACPI_SLEEP_BUTTON) ? KEY_SLEEP : KEY_POWER;
289     evdev_push_key(sc->button_evdev, key, 1);
290     evdev_sync(sc->button_evdev);
291     evdev_push_key(sc->button_evdev, key, 0);
292     evdev_sync(sc->button_evdev);
293 #endif
294 }
295 
296 static ACPI_STATUS
297 acpi_button_fixed_handler(void *context)
298 {
299     struct acpi_button_softc	*sc = (struct acpi_button_softc *)context;
300 
301     ACPI_FUNCTION_TRACE_PTR((char *)(uintptr_t)__func__, context);
302 
303     if (context == NULL)
304 	return_ACPI_STATUS (AE_BAD_PARAMETER);
305 
306     acpi_button_notify_handler(sc->button_handle,
307 			       ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP, sc);
308     return_ACPI_STATUS (AE_OK);
309 }
310