acpi_button.c revision 131282
1295367Sdes/*-
257429Smarkm * Copyright (c) 2000 Mitsaru IWASAKI <iwasaki@jp.freebsd.org>
357429Smarkm * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
457429Smarkm * Copyright (c) 2000 BSDi
557429Smarkm * All rights reserved.
657429Smarkm *
757429Smarkm * Redistribution and use in source and binary forms, with or without
857429Smarkm * modification, are permitted provided that the following conditions
960576Skris * are met:
1065674Skris * 1. Redistributions of source code must retain the above copyright
1165674Skris *    notice, this list of conditions and the following disclaimer.
1265674Skris * 2. Redistributions in binary form must reproduce the above copyright
1365674Skris *    notice, this list of conditions and the following disclaimer in the
1465674Skris *    documentation and/or other materials provided with the distribution.
1557429Smarkm *
1657429Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1757429Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1857429Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19295367Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20295367Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21162856Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22162856Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23162856Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2476262Sgreen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2576262Sgreen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26162856Sdes * SUCH DAMAGE.
27162856Sdes *
28162856Sdes *	$FreeBSD: head/sys/dev/acpica/acpi_button.c 131282 2004-06-29 19:02:27Z njl $
29162856Sdes */
30162856Sdes
31162856Sdes#include "opt_acpi.h"
3257429Smarkm#include <sys/param.h>
3357429Smarkm#include <sys/kernel.h>
3476262Sgreen#include <sys/module.h>
3557429Smarkm#include <sys/bus.h>
3658585Skris
37162856Sdes#include "acpi.h"
3876262Sgreen#include <dev/acpica/acpivar.h>
3976262Sgreen
40295367Sdes/* Hooks for the ACPI CA debugging infrastructure */
4157429Smarkm#define _COMPONENT	ACPI_BUTTON
42162856SdesACPI_MODULE_NAME("BUTTON")
43215116Sdes
44162856Sdesstruct acpi_button_softc {
4576262Sgreen    device_t	button_dev;
46162856Sdes    ACPI_HANDLE	button_handle;
47162856Sdes    boolean_t	button_type;
48162856Sdes#define		ACPI_POWER_BUTTON	0
4998684Sdes#define		ACPI_SLEEP_BUTTON	1
5098684Sdes    boolean_t	fixed;
5157429Smarkm};
52264377Sdes
53264377Sdes#define		ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP	0x80
5469591Sgreen#define		ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP	0x02
5569591Sgreen
5669591Sgreenstatic int	acpi_button_probe(device_t dev);
5757429Smarkmstatic int	acpi_button_attach(device_t dev);
5857429Smarkmstatic int	acpi_button_suspend(device_t dev);
5957429Smarkmstatic int	acpi_button_resume(device_t dev);
6057429Smarkmstatic void 	acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify,
6176262Sgreen					   void *context);
6257429Smarkmstatic ACPI_STATUS
6357429Smarkm		acpi_button_fixed_handler(void *context);
6457429Smarkmstatic void	acpi_button_notify_sleep(void *arg);
6557429Smarkmstatic void	acpi_button_notify_wakeup(void *arg);
6657429Smarkm
6757429Smarkmstatic char *btn_ids[] = {
6857429Smarkm    "PNP0C0C", "ACPI_FPB", "PNP0C0E", "ACPI_FSB",
69147005Sdes    NULL
7057429Smarkm};
7157429Smarkm
7257429Smarkmstatic device_method_t acpi_button_methods[] = {
7398684Sdes    /* Device interface */
7498684Sdes    DEVMETHOD(device_probe,	acpi_button_probe),
7598684Sdes    DEVMETHOD(device_attach,	acpi_button_attach),
7698684Sdes    DEVMETHOD(device_suspend,	acpi_button_suspend),
7798684Sdes    DEVMETHOD(device_shutdown,	acpi_button_suspend),
7898684Sdes    DEVMETHOD(device_resume,	acpi_button_resume),
7998684Sdes
8098684Sdes    {0, 0}
8198684Sdes};
82164149Sdes
83164149Sdesstatic driver_t acpi_button_driver = {
8498684Sdes    "acpi_button",
85164149Sdes    acpi_button_methods,
86164149Sdes    sizeof(struct acpi_button_softc),
87164149Sdes};
8898684Sdes
8998684Sdesstatic devclass_t acpi_button_devclass;
9098684SdesDRIVER_MODULE(acpi_button, acpi, acpi_button_driver, acpi_button_devclass,
9198684Sdes	      0, 0);
9298684SdesMODULE_DEPEND(acpi_button, acpi, 1, 1, 1);
9398684Sdes
9498684Sdesstatic int
9598684Sdesacpi_button_probe(device_t dev)
9698684Sdes{
97264377Sdes    struct acpi_button_softc *sc;
9898684Sdes    char *str;
9998684Sdes
10098684Sdes    if (acpi_disabled("button") ||
10198684Sdes	(str = ACPI_ID_PROBE(device_get_parent(dev), dev, btn_ids)) == NULL)
102264377Sdes	return (ENXIO);
103264377Sdes
10498684Sdes    sc = device_get_softc(dev);
10598684Sdes    if (strcmp(str, "PNP0C0C") == 0) {
10698684Sdes	device_set_desc(dev, "Power Button");
10798684Sdes	sc->button_type = ACPI_POWER_BUTTON;
10898684Sdes    } else if (strcmp(str, "ACPI_FPB") == 0) {
10998684Sdes	device_set_desc(dev, "Power Button (fixed)");
11098684Sdes	sc->button_type = ACPI_POWER_BUTTON;
111264377Sdes	sc->fixed = 1;
11298684Sdes    } else if (strcmp(str, "PNP0C0E") == 0) {
11398684Sdes	device_set_desc(dev, "Sleep Button");
114264377Sdes	sc->button_type = ACPI_SLEEP_BUTTON;
115264377Sdes    } else if (strcmp(str, "ACPI_FSB") == 0) {
116264377Sdes	device_set_desc(dev, "Sleep Button (fixed)");
117264377Sdes	sc->button_type = ACPI_SLEEP_BUTTON;
118264377Sdes	sc->fixed = 1;
119264377Sdes    }
12098684Sdes
12198684Sdes    return (0);
122215116Sdes}
12398684Sdes
12498684Sdesstatic int
12598684Sdesacpi_button_attach(device_t dev)
12698684Sdes{
12798684Sdes    struct acpi_button_softc	*sc;
12898684Sdes    ACPI_STATUS			status;
12998684Sdes    int event;
13057429Smarkm
13157429Smarkm    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
13257429Smarkm
13357429Smarkm    sc = device_get_softc(dev);
13457429Smarkm    sc->button_dev = dev;
13557429Smarkm    sc->button_handle = acpi_get_handle(dev);
13657429Smarkm    event = (sc->button_type == ACPI_SLEEP_BUTTON) ?
13798684Sdes	    ACPI_EVENT_SLEEP_BUTTON : ACPI_EVENT_POWER_BUTTON;
13857429Smarkm
13957429Smarkm    /*
14098684Sdes     * Install the new handler.  We could remove any fixed handlers added
14198684Sdes     * from the FADT once we have a duplicate from the AML but some systems
14257429Smarkm     * only return events on one or the other so we have to keep both.
14392559Sdes     */
14492559Sdes    if (sc->fixed) {
14557429Smarkm	AcpiClearEvent(event);
14698684Sdes	status = AcpiInstallFixedEventHandler(event,
14757429Smarkm			acpi_button_fixed_handler, sc);
14857429Smarkm    } else {
149295367Sdes	/*
150295367Sdes	 * If a system does not get lid events, it may make sense to change
15157429Smarkm	 * the type to ACPI_ALL_NOTIFY.  Some systems generate both a wake
15257429Smarkm	 * and runtime notify in that case though.
15357429Smarkm	 */
15457429Smarkm	status = AcpiInstallNotifyHandler(sc->button_handle,
15557429Smarkm			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, sc);
15657429Smarkm    }
15757429Smarkm    if (ACPI_FAILURE(status)) {
15857429Smarkm	device_printf(sc->button_dev, "couldn't install notify handler - %s\n",
15957429Smarkm		      AcpiFormatException(status));
16092559Sdes	return_VALUE (ENXIO);
16157429Smarkm    }
162162856Sdes
16392559Sdes    /* Enable the GPE for wake/runtime. */
16457429Smarkm    acpi_wake_init(dev, ACPI_GPE_TYPE_WAKE_RUN);
16598684Sdes    acpi_wake_set_enable(dev, 1);
16657429Smarkm
16798684Sdes    return_VALUE (0);
16857429Smarkm}
16957429Smarkm
170226046Sdesstatic int
171226046Sdesacpi_button_suspend(device_t dev)
172226046Sdes{
17357429Smarkm    struct acpi_softc           *acpi_sc;
174255767Sdes
175255767Sdes    acpi_sc = acpi_device_get_parent_softc(dev);
17657429Smarkm    acpi_wake_sleep_prep(dev, acpi_sc->acpi_sstate);
17776262Sgreen    return (0);
17892559Sdes}
17957429Smarkm
18092559Sdesstatic int
181226046Sdesacpi_button_resume(device_t dev)
182226046Sdes{
18357429Smarkm    acpi_wake_run_prep(dev);
18457429Smarkm    return (0);
18557429Smarkm}
18657429Smarkm
18757429Smarkmstatic void
18857429Smarkmacpi_button_notify_sleep(void *arg)
189226046Sdes{
190147005Sdes    struct acpi_button_softc	*sc;
19157429Smarkm    struct acpi_softc		*acpi_sc;
192137019Sdes
193149753Sdes    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
19457429Smarkm
19557429Smarkm    sc = (struct acpi_button_softc *)arg;
19657429Smarkm    acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
19757429Smarkm    if (acpi_sc == NULL)
19857429Smarkm	return_VOID;
19957429Smarkm
20057429Smarkm    acpi_UserNotify("Button", sc->button_handle, sc->button_type);
20157429Smarkm
20257429Smarkm    switch (sc->button_type) {
20357429Smarkm    case ACPI_POWER_BUTTON:
20457429Smarkm	ACPI_VPRINT(sc->button_dev, acpi_sc, "power button pressed\n");
20557429Smarkm	acpi_event_power_button_sleep(acpi_sc);
20657429Smarkm	break;
20757429Smarkm    case ACPI_SLEEP_BUTTON:
20857429Smarkm	ACPI_VPRINT(sc->button_dev, acpi_sc, "sleep button pressed\n");
209137019Sdes	acpi_event_sleep_button_sleep(acpi_sc);
21057429Smarkm	break;
21157429Smarkm    default:
21257429Smarkm	break;		/* unknown button type */
21357429Smarkm    }
21457429Smarkm}
21557429Smarkm
21657429Smarkmstatic void
217137019Sdesacpi_button_notify_wakeup(void *arg)
21857429Smarkm{
21957429Smarkm    struct acpi_button_softc	*sc;
22092559Sdes    struct acpi_softc		*acpi_sc;
22192559Sdes
22276262Sgreen    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
22357429Smarkm
22457429Smarkm    sc = (struct acpi_button_softc *)arg;
22557429Smarkm    acpi_sc = acpi_device_get_parent_softc(sc->button_dev);
22657429Smarkm    if (acpi_sc == NULL)
227226046Sdes	return_VOID;
228226046Sdes
229226046Sdes    acpi_UserNotify("Button", sc->button_handle, sc->button_type);
230226046Sdes
23192559Sdes    switch (sc->button_type) {
23257429Smarkm    case ACPI_POWER_BUTTON:
23357429Smarkm	ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by power button\n");
23457429Smarkm	acpi_event_power_button_wake(acpi_sc);
235149753Sdes	break;
236255767Sdes    case ACPI_SLEEP_BUTTON:
237124211Sdes	ACPI_VPRINT(sc->button_dev, acpi_sc, "wakeup by sleep button\n");
23857429Smarkm	acpi_event_sleep_button_wake(acpi_sc);
23992559Sdes	break;
24057429Smarkm    default:
241295367Sdes	break;		/* unknown button type */
242295367Sdes    }
243295367Sdes}
244255767Sdes
245255767Sdesstatic void
246255767Sdesacpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
247255767Sdes{
248221420Sdes    struct acpi_button_softc	*sc;
249221420Sdes
250221420Sdes    ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
251221420Sdes
25257429Smarkm    sc = (struct acpi_button_softc *)context;
25376262Sgreen    switch (notify) {
25476262Sgreen    case ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP:
25576262Sgreen	AcpiOsQueueForExecution(OSD_PRIORITY_LO,
25676262Sgreen				acpi_button_notify_sleep, sc);
257137019Sdes	break;
25876262Sgreen    case ACPI_NOTIFY_BUTTON_PRESSED_FOR_WAKEUP:
259215116Sdes	AcpiOsQueueForExecution(OSD_PRIORITY_LO,
260215116Sdes				acpi_button_notify_wakeup, sc);
26198684Sdes	break;
26298684Sdes    default:
26369591Sgreen	device_printf(sc->button_dev, "unknown notify %#x\n", notify);
26457429Smarkm	break;
26557429Smarkm    }
26657429Smarkm}
26757429Smarkm
26857429Smarkmstatic ACPI_STATUS
26998684Sdesacpi_button_fixed_handler(void *context)
27098684Sdes{
27198684Sdes    struct acpi_button_softc	*sc = (struct acpi_button_softc *)context;
27298684Sdes
27398684Sdes    ACPI_FUNCTION_TRACE_PTR((char *)(uintptr_t)__func__, context);
274226046Sdes
275226046Sdes    if (context == NULL)
27698684Sdes	return_ACPI_STATUS (AE_BAD_PARAMETER);
27757429Smarkm
27898684Sdes    acpi_button_notify_handler(sc->button_handle,
279226046Sdes			       ACPI_NOTIFY_BUTTON_PRESSED_FOR_SLEEP, sc);
280226046Sdes    return_ACPI_STATUS (AE_OK);
281226046Sdes}
282226046Sdes