1 /* $NetBSD: dalb_acpi.c,v 1.10 2010/03/08 11:39:42 jruoho 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.10 2010/03/08 11:39:42 jruoho 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/systm.h> 40 41 #include <dev/acpi/acpireg.h> 42 #include <dev/acpi/acpivar.h> 43 44 #define _COMPONENT ACPI_RESOURCE_COMPONENT 45 ACPI_MODULE_NAME ("dalb_acpi") 46 47 #define DALB_ID_INVALID -1 48 49 struct acpi_dalb_softc { 50 device_t sc_dev; 51 struct acpi_devnode *sc_node; 52 53 ACPI_INTEGER sc_usageid; 54 55 /* There's one PNP0C32 ACPI device for each button. 56 * Therefore, one instance is enough. */ 57 struct sysmon_pswitch sc_smpsw; 58 bool sc_smpsw_valid; 59 }; 60 61 static int acpi_dalb_match(device_t, cfdata_t, void *); 62 static void acpi_dalb_attach(device_t, device_t, void *); 63 static int acpi_dalb_detach(device_t, int); 64 static void acpi_dalb_notify_handler(ACPI_HANDLE, UINT32, void *); 65 static bool acpi_dalb_resume(device_t, const pmf_qual_t *); 66 67 static void acpi_dalb_get_wakeup_hotkeys(void *opaque); 68 static void acpi_dalb_get_runtime_hotkeys(void *opaque); 69 70 CFATTACH_DECL_NEW(acpidalb, sizeof(struct acpi_dalb_softc), 71 acpi_dalb_match, acpi_dalb_attach, acpi_dalb_detach, NULL); 72 73 static const char * const acpi_dalb_ids[] = { 74 "PNP0C32", /* Direct Application Launch Button */ 75 NULL 76 }; 77 78 #ifdef DEBUG 79 #define DPRINTF(x) printf x 80 #else 81 #define DPRINTF(x) 82 #endif 83 84 #define DALB_SYSTEM_WAKEUP 0x02 85 #define DALB_SYSTEM_RUNTIME 0x80 86 87 static int 88 acpi_dalb_match(device_t parent, cfdata_t match, void *aux) 89 { 90 struct acpi_attach_args *aa = aux; 91 92 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 93 return 0; 94 95 return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_dalb_ids); 96 } 97 98 static void 99 acpi_dalb_sysmon_init(struct acpi_dalb_softc *sc) 100 { 101 sc->sc_smpsw_valid = true; 102 sc->sc_smpsw.smpsw_name = device_xname(sc->sc_dev); 103 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_HOTKEY; 104 if (sysmon_pswitch_register(&sc->sc_smpsw)) { 105 aprint_error_dev(sc->sc_dev, 106 "couldn't register sleep with sysmon\n"); 107 sc->sc_smpsw_valid = false; 108 } 109 } 110 111 112 static void 113 acpi_dalb_init(device_t dev) 114 { 115 struct acpi_dalb_softc *sc = device_private(dev); 116 ACPI_OBJECT *obj; 117 ACPI_STATUS rv; 118 ACPI_BUFFER ret; 119 120 rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret); 121 if (ACPI_FAILURE(rv) || ret.Pointer == NULL) { 122 aprint_error_dev(dev, 123 "couldn't enable notify handler: (%s)\n", 124 AcpiFormatException(rv)); 125 return; 126 } 127 128 obj = (ACPI_OBJECT *)ret.Pointer; 129 if (obj->Type != ACPI_TYPE_BUFFER) { 130 sc->sc_usageid = DALB_ID_INVALID; 131 aprint_debug_dev(dev, "invalid ACPI type: %u\n", obj->Type); 132 goto out; 133 } 134 135 switch (obj->Buffer.Length) { 136 case 1: 137 sc->sc_usageid = *(uint8_t *)obj->Buffer.Pointer; 138 break; 139 case 2: 140 sc->sc_usageid = le16toh(*(uint16_t *)obj->Buffer.Pointer); 141 break; 142 case 4: 143 sc->sc_usageid = le32toh(*(uint32_t *)obj->Buffer.Pointer); 144 break; 145 default: 146 aprint_debug_dev(dev, "unhandled ret.Length: 0x%lx\n", 147 (unsigned long)obj->Buffer.Length); 148 sc->sc_usageid = DALB_ID_INVALID; 149 break; 150 } 151 152 out: 153 ACPI_FREE(ret.Pointer); 154 } 155 156 static void 157 acpi_dalb_attach(device_t parent, device_t self, void *aux) 158 { 159 struct acpi_dalb_softc *sc = device_private(self); 160 struct acpi_attach_args *aa = aux; 161 ACPI_STATUS rv; 162 163 aprint_naive("\n"); 164 aprint_normal(": Direct Application Launch Button\n"); 165 166 sc->sc_node = aa->aa_node; 167 sc->sc_dev = self; 168 169 config_interrupts(self, acpi_dalb_init); 170 171 /* Install notify handler */ 172 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 173 ACPI_ALL_NOTIFY, acpi_dalb_notify_handler, self); 174 if (ACPI_FAILURE(rv)) 175 aprint_error_dev(self, 176 "couldn't install notify handler: (%s)\n", 177 AcpiFormatException(rv)); 178 179 sc->sc_smpsw_valid = false; 180 acpi_dalb_sysmon_init(sc); 181 182 if (!pmf_device_register(self, NULL, acpi_dalb_resume)) 183 aprint_error_dev(self, "couldn't establish power handler\n"); 184 } 185 186 static int 187 acpi_dalb_detach(device_t self, int flags) 188 { 189 struct acpi_dalb_softc *sc = device_private(self); 190 ACPI_STATUS rv; 191 192 rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle, 193 ACPI_ALL_NOTIFY, acpi_dalb_notify_handler); 194 195 if (ACPI_FAILURE(rv)) 196 return EBUSY; 197 198 pmf_device_deregister(self); 199 sysmon_pswitch_unregister(&sc->sc_smpsw); 200 201 return 0; 202 } 203 204 static void 205 acpi_dalb_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque) 206 { 207 device_t dev = opaque; 208 struct acpi_dalb_softc *sc = device_private(dev); 209 ACPI_STATUS rv; 210 211 switch (notify) { 212 case DALB_SYSTEM_WAKEUP: 213 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 214 acpi_dalb_get_wakeup_hotkeys, dev); 215 break; 216 case DALB_SYSTEM_RUNTIME: 217 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 218 acpi_dalb_get_runtime_hotkeys, dev); 219 break; 220 221 default: 222 aprint_error_dev(dev, 223 "unknown notification event 0x%x from button 0x%x\n", 224 notify, (uint32_t)sc->sc_usageid); 225 return; 226 } 227 228 if (ACPI_FAILURE(rv)) 229 aprint_error_dev(dev, "couldn't queue hotkey handler: %s\n", 230 AcpiFormatException(rv)); 231 } 232 233 static void 234 acpi_dalb_get_wakeup_hotkeys(void *opaque) 235 { 236 device_t dev = opaque; 237 struct acpi_dalb_softc *sc = device_private(dev); 238 239 if (!sc->sc_smpsw_valid) 240 return; 241 DPRINTF(("%s: %s: invoking sysmon_pswitch_event\n", 242 sc->sc_smpsw.smpsw_name, __func__)); 243 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 244 } 245 246 static void 247 acpi_dalb_get_runtime_hotkeys(void *opaque) 248 { 249 device_t dev = opaque; 250 struct acpi_dalb_softc *sc = device_private(dev); 251 252 if (!sc->sc_smpsw_valid) 253 return; 254 DPRINTF(("%s: %s: invoking sysmon_pswitch_event\n", 255 sc->sc_smpsw.smpsw_name, __func__)); 256 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 257 } 258 259 static bool 260 acpi_dalb_resume(device_t dev, const pmf_qual_t *qual) 261 { 262 struct acpi_dalb_softc *sc = device_private(dev); 263 ACPI_STATUS rv; 264 ACPI_BUFFER ret; 265 266 rv = acpi_eval_struct(sc->sc_node->ad_handle, "GHID", &ret); 267 if (ACPI_FAILURE(rv)) { 268 aprint_error_dev(dev, "couldn't evaluate GHID: %s\n", 269 AcpiFormatException(rv)); 270 return false; 271 } 272 if (ret.Pointer) 273 ACPI_FREE(ret.Pointer); 274 275 return true; 276 } 277