dalb_acpi.c revision 1.10.2.2 1 /* $NetBSD: dalb_acpi.c,v 1.10.2.2 2011/03/05 20:53:03 rmind Exp $ */
2
3 /*-
4 * Copyright (c) 2008 Christoph Egger <cegger (at) netbsd.org>
5 * Copyright (c) 2008 Jared D. McNeill <jmcneill (at) 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.10.2.2 2011/03/05 20:53:03 rmind 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
46 ACPI_MODULE_NAME ("dalb_acpi")
47
48 #define DALB_ID_INVALID -1
49
50 struct 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
62 static int acpi_dalb_match(device_t, cfdata_t, void *);
63 static void acpi_dalb_attach(device_t, device_t, void *);
64 static int acpi_dalb_detach(device_t, int);
65 static void acpi_dalb_notify_handler(ACPI_HANDLE, uint32_t, void *);
66 static bool acpi_dalb_resume(device_t, const pmf_qual_t *);
67
68 static void acpi_dalb_get_wakeup_hotkeys(void *opaque);
69 static void acpi_dalb_get_runtime_hotkeys(void *opaque);
70
71 CFATTACH_DECL_NEW(acpidalb, sizeof(struct acpi_dalb_softc),
72 acpi_dalb_match, acpi_dalb_attach, acpi_dalb_detach, NULL);
73
74 static 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
82 static int
83 acpi_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
93 static void
94 acpi_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
107 static void
108 acpi_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
149 out:
150 ACPI_FREE(ret.Pointer);
151 }
152
153 static void
154 acpi_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
174 static int
175 acpi_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
186 static void
187 acpi_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
215 static void
216 acpi_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
230 static void
231 acpi_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
245 static bool
246 acpi_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
264 MODULE(MODULE_CLASS_DRIVER, acpidalb, NULL);
265
266 #ifdef _MODULE
267 #include "ioconf.c"
268 #endif
269
270 static int
271 acpidalb_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