1 /* $NetBSD: dalb_acpi.c,v 1.19 2021/01/29 15:49:55 thorpej 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.19 2021/01/29 15:49:55 thorpej 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 struct device_compatible_entry compat_data[] = { 75 { .compat = "PNP0C32" }, /* Direct Application Launch Button */ 76 DEVICE_COMPAT_EOL 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 return acpi_compatible_match(aa, compat_data); 88 } 89 90 static void 91 acpi_dalb_sysmon_init(struct acpi_dalb_softc *sc) 92 { 93 sc->sc_smpsw_valid = true; 94 sc->sc_smpsw.smpsw_name = device_xname(sc->sc_dev); 95 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_HOTKEY; 96 if (sysmon_pswitch_register(&sc->sc_smpsw)) { 97 aprint_error_dev(sc->sc_dev, 98 "couldn't register sleep with sysmon\n"); 99 sc->sc_smpsw_valid = false; 100 } 101 } 102 103 104 static void 105 acpi_dalb_init(device_t dev) 106 { 107 struct acpi_dalb_softc *sc = device_private(dev); 108 ACPI_OBJECT *obj; 109 ACPI_STATUS rv; 110 ACPI_BUFFER ret; 111 112 rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret); 113 114 if (ACPI_FAILURE(rv) || ret.Pointer == NULL) { 115 aprint_error_dev(dev, 116 "couldn't enable notify handler: (%s)\n", 117 AcpiFormatException(rv)); 118 return; 119 } 120 121 obj = ret.Pointer; 122 123 if (obj->Type != ACPI_TYPE_BUFFER) { 124 sc->sc_usageid = DALB_ID_INVALID; 125 aprint_debug_dev(dev, "invalid ACPI type: %u\n", obj->Type); 126 goto out; 127 } 128 129 switch (obj->Buffer.Length) { 130 case 1: 131 sc->sc_usageid = *(uint8_t *)obj->Buffer.Pointer; 132 break; 133 case 2: 134 sc->sc_usageid = le16toh(*(uint16_t *)obj->Buffer.Pointer); 135 break; 136 case 4: 137 sc->sc_usageid = le32toh(*(uint32_t *)obj->Buffer.Pointer); 138 break; 139 default: 140 aprint_debug_dev(dev, "unhandled ret.Length: 0x%lx\n", 141 (unsigned long)obj->Buffer.Length); 142 sc->sc_usageid = DALB_ID_INVALID; 143 break; 144 } 145 146 out: 147 ACPI_FREE(ret.Pointer); 148 } 149 150 static void 151 acpi_dalb_attach(device_t parent, device_t self, void *aux) 152 { 153 struct acpi_dalb_softc *sc = device_private(self); 154 struct acpi_attach_args *aa = aux; 155 156 aprint_naive("\n"); 157 aprint_normal(": Direct Application Launch Button\n"); 158 159 sc->sc_dev = self; 160 sc->sc_node = aa->aa_node; 161 162 config_interrupts(self, acpi_dalb_init); 163 164 (void)pmf_device_register(self, NULL, acpi_dalb_resume); 165 (void)acpi_register_notify(sc->sc_node, acpi_dalb_notify_handler); 166 167 sc->sc_smpsw_valid = false; 168 acpi_dalb_sysmon_init(sc); 169 } 170 171 static int 172 acpi_dalb_detach(device_t self, int flags) 173 { 174 struct acpi_dalb_softc *sc = device_private(self); 175 176 pmf_device_deregister(self); 177 acpi_deregister_notify(sc->sc_node); 178 sysmon_pswitch_unregister(&sc->sc_smpsw); 179 180 return 0; 181 } 182 183 static void 184 acpi_dalb_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 185 { 186 device_t dev = opaque; 187 struct acpi_dalb_softc *sc = device_private(dev); 188 ACPI_STATUS rv; 189 190 switch (notify) { 191 case DALB_SYSTEM_WAKEUP: 192 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 193 acpi_dalb_get_wakeup_hotkeys, dev); 194 break; 195 case DALB_SYSTEM_RUNTIME: 196 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 197 acpi_dalb_get_runtime_hotkeys, dev); 198 break; 199 200 default: 201 aprint_error_dev(dev, 202 "unknown notification event 0x%x from button 0x%x\n", 203 notify, (uint32_t)sc->sc_usageid); 204 return; 205 } 206 207 if (ACPI_FAILURE(rv)) 208 aprint_error_dev(dev, "couldn't queue hotkey handler: %s\n", 209 AcpiFormatException(rv)); 210 } 211 212 static void 213 acpi_dalb_get_wakeup_hotkeys(void *opaque) 214 { 215 device_t dev = opaque; 216 struct acpi_dalb_softc *sc = device_private(dev); 217 218 if (!sc->sc_smpsw_valid) 219 return; 220 221 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 222 "invoking %s (wakeup)\n", sc->sc_smpsw.smpsw_name)); 223 224 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 225 } 226 227 static void 228 acpi_dalb_get_runtime_hotkeys(void *opaque) 229 { 230 device_t dev = opaque; 231 struct acpi_dalb_softc *sc = device_private(dev); 232 233 if (!sc->sc_smpsw_valid) 234 return; 235 236 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 237 "invoking %s (runtime)\n", sc->sc_smpsw.smpsw_name)); 238 239 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 240 } 241 242 static bool 243 acpi_dalb_resume(device_t dev, const pmf_qual_t *qual) 244 { 245 struct acpi_dalb_softc *sc = device_private(dev); 246 ACPI_STATUS rv; 247 ACPI_BUFFER ret; 248 249 rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret); 250 if (ACPI_FAILURE(rv)) { 251 aprint_error_dev(dev, "couldn't evaluate GHID: %s\n", 252 AcpiFormatException(rv)); 253 return false; 254 } 255 if (ret.Pointer) 256 ACPI_FREE(ret.Pointer); 257 258 return true; 259 } 260 261 MODULE(MODULE_CLASS_DRIVER, acpidalb, "sysmon_power"); 262 263 #ifdef _MODULE 264 #include "ioconf.c" 265 #endif 266 267 static int 268 acpidalb_modcmd(modcmd_t cmd, void *aux) 269 { 270 int rv = 0; 271 272 switch (cmd) { 273 274 case MODULE_CMD_INIT: 275 276 #ifdef _MODULE 277 rv = config_init_component(cfdriver_ioconf_acpidalb, 278 cfattach_ioconf_acpidalb, cfdata_ioconf_acpidalb); 279 #endif 280 break; 281 282 case MODULE_CMD_FINI: 283 284 #ifdef _MODULE 285 rv = config_fini_component(cfdriver_ioconf_acpidalb, 286 cfattach_ioconf_acpidalb, cfdata_ioconf_acpidalb); 287 #endif 288 break; 289 290 default: 291 rv = ENOTTY; 292 } 293 294 return rv; 295 } 296