1/* $NetBSD: dalb_acpi.c,v 1.18 2015/04/23 23:23:00 pgoyette Exp $ */
2
3/*-
4 * Copyright (c) 2008 Christoph Egger <cegger@netbsd.org>
5 * Copyright (c) 2008 Jared D. McNeill <jmcneill@netbsd.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
16 *
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.
28 */
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: dalb_acpi.c,v 1.18 2015/04/23 23:23:00 pgoyette Exp $");
31
32/*
33 * Direct Application Launch Button:
34 * http://www.microsoft.com/whdc/system/vista/DirAppLaunch.mspx
35 */
36
37#include <sys/param.h>
38#include <sys/device.h>
39#include <sys/module.h>
40#include <sys/systm.h>
41
42#include <dev/acpi/acpireg.h>
43#include <dev/acpi/acpivar.h>
44
45#define _COMPONENT ACPI_RESOURCE_COMPONENT
46ACPI_MODULE_NAME ("dalb_acpi")
47
48#define DALB_ID_INVALID -1
49
50struct acpi_dalb_softc {
51 device_t sc_dev;
52 struct acpi_devnode *sc_node;
53
54 ACPI_INTEGER sc_usageid;
55
56 /* There's one PNP0C32 ACPI device for each button.
57 * Therefore, one instance is enough. */
58 struct sysmon_pswitch sc_smpsw;
59 bool sc_smpsw_valid;
60};
61
62static int acpi_dalb_match(device_t, cfdata_t, void *);
63static void acpi_dalb_attach(device_t, device_t, void *);
64static int acpi_dalb_detach(device_t, int);
65static void acpi_dalb_notify_handler(ACPI_HANDLE, uint32_t, void *);
66static bool acpi_dalb_resume(device_t, const pmf_qual_t *);
67
68static void acpi_dalb_get_wakeup_hotkeys(void *opaque);
69static void acpi_dalb_get_runtime_hotkeys(void *opaque);
70
71CFATTACH_DECL_NEW(acpidalb, sizeof(struct acpi_dalb_softc),
72 acpi_dalb_match, acpi_dalb_attach, acpi_dalb_detach, NULL);
73
74static const char * const acpi_dalb_ids[] = {
75 "PNP0C32", /* Direct Application Launch Button */
76 NULL
77};
78
79#define DALB_SYSTEM_WAKEUP 0x02
80#define DALB_SYSTEM_RUNTIME 0x80
81
82static int
83acpi_dalb_match(device_t parent, cfdata_t match, void *aux)
84{
85 struct acpi_attach_args *aa = aux;
86
87 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
88 return 0;
89
90 return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_dalb_ids);
91}
92
93static void
94acpi_dalb_sysmon_init(struct acpi_dalb_softc *sc)
95{
96 sc->sc_smpsw_valid = true;
97 sc->sc_smpsw.smpsw_name = device_xname(sc->sc_dev);
98 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_HOTKEY;
99 if (sysmon_pswitch_register(&sc->sc_smpsw)) {
100 aprint_error_dev(sc->sc_dev,
101 "couldn't register sleep with sysmon\n");
102 sc->sc_smpsw_valid = false;
103 }
104}
105
106
107static void
108acpi_dalb_init(device_t dev)
109{
110 struct acpi_dalb_softc *sc = device_private(dev);
111 ACPI_OBJECT *obj;
112 ACPI_STATUS rv;
113 ACPI_BUFFER ret;
114
115 rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret);
116
117 if (ACPI_FAILURE(rv) || ret.Pointer == NULL) {
118 aprint_error_dev(dev,
119 "couldn't enable notify handler: (%s)\n",
120 AcpiFormatException(rv));
121 return;
122 }
123
124 obj = ret.Pointer;
125
126 if (obj->Type != ACPI_TYPE_BUFFER) {
127 sc->sc_usageid = DALB_ID_INVALID;
128 aprint_debug_dev(dev, "invalid ACPI type: %u\n", obj->Type);
129 goto out;
130 }
131
132 switch (obj->Buffer.Length) {
133 case 1:
134 sc->sc_usageid = *(uint8_t *)obj->Buffer.Pointer;
135 break;
136 case 2:
137 sc->sc_usageid = le16toh(*(uint16_t *)obj->Buffer.Pointer);
138 break;
139 case 4:
140 sc->sc_usageid = le32toh(*(uint32_t *)obj->Buffer.Pointer);
141 break;
142 default:
143 aprint_debug_dev(dev, "unhandled ret.Length: 0x%lx\n",
144 (unsigned long)obj->Buffer.Length);
145 sc->sc_usageid = DALB_ID_INVALID;
146 break;
147 }
148
149out:
150 ACPI_FREE(ret.Pointer);
151}
152
153static void
154acpi_dalb_attach(device_t parent, device_t self, void *aux)
155{
156 struct acpi_dalb_softc *sc = device_private(self);
157 struct acpi_attach_args *aa = aux;
158
159 aprint_naive("\n");
160 aprint_normal(": Direct Application Launch Button\n");
161
162 sc->sc_dev = self;
163 sc->sc_node = aa->aa_node;
164
165 config_interrupts(self, acpi_dalb_init);
166
167 (void)pmf_device_register(self, NULL, acpi_dalb_resume);
168 (void)acpi_register_notify(sc->sc_node, acpi_dalb_notify_handler);
169
170 sc->sc_smpsw_valid = false;
171 acpi_dalb_sysmon_init(sc);
172}
173
174static int
175acpi_dalb_detach(device_t self, int flags)
176{
177 struct acpi_dalb_softc *sc = device_private(self);
178
179 pmf_device_deregister(self);
180 acpi_deregister_notify(sc->sc_node);
181 sysmon_pswitch_unregister(&sc->sc_smpsw);
182
183 return 0;
184}
185
186static void
187acpi_dalb_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
188{
189 device_t dev = opaque;
190 struct acpi_dalb_softc *sc = device_private(dev);
191 ACPI_STATUS rv;
192
193 switch (notify) {
194 case DALB_SYSTEM_WAKEUP:
195 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER,
196 acpi_dalb_get_wakeup_hotkeys, dev);
197 break;
198 case DALB_SYSTEM_RUNTIME:
199 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER,
200 acpi_dalb_get_runtime_hotkeys, dev);
201 break;
202
203 default:
204 aprint_error_dev(dev,
205 "unknown notification event 0x%x from button 0x%x\n",
206 notify, (uint32_t)sc->sc_usageid);
207 return;
208 }
209
210 if (ACPI_FAILURE(rv))
211 aprint_error_dev(dev, "couldn't queue hotkey handler: %s\n",
212 AcpiFormatException(rv));
213}
214
215static void
216acpi_dalb_get_wakeup_hotkeys(void *opaque)
217{
218 device_t dev = opaque;
219 struct acpi_dalb_softc *sc = device_private(dev);
220
221 if (!sc->sc_smpsw_valid)
222 return;
223
224 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
225 "invoking %s (wakeup)\n", sc->sc_smpsw.smpsw_name));
226
227 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED);
228}
229
230static void
231acpi_dalb_get_runtime_hotkeys(void *opaque)
232{
233 device_t dev = opaque;
234 struct acpi_dalb_softc *sc = device_private(dev);
235
236 if (!sc->sc_smpsw_valid)
237 return;
238
239 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
240 "invoking %s (runtime)\n", sc->sc_smpsw.smpsw_name));
241
242 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED);
243}
244
245static bool
246acpi_dalb_resume(device_t dev, const pmf_qual_t *qual)
247{
248 struct acpi_dalb_softc *sc = device_private(dev);
249 ACPI_STATUS rv;
250 ACPI_BUFFER ret;
251
252 rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret);
253 if (ACPI_FAILURE(rv)) {
254 aprint_error_dev(dev, "couldn't evaluate GHID: %s\n",
255 AcpiFormatException(rv));
256 return false;
257 }
258 if (ret.Pointer)
259 ACPI_FREE(ret.Pointer);
260
261 return true;
262}
263
264MODULE(MODULE_CLASS_DRIVER, acpidalb, "sysmon_power");
265
266#ifdef _MODULE
267#include "ioconf.c"
268#endif
269
270static int
271acpidalb_modcmd(modcmd_t cmd, void *aux)
272{
273 int rv = 0;
274
275 switch (cmd) {
276
277 case MODULE_CMD_INIT:
278
279#ifdef _MODULE
280 rv = config_init_component(cfdriver_ioconf_acpidalb,
281 cfattach_ioconf_acpidalb, cfdata_ioconf_acpidalb);
282#endif
283 break;
284
285 case MODULE_CMD_FINI:
286
287#ifdef _MODULE
288 rv = config_fini_component(cfdriver_ioconf_acpidalb,
289 cfattach_ioconf_acpidalb, cfdata_ioconf_acpidalb);
290#endif
291 break;
292
293 default:
294 rv = ENOTTY;
295 }
296
297 return rv;
298}
299