1 /* $NetBSD: dalb_acpi.c,v 1.4 2010/01/05 13:39:49 jruoho Exp $ */
4 * Copyright (c) 2008 Christoph Egger <cegger@netbsd.org>
5 * Copyright (c) 2008 Jared D. McNeill <jmcneill@netbsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: dalb_acpi.c,v 1.4 2010/01/05 13:39:49 jruoho Exp $");
33 * Direct Application Launch Button:
34 * http://www.microsoft.com/whdc/system/vista/DirAppLaunch.mspx
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
41 #include <sys/kernel.h>
42 #include <sys/callout.h>
43 #include <sys/sysctl.h>
45 #include <machine/bus.h>
47 #include <dev/acpi/acpica.h>
48 #include <dev/acpi/acpivar.h>
49 #include <dev/acpi/acpireg.h>
51 #define _COMPONENT ACPI_RESOURCE_COMPONENT
52 ACPI_MODULE_NAME ("dalb_acpi")
54 #define DALB_ID_INVALID -1
56 struct acpi_dalb_softc
{
58 struct acpi_devnode
*sc_node
;
59 struct sysctllog
*sc_log
;
61 ACPI_INTEGER sc_usageid
;
63 /* There's one PNP0C32 ACPI device for each button.
64 * Therefore, one instance is enough. */
65 struct sysmon_pswitch sc_smpsw
;
69 static int acpi_dalb_match(device_t
, cfdata_t
, void *);
70 static void acpi_dalb_attach(device_t
, device_t
, void *);
71 static void acpi_dalb_notify_handler(ACPI_HANDLE
, UINT32
, void *);
72 static bool acpi_dalb_resume(device_t
, pmf_qual_t
);
74 static void acpi_dalb_get_wakeup_hotkeys(void *opaque
);
75 static void acpi_dalb_get_runtime_hotkeys(void *opaque
);
77 CFATTACH_DECL_NEW(acpidalb
, sizeof(struct acpi_dalb_softc
),
78 acpi_dalb_match
, acpi_dalb_attach
, NULL
, NULL
);
80 static const char * const acpi_dalb_ids
[] = {
81 "PNP0C32", /* Direct Application Launch Button */
86 #define DPRINTF(x) printf x
91 #define DALB_SYSTEM_WAKEUP 0x02
92 #define DALB_SYSTEM_RUNTIME 0x80
95 acpi_dalb_match(device_t parent
, cfdata_t match
, void *aux
)
97 struct acpi_attach_args
*aa
= aux
;
99 if (aa
->aa_node
->ad_type
!= ACPI_TYPE_DEVICE
)
102 return acpi_match_hid(aa
->aa_node
->ad_devinfo
, acpi_dalb_ids
);
106 acpi_dalb_sysmon_init(struct acpi_dalb_softc
*sc
)
108 sc
->sc_smpsw_valid
= true;
109 sc
->sc_smpsw
.smpsw_name
= device_xname(sc
->sc_dev
);
110 sc
->sc_smpsw
.smpsw_type
= PSWITCH_TYPE_HOTKEY
;
111 if (sysmon_pswitch_register(&sc
->sc_smpsw
)) {
112 aprint_error_dev(sc
->sc_dev
,
113 "couldn't register sleep with sysmon\n");
114 sc
->sc_smpsw_valid
= false;
120 acpi_dalb_init(device_t dev
)
122 struct acpi_dalb_softc
*sc
= device_private(dev
);
127 rv
= acpi_eval_struct(sc
->sc_node
->ad_handle
, "GHID", &ret
);
128 if (ACPI_FAILURE(rv
) || ret
.Pointer
== NULL
) {
129 aprint_error_dev(dev
,
130 "couldn't enable notify handler: (%s)\n",
131 AcpiFormatException(rv
));
135 obj
= (ACPI_OBJECT
*)ret
.Pointer
;
136 if (obj
->Type
!= ACPI_TYPE_BUFFER
) {
137 sc
->sc_usageid
= DALB_ID_INVALID
;
138 aprint_debug_dev(dev
, "invalid ACPI type: %d\n", obj
->Type
);
142 switch (obj
->Buffer
.Length
) {
144 sc
->sc_usageid
= *(uint8_t *)obj
->Buffer
.Pointer
;
147 sc
->sc_usageid
= le16toh(*(uint16_t *)obj
->Buffer
.Pointer
);
150 sc
->sc_usageid
= le32toh(*(uint32_t *)obj
->Buffer
.Pointer
);
153 aprint_debug_dev(dev
, "unhandled ret.Length: 0x%lx\n",
154 (unsigned long)obj
->Buffer
.Length
);
155 sc
->sc_usageid
= DALB_ID_INVALID
;
160 ACPI_FREE(ret
.Pointer
);
164 acpi_dalb_attach(device_t parent
, device_t self
, void *aux
)
166 struct acpi_dalb_softc
*sc
= device_private(self
);
167 struct acpi_attach_args
*aa
= aux
;
171 aprint_normal(": Direct Application Launch Button\n");
173 sc
->sc_node
= aa
->aa_node
;
176 config_interrupts(self
, acpi_dalb_init
);
178 /* Install notify handler */
179 rv
= AcpiInstallNotifyHandler(sc
->sc_node
->ad_handle
,
180 ACPI_ALL_NOTIFY
, acpi_dalb_notify_handler
, self
);
181 if (ACPI_FAILURE(rv
))
182 aprint_error_dev(self
,
183 "couldn't install notify handler: (%s)\n",
184 AcpiFormatException(rv
));
186 sc
->sc_smpsw_valid
= false;
187 acpi_dalb_sysmon_init(sc
);
189 if (!pmf_device_register(self
, NULL
, acpi_dalb_resume
))
190 aprint_error_dev(self
, "couldn't establish power handler\n");
194 acpi_dalb_notify_handler(ACPI_HANDLE hdl
, UINT32 notify
, void *opaque
)
196 device_t dev
= opaque
;
197 struct acpi_dalb_softc
*sc
= device_private(dev
);
201 case DALB_SYSTEM_WAKEUP
:
202 rv
= AcpiOsExecute(OSL_NOTIFY_HANDLER
,
203 acpi_dalb_get_wakeup_hotkeys
, dev
);
205 case DALB_SYSTEM_RUNTIME
:
206 rv
= AcpiOsExecute(OSL_NOTIFY_HANDLER
,
207 acpi_dalb_get_runtime_hotkeys
, dev
);
211 aprint_error_dev(dev
,
212 "unknown notification event 0x%x from button 0x%x\n",
213 notify
, (uint32_t)sc
->sc_usageid
);
217 if (ACPI_FAILURE(rv
))
218 aprint_error_dev(dev
, "couldn't queue hotkey handler: %s\n",
219 AcpiFormatException(rv
));
223 acpi_dalb_get_wakeup_hotkeys(void *opaque
)
225 device_t dev
= opaque
;
226 struct acpi_dalb_softc
*sc
= device_private(dev
);
228 if (!sc
->sc_smpsw_valid
)
230 DPRINTF(("%s: %s: invoking sysmon_pswitch_event\n",
231 sc
->sc_smpsw
.smpsw_name
, __func__
));
232 sysmon_pswitch_event(&sc
->sc_smpsw
, PSWITCH_EVENT_PRESSED
);
236 acpi_dalb_get_runtime_hotkeys(void *opaque
)
238 device_t dev
= opaque
;
239 struct acpi_dalb_softc
*sc
= device_private(dev
);
241 if (!sc
->sc_smpsw_valid
)
243 DPRINTF(("%s: %s: invoking sysmon_pswitch_event\n",
244 sc
->sc_smpsw
.smpsw_name
, __func__
));
245 sysmon_pswitch_event(&sc
->sc_smpsw
, PSWITCH_EVENT_PRESSED
);
249 acpi_dalb_resume(device_t dev
, pmf_qual_t qual
)
251 struct acpi_dalb_softc
*sc
= device_private(dev
);
255 rv
= acpi_eval_struct(sc
->sc_node
->ad_handle
, "GHID", &ret
);
256 if (ACPI_FAILURE(rv
)) {
257 aprint_error_dev(dev
, "couldn't evaluate GHID: %s\n",
258 AcpiFormatException(rv
));
262 ACPI_FREE(ret
.Pointer
);