xref: /freebsd/sys/dev/acpica/acpi_button.c (revision 685dc743)
115e32d5dSMike Smith /*-
215e32d5dSMike Smith  * Copyright (c) 2000 Mitsaru IWASAKI <iwasaki@jp.freebsd.org>
315e32d5dSMike Smith  * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
415e32d5dSMike Smith  * Copyright (c) 2000 BSDi
515e32d5dSMike Smith  * All rights reserved.
615e32d5dSMike Smith  *
715e32d5dSMike Smith  * Redistribution and use in source and binary forms, with or without
815e32d5dSMike Smith  * modification, are permitted provided that the following conditions
915e32d5dSMike Smith  * are met:
1015e32d5dSMike Smith  * 1. Redistributions of source code must retain the above copyright
1115e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer.
1215e32d5dSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1315e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer in the
1415e32d5dSMike Smith  *    documentation and/or other materials provided with the distribution.
1515e32d5dSMike Smith  *
1615e32d5dSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1715e32d5dSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1815e32d5dSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1915e32d5dSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2015e32d5dSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2115e32d5dSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2215e32d5dSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2315e32d5dSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2415e32d5dSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2515e32d5dSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2615e32d5dSMike Smith  * SUCH DAMAGE.
2715e32d5dSMike Smith  */
2815e32d5dSMike Smith 
29dad97feeSDavid E. O'Brien #include <sys/cdefs.h>
3015e32d5dSMike Smith #include "opt_acpi.h"
31543b9d59SHans Petter Selasky #include "opt_evdev.h"
3215e32d5dSMike Smith #include <sys/param.h>
3315e32d5dSMike Smith #include <sys/kernel.h>
34fe12f24bSPoul-Henning Kamp #include <sys/module.h>
3515e32d5dSMike Smith #include <sys/bus.h>
3615e32d5dSMike Smith 
37129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
38129d3046SJung-uk Kim #include <contrib/dev/acpica/include/accommon.h>
39129d3046SJung-uk Kim 
4015e32d5dSMike Smith #include <dev/acpica/acpivar.h>
4115e32d5dSMike Smith 
42543b9d59SHans Petter Selasky #ifdef EVDEV_SUPPORT
43543b9d59SHans Petter Selasky #include <dev/evdev/input.h>
44543b9d59SHans Petter Selasky #include <dev/evdev/evdev.h>
45543b9d59SHans Petter Selasky #endif
46543b9d59SHans Petter Selasky 
47798fb860SNate Lawson /* Hooks for the ACPI CA debugging infrastructure */
482a4ac806SMike Smith #define _COMPONENT	ACPI_BUTTON
499127281cSMike Smith ACPI_MODULE_NAME("BUTTON")
500ae55423SMike Smith 
5115e32d5dSMike Smith struct acpi_button_softc {
5215e32d5dSMike Smith     device_t	button_dev;
5315e32d5dSMike Smith     ACPI_HANDLE	button_handle;
5465c92e48SJohn Baldwin     enum { ACPI_POWER_BUTTON, ACPI_SLEEP_BUTTON } button_type;
5565c92e48SJohn Baldwin     bool	fixed;
56543b9d59SHans Petter Selasky #ifdef EVDEV_SUPPORT
57543b9d59SHans Petter Selasky     struct evdev_dev *button_evdev;
58543b9d59SHans Petter Selasky #endif
5915e32d5dSMike Smith };
6015e32d5dSMike Smith 
61798fb860SNate Lawson #define		ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP	0x80
62798fb860SNate Lawson #define		ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP	0x02
63798fb860SNate Lawson 
6415e32d5dSMike Smith static int	acpi_button_probe(device_t dev);
6515e32d5dSMike Smith static int	acpi_button_attach(device_t dev);
66a1fccb47SMitsuru IWASAKI static int	acpi_button_suspend(device_t dev);
67a1fccb47SMitsuru IWASAKI static int	acpi_button_resume(device_t dev);
68798fb860SNate Lawson static void 	acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify,
69798fb860SNate Lawson 					   void *context);
70be8dca59SNate Lawson static ACPI_STATUS
71be8dca59SNate Lawson 		acpi_button_fixed_handler(void *context);
7233febf93SNate Lawson static void	acpi_button_notify_sleep(void *arg);
7333febf93SNate Lawson static void	acpi_button_notify_wakeup(void *arg);
7415e32d5dSMike Smith 
755fcc8a58SNate Lawson static char *btn_ids[] = {
765fcc8a58SNate Lawson     "PNP0C0C", "ACPI_FPB", "PNP0C0E", "ACPI_FSB",
775fcc8a58SNate Lawson     NULL
785fcc8a58SNate Lawson };
795fcc8a58SNate Lawson 
8015e32d5dSMike Smith static device_method_t acpi_button_methods[] = {
8115e32d5dSMike Smith     /* Device interface */
8215e32d5dSMike Smith     DEVMETHOD(device_probe,	acpi_button_probe),
8315e32d5dSMike Smith     DEVMETHOD(device_attach,	acpi_button_attach),
84a1fccb47SMitsuru IWASAKI     DEVMETHOD(device_suspend,	acpi_button_suspend),
8562d4c42bSTakanori Watanabe     DEVMETHOD(device_shutdown,	acpi_button_suspend),
86a1fccb47SMitsuru IWASAKI     DEVMETHOD(device_resume,	acpi_button_resume),
8761bfd867SSofian Brabez     DEVMETHOD_END
8815e32d5dSMike Smith };
8915e32d5dSMike Smith 
9015e32d5dSMike Smith static driver_t acpi_button_driver = {
9115e32d5dSMike Smith     "acpi_button",
9215e32d5dSMike Smith     acpi_button_methods,
9315e32d5dSMike Smith     sizeof(struct acpi_button_softc),
9415e32d5dSMike Smith };
9515e32d5dSMike Smith 
96916a5d8aSJohn Baldwin DRIVER_MODULE(acpi_button, acpi, acpi_button_driver, 0, 0);
9764278df5SNate Lawson MODULE_DEPEND(acpi_button, acpi, 1, 1, 1);
9815e32d5dSMike Smith 
9915e32d5dSMike Smith static int
acpi_button_probe(device_t dev)10015e32d5dSMike Smith acpi_button_probe(device_t dev)
10115e32d5dSMike Smith {
10215e32d5dSMike Smith     struct acpi_button_softc *sc;
1035fcc8a58SNate Lawson     char *str;
1045efca36fSTakanori Watanabe     int rv;
10515e32d5dSMike Smith 
1065efca36fSTakanori Watanabe     if (acpi_disabled("button"))
1075efca36fSTakanori Watanabe 	return (ENXIO);
1085efca36fSTakanori Watanabe     rv = ACPI_ID_PROBE(device_get_parent(dev), dev, btn_ids, &str);
1095efca36fSTakanori Watanabe     if (rv > 0)
1105fcc8a58SNate Lawson 	return (ENXIO);
1115fcc8a58SNate Lawson 
11215e32d5dSMike Smith     sc = device_get_softc(dev);
1135fcc8a58SNate Lawson     if (strcmp(str, "PNP0C0C") == 0) {
11467ce1673SMike Smith 	device_set_desc(dev, "Power Button");
11515e32d5dSMike Smith 	sc->button_type = ACPI_POWER_BUTTON;
1165fcc8a58SNate Lawson     } else if (strcmp(str, "ACPI_FPB") == 0) {
117be8dca59SNate Lawson 	device_set_desc(dev, "Power Button (fixed)");
118be8dca59SNate Lawson 	sc->button_type = ACPI_POWER_BUTTON;
11965c92e48SJohn Baldwin 	sc->fixed = true;
1205fcc8a58SNate Lawson     } else if (strcmp(str, "PNP0C0E") == 0) {
12167ce1673SMike Smith 	device_set_desc(dev, "Sleep Button");
12215e32d5dSMike Smith 	sc->button_type = ACPI_SLEEP_BUTTON;
1235fcc8a58SNate Lawson     } else if (strcmp(str, "ACPI_FSB") == 0) {
124be8dca59SNate Lawson 	device_set_desc(dev, "Sleep Button (fixed)");
125be8dca59SNate Lawson 	sc->button_type = ACPI_SLEEP_BUTTON;
12665c92e48SJohn Baldwin 	sc->fixed = true;
12715e32d5dSMike Smith     }
1285fcc8a58SNate Lawson 
1295efca36fSTakanori Watanabe     return (rv);
13015e32d5dSMike Smith }
13115e32d5dSMike Smith 
13215e32d5dSMike Smith static int
acpi_button_attach(device_t dev)13315e32d5dSMike Smith acpi_button_attach(device_t dev)
13415e32d5dSMike Smith {
135a88e22b7SJung-uk Kim     struct acpi_prw_data	prw;
13615e32d5dSMike Smith     struct acpi_button_softc	*sc;
13715e32d5dSMike Smith     ACPI_STATUS			status;
138be8dca59SNate Lawson     int event;
13915e32d5dSMike Smith 
140b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1410ae55423SMike Smith 
14215e32d5dSMike Smith     sc = device_get_softc(dev);
14315e32d5dSMike Smith     sc->button_dev = dev;
14415e32d5dSMike Smith     sc->button_handle = acpi_get_handle(dev);
145be8dca59SNate Lawson     event = (sc->button_type == ACPI_SLEEP_BUTTON) ?
146be8dca59SNate Lawson 	    ACPI_EVENT_SLEEP_BUTTON : ACPI_EVENT_POWER_BUTTON;
14733febf93SNate Lawson 
148543b9d59SHans Petter Selasky #ifdef EVDEV_SUPPORT
149543b9d59SHans Petter Selasky     sc->button_evdev = evdev_alloc();
150543b9d59SHans Petter Selasky     evdev_set_name(sc->button_evdev, device_get_desc(dev));
151543b9d59SHans Petter Selasky     evdev_set_phys(sc->button_evdev, device_get_nameunit(dev));
152543b9d59SHans Petter Selasky     evdev_set_id(sc->button_evdev, BUS_HOST, 0, 0, 1);
153543b9d59SHans Petter Selasky     evdev_support_event(sc->button_evdev, EV_SYN);
154543b9d59SHans Petter Selasky     evdev_support_event(sc->button_evdev, EV_KEY);
155543b9d59SHans Petter Selasky     evdev_support_key(sc->button_evdev,
156543b9d59SHans Petter Selasky 	(sc->button_type == ACPI_SLEEP_BUTTON) ? KEY_SLEEP : KEY_POWER);
157543b9d59SHans Petter Selasky 
158543b9d59SHans Petter Selasky     if (evdev_register(sc->button_evdev))
159543b9d59SHans Petter Selasky         return (ENXIO);
160543b9d59SHans Petter Selasky #endif
161543b9d59SHans Petter Selasky 
162bb949efbSNate Lawson     /*
163bb949efbSNate Lawson      * Install the new handler.  We could remove any fixed handlers added
164bb949efbSNate Lawson      * from the FADT once we have a duplicate from the AML but some systems
165bb949efbSNate Lawson      * only return events on one or the other so we have to keep both.
166bb949efbSNate Lawson      */
16733febf93SNate Lawson     if (sc->fixed) {
16833febf93SNate Lawson 	AcpiClearEvent(event);
169be8dca59SNate Lawson 	status = AcpiInstallFixedEventHandler(event,
170be8dca59SNate Lawson 			acpi_button_fixed_handler, sc);
171be8dca59SNate Lawson     } else {
172f3fc4f8bSNate Lawson 	/*
173f3fc4f8bSNate Lawson 	 * If a system does not get lid events, it may make sense to change
174f3fc4f8bSNate Lawson 	 * the type to ACPI_ALL_NOTIFY.  Some systems generate both a wake
175f3fc4f8bSNate Lawson 	 * and runtime notify in that case though.
176f3fc4f8bSNate Lawson 	 */
177be8dca59SNate Lawson 	status = AcpiInstallNotifyHandler(sc->button_handle,
178be8dca59SNate Lawson 			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, sc);
179be8dca59SNate Lawson     }
180798fb860SNate Lawson     if (ACPI_FAILURE(status)) {
181be8dca59SNate Lawson 	device_printf(sc->button_dev, "couldn't install notify handler - %s\n",
182798fb860SNate Lawson 		      AcpiFormatException(status));
1830ae55423SMike Smith 	return_VALUE (ENXIO);
18415e32d5dSMike Smith     }
185e8b4d56eSNate Lawson 
186e8b4d56eSNate Lawson     /* Enable the GPE for wake/runtime. */
187e8b4d56eSNate Lawson     acpi_wake_set_enable(dev, 1);
188a88e22b7SJung-uk Kim     if (acpi_parse_prw(sc->button_handle, &prw) == 0)
189a88e22b7SJung-uk Kim 	AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit);
19033febf93SNate Lawson 
1910ae55423SMike Smith     return_VALUE (0);
19215e32d5dSMike Smith }
19315e32d5dSMike Smith 
194a1fccb47SMitsuru IWASAKI static int
acpi_button_suspend(device_t dev)195a1fccb47SMitsuru IWASAKI acpi_button_suspend(device_t dev)
196a1fccb47SMitsuru IWASAKI {
197a1fccb47SMitsuru IWASAKI     return (0);
198a1fccb47SMitsuru IWASAKI }
199a1fccb47SMitsuru IWASAKI 
200a1fccb47SMitsuru IWASAKI static int
acpi_button_resume(device_t dev)201a1fccb47SMitsuru IWASAKI acpi_button_resume(device_t dev)
202a1fccb47SMitsuru IWASAKI {
203a1fccb47SMitsuru IWASAKI     return (0);
204a1fccb47SMitsuru IWASAKI }
205a1fccb47SMitsuru IWASAKI 
20615e32d5dSMike Smith static void
acpi_button_notify_sleep(void * arg)20733febf93SNate Lawson acpi_button_notify_sleep(void *arg)
20815e32d5dSMike Smith {
20915e32d5dSMike Smith     struct acpi_button_softc	*sc;
21015e32d5dSMike Smith     struct acpi_softc		*acpi_sc;
21115e32d5dSMike Smith 
212b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
2130ae55423SMike Smith 
21415e32d5dSMike Smith     sc = (struct acpi_button_softc *)arg;
21515e32d5dSMike Smith     acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
216798fb860SNate Lawson     if (acpi_sc == NULL)
2170ae55423SMike Smith 	return_VOID;
21815e32d5dSMike Smith 
2199b937d48SNate Lawson     acpi_UserNotify("Button", sc->button_handle, sc->button_type);
2209b937d48SNate Lawson 
22115e32d5dSMike Smith     switch (sc->button_type) {
22215e32d5dSMike Smith     case ACPI_POWER_BUTTON:
223e788f796SBruce Evans 	ACPI_VPRINT(sc->button_dev, acpi_sc, "power button pressed\n");
22433febf93SNate Lawson 	acpi_event_power_button_sleep(acpi_sc);
22515e32d5dSMike Smith 	break;
22615e32d5dSMike Smith     case ACPI_SLEEP_BUTTON:
227e788f796SBruce Evans 	ACPI_VPRINT(sc->button_dev, acpi_sc, "sleep button pressed\n");
22833febf93SNate Lawson 	acpi_event_sleep_button_sleep(acpi_sc);
22915e32d5dSMike Smith 	break;
23015e32d5dSMike Smith     default:
2310ae55423SMike Smith 	break;		/* unknown button type */
23215e32d5dSMike Smith     }
23315e32d5dSMike Smith }
23415e32d5dSMike Smith 
23515e32d5dSMike Smith static void
acpi_button_notify_wakeup(void * arg)23633febf93SNate Lawson acpi_button_notify_wakeup(void *arg)
23715e32d5dSMike Smith {
23815e32d5dSMike Smith     struct acpi_button_softc	*sc;
23915e32d5dSMike Smith     struct acpi_softc		*acpi_sc;
24015e32d5dSMike Smith 
241b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
2420ae55423SMike Smith 
24315e32d5dSMike Smith     sc = (struct acpi_button_softc *)arg;
24415e32d5dSMike Smith     acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
245798fb860SNate Lawson     if (acpi_sc == NULL)
2460ae55423SMike Smith 	return_VOID;
24715e32d5dSMike Smith 
2489b937d48SNate Lawson     acpi_UserNotify("Button", sc->button_handle, sc->button_type);
2499b937d48SNate Lawson 
25015e32d5dSMike Smith     switch (sc->button_type) {
25115e32d5dSMike Smith     case ACPI_POWER_BUTTON:
252e788f796SBruce Evans 	ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by power button\n");
25333febf93SNate Lawson 	acpi_event_power_button_wake(acpi_sc);
25415e32d5dSMike Smith 	break;
25515e32d5dSMike Smith     case ACPI_SLEEP_BUTTON:
256e788f796SBruce Evans 	ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by sleep button\n");
25733febf93SNate Lawson 	acpi_event_sleep_button_wake(acpi_sc);
25815e32d5dSMike Smith 	break;
25915e32d5dSMike Smith     default:
2600ae55423SMike Smith 	break;		/* unknown button type */
26115e32d5dSMike Smith     }
26215e32d5dSMike Smith }
26315e32d5dSMike Smith 
26415e32d5dSMike Smith static void
acpi_button_notify_handler(ACPI_HANDLE h,UINT32 notify,void * context)26515e32d5dSMike Smith acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
26615e32d5dSMike Smith {
267f3fc4f8bSNate Lawson     struct acpi_button_softc	*sc;
268543b9d59SHans Petter Selasky #ifdef EVDEV_SUPPORT
269543b9d59SHans Petter Selasky     uint16_t key;
270543b9d59SHans Petter Selasky #endif
27115e32d5dSMike Smith 
272b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
2730ae55423SMike Smith 
274f3fc4f8bSNate Lawson     sc = (struct acpi_button_softc *)context;
27515e32d5dSMike Smith     switch (notify) {
27615e32d5dSMike Smith     case ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP:
2772be4e471SJung-uk Kim 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_button_notify_sleep, sc);
27815e32d5dSMike Smith 	break;
27915e32d5dSMike Smith     case ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP:
2802be4e471SJung-uk Kim 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_button_notify_wakeup, sc);
28115e32d5dSMike Smith 	break;
28215e32d5dSMike Smith     default:
283f3fc4f8bSNate Lawson 	device_printf(sc->button_dev, "unknown notify %#x\n", notify);
284f3fc4f8bSNate Lawson 	break;
28515e32d5dSMike Smith     }
286543b9d59SHans Petter Selasky 
287543b9d59SHans Petter Selasky #ifdef EVDEV_SUPPORT
288543b9d59SHans Petter Selasky     key = (sc->button_type == ACPI_SLEEP_BUTTON) ? KEY_SLEEP : KEY_POWER;
289543b9d59SHans Petter Selasky     evdev_push_key(sc->button_evdev, key, 1);
290543b9d59SHans Petter Selasky     evdev_sync(sc->button_evdev);
291543b9d59SHans Petter Selasky     evdev_push_key(sc->button_evdev, key, 0);
292543b9d59SHans Petter Selasky     evdev_sync(sc->button_evdev);
293543b9d59SHans Petter Selasky #endif
29415e32d5dSMike Smith }
295be8dca59SNate Lawson 
296be8dca59SNate Lawson static ACPI_STATUS
acpi_button_fixed_handler(void * context)297be8dca59SNate Lawson acpi_button_fixed_handler(void *context)
298be8dca59SNate Lawson {
299be8dca59SNate Lawson     struct acpi_button_softc	*sc = (struct acpi_button_softc *)context;
300be8dca59SNate Lawson 
301a6a1d015SNate Lawson     ACPI_FUNCTION_TRACE_PTR((char *)(uintptr_t)__func__, context);
302d2f243d0SPoul-Henning Kamp 
303be8dca59SNate Lawson     if (context == NULL)
304be8dca59SNate Lawson 	return_ACPI_STATUS (AE_BAD_PARAMETER);
305be8dca59SNate Lawson 
306be8dca59SNate Lawson     acpi_button_notify_handler(sc->button_handle,
307be8dca59SNate Lawson 			       ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP, sc);
308a6a1d015SNate Lawson     return_ACPI_STATUS (AE_OK);
309be8dca59SNate Lawson }
310