1 1.19 thorpej /* $NetBSD: dalb_acpi.c,v 1.19 2021/01/29 15:49:55 thorpej Exp $ */ 2 1.1 cegger 3 1.1 cegger /*- 4 1.1 cegger * Copyright (c) 2008 Christoph Egger <cegger (at) netbsd.org> 5 1.1 cegger * Copyright (c) 2008 Jared D. McNeill <jmcneill (at) netbsd.org> 6 1.1 cegger * All rights reserved. 7 1.1 cegger * 8 1.1 cegger * Redistribution and use in source and binary forms, with or without 9 1.1 cegger * modification, are permitted provided that the following conditions 10 1.1 cegger * are met: 11 1.1 cegger * 1. Redistributions of source code must retain the above copyright 12 1.1 cegger * notice, this list of conditions and the following disclaimer. 13 1.1 cegger * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 cegger * notice, this list of conditions and the following disclaimer in the 15 1.1 cegger * documentation and/or other materials provided with the distribution. 16 1.1 cegger * 17 1.1 cegger * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 1.1 cegger * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 1.1 cegger * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 cegger * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 1.1 cegger * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 1.1 cegger * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 1.1 cegger * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 cegger * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 1.1 cegger * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 1.1 cegger * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 1.1 cegger * POSSIBILITY OF SUCH DAMAGE. 28 1.1 cegger */ 29 1.1 cegger #include <sys/cdefs.h> 30 1.19 thorpej __KERNEL_RCSID(0, "$NetBSD: dalb_acpi.c,v 1.19 2021/01/29 15:49:55 thorpej Exp $"); 31 1.1 cegger 32 1.1 cegger /* 33 1.1 cegger * Direct Application Launch Button: 34 1.1 cegger * http://www.microsoft.com/whdc/system/vista/DirAppLaunch.mspx 35 1.1 cegger */ 36 1.1 cegger 37 1.1 cegger #include <sys/param.h> 38 1.1 cegger #include <sys/device.h> 39 1.16 jruoho #include <sys/module.h> 40 1.9 jruoho #include <sys/systm.h> 41 1.1 cegger 42 1.9 jruoho #include <dev/acpi/acpireg.h> 43 1.1 cegger #include <dev/acpi/acpivar.h> 44 1.3 mlelstv 45 1.3 mlelstv #define _COMPONENT ACPI_RESOURCE_COMPONENT 46 1.3 mlelstv ACPI_MODULE_NAME ("dalb_acpi") 47 1.1 cegger 48 1.1 cegger #define DALB_ID_INVALID -1 49 1.1 cegger 50 1.1 cegger struct acpi_dalb_softc { 51 1.1 cegger device_t sc_dev; 52 1.1 cegger struct acpi_devnode *sc_node; 53 1.1 cegger 54 1.1 cegger ACPI_INTEGER sc_usageid; 55 1.1 cegger 56 1.1 cegger /* There's one PNP0C32 ACPI device for each button. 57 1.1 cegger * Therefore, one instance is enough. */ 58 1.1 cegger struct sysmon_pswitch sc_smpsw; 59 1.1 cegger bool sc_smpsw_valid; 60 1.1 cegger }; 61 1.1 cegger 62 1.1 cegger static int acpi_dalb_match(device_t, cfdata_t, void *); 63 1.1 cegger static void acpi_dalb_attach(device_t, device_t, void *); 64 1.6 jruoho static int acpi_dalb_detach(device_t, int); 65 1.13 jruoho static void acpi_dalb_notify_handler(ACPI_HANDLE, uint32_t, void *); 66 1.7 dyoung static bool acpi_dalb_resume(device_t, const pmf_qual_t *); 67 1.1 cegger 68 1.1 cegger static void acpi_dalb_get_wakeup_hotkeys(void *opaque); 69 1.1 cegger static void acpi_dalb_get_runtime_hotkeys(void *opaque); 70 1.1 cegger 71 1.1 cegger CFATTACH_DECL_NEW(acpidalb, sizeof(struct acpi_dalb_softc), 72 1.6 jruoho acpi_dalb_match, acpi_dalb_attach, acpi_dalb_detach, NULL); 73 1.1 cegger 74 1.19 thorpej static const struct device_compatible_entry compat_data[] = { 75 1.19 thorpej { .compat = "PNP0C32" }, /* Direct Application Launch Button */ 76 1.19 thorpej DEVICE_COMPAT_EOL 77 1.1 cegger }; 78 1.1 cegger 79 1.1 cegger #define DALB_SYSTEM_WAKEUP 0x02 80 1.1 cegger #define DALB_SYSTEM_RUNTIME 0x80 81 1.1 cegger 82 1.1 cegger static int 83 1.1 cegger acpi_dalb_match(device_t parent, cfdata_t match, void *aux) 84 1.1 cegger { 85 1.1 cegger struct acpi_attach_args *aa = aux; 86 1.1 cegger 87 1.19 thorpej return acpi_compatible_match(aa, compat_data); 88 1.1 cegger } 89 1.1 cegger 90 1.1 cegger static void 91 1.1 cegger acpi_dalb_sysmon_init(struct acpi_dalb_softc *sc) 92 1.1 cegger { 93 1.1 cegger sc->sc_smpsw_valid = true; 94 1.1 cegger sc->sc_smpsw.smpsw_name = device_xname(sc->sc_dev); 95 1.1 cegger sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_HOTKEY; 96 1.1 cegger if (sysmon_pswitch_register(&sc->sc_smpsw)) { 97 1.1 cegger aprint_error_dev(sc->sc_dev, 98 1.1 cegger "couldn't register sleep with sysmon\n"); 99 1.1 cegger sc->sc_smpsw_valid = false; 100 1.1 cegger } 101 1.1 cegger } 102 1.1 cegger 103 1.1 cegger 104 1.1 cegger static void 105 1.1 cegger acpi_dalb_init(device_t dev) 106 1.1 cegger { 107 1.1 cegger struct acpi_dalb_softc *sc = device_private(dev); 108 1.1 cegger ACPI_OBJECT *obj; 109 1.1 cegger ACPI_STATUS rv; 110 1.1 cegger ACPI_BUFFER ret; 111 1.1 cegger 112 1.4 jruoho rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret); 113 1.12 jruoho 114 1.1 cegger if (ACPI_FAILURE(rv) || ret.Pointer == NULL) { 115 1.1 cegger aprint_error_dev(dev, 116 1.1 cegger "couldn't enable notify handler: (%s)\n", 117 1.1 cegger AcpiFormatException(rv)); 118 1.1 cegger return; 119 1.1 cegger } 120 1.1 cegger 121 1.12 jruoho obj = ret.Pointer; 122 1.12 jruoho 123 1.1 cegger if (obj->Type != ACPI_TYPE_BUFFER) { 124 1.1 cegger sc->sc_usageid = DALB_ID_INVALID; 125 1.8 jruoho aprint_debug_dev(dev, "invalid ACPI type: %u\n", obj->Type); 126 1.1 cegger goto out; 127 1.1 cegger } 128 1.1 cegger 129 1.1 cegger switch (obj->Buffer.Length) { 130 1.1 cegger case 1: 131 1.1 cegger sc->sc_usageid = *(uint8_t *)obj->Buffer.Pointer; 132 1.1 cegger break; 133 1.1 cegger case 2: 134 1.1 cegger sc->sc_usageid = le16toh(*(uint16_t *)obj->Buffer.Pointer); 135 1.1 cegger break; 136 1.1 cegger case 4: 137 1.1 cegger sc->sc_usageid = le32toh(*(uint32_t *)obj->Buffer.Pointer); 138 1.1 cegger break; 139 1.1 cegger default: 140 1.1 cegger aprint_debug_dev(dev, "unhandled ret.Length: 0x%lx\n", 141 1.1 cegger (unsigned long)obj->Buffer.Length); 142 1.1 cegger sc->sc_usageid = DALB_ID_INVALID; 143 1.1 cegger break; 144 1.1 cegger } 145 1.1 cegger 146 1.1 cegger out: 147 1.3 mlelstv ACPI_FREE(ret.Pointer); 148 1.1 cegger } 149 1.1 cegger 150 1.1 cegger static void 151 1.1 cegger acpi_dalb_attach(device_t parent, device_t self, void *aux) 152 1.1 cegger { 153 1.1 cegger struct acpi_dalb_softc *sc = device_private(self); 154 1.1 cegger struct acpi_attach_args *aa = aux; 155 1.1 cegger 156 1.1 cegger aprint_naive("\n"); 157 1.1 cegger aprint_normal(": Direct Application Launch Button\n"); 158 1.1 cegger 159 1.14 jruoho sc->sc_dev = self; 160 1.1 cegger sc->sc_node = aa->aa_node; 161 1.1 cegger 162 1.1 cegger config_interrupts(self, acpi_dalb_init); 163 1.1 cegger 164 1.14 jruoho (void)pmf_device_register(self, NULL, acpi_dalb_resume); 165 1.14 jruoho (void)acpi_register_notify(sc->sc_node, acpi_dalb_notify_handler); 166 1.1 cegger 167 1.1 cegger sc->sc_smpsw_valid = false; 168 1.1 cegger acpi_dalb_sysmon_init(sc); 169 1.1 cegger } 170 1.1 cegger 171 1.6 jruoho static int 172 1.6 jruoho acpi_dalb_detach(device_t self, int flags) 173 1.6 jruoho { 174 1.6 jruoho struct acpi_dalb_softc *sc = device_private(self); 175 1.6 jruoho 176 1.6 jruoho pmf_device_deregister(self); 177 1.14 jruoho acpi_deregister_notify(sc->sc_node); 178 1.6 jruoho sysmon_pswitch_unregister(&sc->sc_smpsw); 179 1.6 jruoho 180 1.6 jruoho return 0; 181 1.6 jruoho } 182 1.6 jruoho 183 1.1 cegger static void 184 1.13 jruoho acpi_dalb_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 185 1.1 cegger { 186 1.1 cegger device_t dev = opaque; 187 1.1 cegger struct acpi_dalb_softc *sc = device_private(dev); 188 1.1 cegger ACPI_STATUS rv; 189 1.1 cegger 190 1.1 cegger switch (notify) { 191 1.1 cegger case DALB_SYSTEM_WAKEUP: 192 1.1 cegger rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 193 1.1 cegger acpi_dalb_get_wakeup_hotkeys, dev); 194 1.1 cegger break; 195 1.1 cegger case DALB_SYSTEM_RUNTIME: 196 1.1 cegger rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 197 1.1 cegger acpi_dalb_get_runtime_hotkeys, dev); 198 1.16 jruoho break; 199 1.1 cegger 200 1.1 cegger default: 201 1.1 cegger aprint_error_dev(dev, 202 1.1 cegger "unknown notification event 0x%x from button 0x%x\n", 203 1.1 cegger notify, (uint32_t)sc->sc_usageid); 204 1.1 cegger return; 205 1.1 cegger } 206 1.1 cegger 207 1.1 cegger if (ACPI_FAILURE(rv)) 208 1.1 cegger aprint_error_dev(dev, "couldn't queue hotkey handler: %s\n", 209 1.1 cegger AcpiFormatException(rv)); 210 1.1 cegger } 211 1.1 cegger 212 1.1 cegger static void 213 1.1 cegger acpi_dalb_get_wakeup_hotkeys(void *opaque) 214 1.1 cegger { 215 1.1 cegger device_t dev = opaque; 216 1.1 cegger struct acpi_dalb_softc *sc = device_private(dev); 217 1.1 cegger 218 1.1 cegger if (!sc->sc_smpsw_valid) 219 1.1 cegger return; 220 1.11 jruoho 221 1.11 jruoho ACPI_DEBUG_PRINT((ACPI_DB_INFO, 222 1.15 jruoho "invoking %s (wakeup)\n", sc->sc_smpsw.smpsw_name)); 223 1.11 jruoho 224 1.1 cegger sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 225 1.1 cegger } 226 1.1 cegger 227 1.1 cegger static void 228 1.1 cegger acpi_dalb_get_runtime_hotkeys(void *opaque) 229 1.1 cegger { 230 1.1 cegger device_t dev = opaque; 231 1.1 cegger struct acpi_dalb_softc *sc = device_private(dev); 232 1.1 cegger 233 1.1 cegger if (!sc->sc_smpsw_valid) 234 1.1 cegger return; 235 1.11 jruoho 236 1.11 jruoho ACPI_DEBUG_PRINT((ACPI_DB_INFO, 237 1.15 jruoho "invoking %s (runtime)\n", sc->sc_smpsw.smpsw_name)); 238 1.11 jruoho 239 1.1 cegger sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 240 1.1 cegger } 241 1.1 cegger 242 1.1 cegger static bool 243 1.7 dyoung acpi_dalb_resume(device_t dev, const pmf_qual_t *qual) 244 1.1 cegger { 245 1.2 jmcneill struct acpi_dalb_softc *sc = device_private(dev); 246 1.2 jmcneill ACPI_STATUS rv; 247 1.2 jmcneill ACPI_BUFFER ret; 248 1.2 jmcneill 249 1.4 jruoho rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret); 250 1.2 jmcneill if (ACPI_FAILURE(rv)) { 251 1.2 jmcneill aprint_error_dev(dev, "couldn't evaluate GHID: %s\n", 252 1.2 jmcneill AcpiFormatException(rv)); 253 1.2 jmcneill return false; 254 1.2 jmcneill } 255 1.2 jmcneill if (ret.Pointer) 256 1.3 mlelstv ACPI_FREE(ret.Pointer); 257 1.2 jmcneill 258 1.1 cegger return true; 259 1.1 cegger } 260 1.16 jruoho 261 1.18 pgoyette MODULE(MODULE_CLASS_DRIVER, acpidalb, "sysmon_power"); 262 1.16 jruoho 263 1.17 jruoho #ifdef _MODULE 264 1.16 jruoho #include "ioconf.c" 265 1.17 jruoho #endif 266 1.16 jruoho 267 1.16 jruoho static int 268 1.17 jruoho acpidalb_modcmd(modcmd_t cmd, void *aux) 269 1.16 jruoho { 270 1.17 jruoho int rv = 0; 271 1.16 jruoho 272 1.16 jruoho switch (cmd) { 273 1.16 jruoho 274 1.16 jruoho case MODULE_CMD_INIT: 275 1.17 jruoho 276 1.17 jruoho #ifdef _MODULE 277 1.17 jruoho rv = config_init_component(cfdriver_ioconf_acpidalb, 278 1.16 jruoho cfattach_ioconf_acpidalb, cfdata_ioconf_acpidalb); 279 1.17 jruoho #endif 280 1.17 jruoho break; 281 1.16 jruoho 282 1.16 jruoho case MODULE_CMD_FINI: 283 1.17 jruoho 284 1.17 jruoho #ifdef _MODULE 285 1.17 jruoho rv = config_fini_component(cfdriver_ioconf_acpidalb, 286 1.16 jruoho cfattach_ioconf_acpidalb, cfdata_ioconf_acpidalb); 287 1.17 jruoho #endif 288 1.17 jruoho break; 289 1.16 jruoho 290 1.16 jruoho default: 291 1.17 jruoho rv = ENOTTY; 292 1.16 jruoho } 293 1.17 jruoho 294 1.17 jruoho return rv; 295 1.16 jruoho } 296