1 1.23 riastrad /* $NetBSD: wmi_acpi.c,v 1.23 2023/08/11 08:36:59 riastradh Exp $ */ 2 1.1 jruoho 3 1.1 jruoho /*- 4 1.1 jruoho * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen (at) iki.fi> 5 1.1 jruoho * All rights reserved. 6 1.1 jruoho * 7 1.1 jruoho * Redistribution and use in source and binary forms, with or without 8 1.1 jruoho * modification, are permitted provided that the following conditions 9 1.1 jruoho * are met: 10 1.1 jruoho * 11 1.1 jruoho * 1. Redistributions of source code must retain the above copyright 12 1.1 jruoho * notice, this list of conditions and the following disclaimer. 13 1.1 jruoho * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 jruoho * notice, this list of conditions and the following disclaimer in the 15 1.1 jruoho * documentation and/or other materials provided with the distribution. 16 1.1 jruoho * 17 1.1 jruoho * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 1.1 jruoho * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 1.1 jruoho * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 jruoho * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 1.1 jruoho * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 jruoho * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 1.1 jruoho * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 1.1 jruoho * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 1.1 jruoho * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 1.1 jruoho * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 1.1 jruoho * SUCH DAMAGE. 28 1.1 jruoho */ 29 1.1 jruoho #include <sys/cdefs.h> 30 1.23 riastrad __KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.23 2023/08/11 08:36:59 riastradh Exp $"); 31 1.1 jruoho 32 1.1 jruoho #include <sys/param.h> 33 1.1 jruoho #include <sys/device.h> 34 1.1 jruoho #include <sys/endian.h> 35 1.1 jruoho #include <sys/kmem.h> 36 1.1 jruoho #include <sys/systm.h> 37 1.9 jmcneill #include <sys/module.h> 38 1.1 jruoho 39 1.1 jruoho #include <dev/acpi/acpireg.h> 40 1.1 jruoho #include <dev/acpi/acpivar.h> 41 1.11 jruoho #include <dev/acpi/acpi_ecvar.h> 42 1.1 jruoho #include <dev/acpi/wmi/wmi_acpivar.h> 43 1.1 jruoho 44 1.1 jruoho #define _COMPONENT ACPI_RESOURCE_COMPONENT 45 1.1 jruoho ACPI_MODULE_NAME ("wmi_acpi") 46 1.1 jruoho 47 1.1 jruoho /* 48 1.1 jruoho * This implements something called "Microsoft Windows Management 49 1.20 andvar * Instrumentation" (WMI). This subset of ACPI is described in: 50 1.1 jruoho * 51 1.1 jruoho * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx 52 1.1 jruoho * 53 1.1 jruoho * (Obtained on Thu Feb 12 18:21:44 EET 2009.) 54 1.1 jruoho */ 55 1.1 jruoho 56 1.11 jruoho static int acpi_wmi_match(device_t, cfdata_t, void *); 57 1.11 jruoho static void acpi_wmi_attach(device_t, device_t, void *); 58 1.11 jruoho static int acpi_wmi_detach(device_t, int); 59 1.11 jruoho static int acpi_wmi_rescan(device_t, const char *, const int *); 60 1.11 jruoho static void acpi_wmi_childdet(device_t, device_t); 61 1.11 jruoho static int acpi_wmi_print(void *, const char *); 62 1.11 jruoho static bool acpi_wmi_init(struct acpi_wmi_softc *); 63 1.11 jruoho static void acpi_wmi_init_ec(struct acpi_wmi_softc *); 64 1.15 chs static void acpi_wmi_add(struct acpi_wmi_softc *, ACPI_OBJECT *); 65 1.11 jruoho static void acpi_wmi_del(struct acpi_wmi_softc *); 66 1.11 jruoho static void acpi_wmi_dump(struct acpi_wmi_softc *); 67 1.11 jruoho static ACPI_STATUS acpi_wmi_guid_get(struct acpi_wmi_softc *, 68 1.11 jruoho const char *, struct wmi_t **); 69 1.11 jruoho static void acpi_wmi_event_add(struct acpi_wmi_softc *); 70 1.11 jruoho static void acpi_wmi_event_del(struct acpi_wmi_softc *); 71 1.11 jruoho static void acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *); 72 1.11 jruoho static ACPI_STATUS acpi_wmi_ec_handler(uint32_t, ACPI_PHYSICAL_ADDRESS, 73 1.11 jruoho uint32_t, ACPI_INTEGER *, void *, void *); 74 1.11 jruoho static bool acpi_wmi_suspend(device_t, const pmf_qual_t *); 75 1.11 jruoho static bool acpi_wmi_resume(device_t, const pmf_qual_t *); 76 1.13 jakllsch static ACPI_STATUS acpi_wmi_enable_event(ACPI_HANDLE, uint8_t, bool); 77 1.13 jakllsch static ACPI_STATUS acpi_wmi_enable_collection(ACPI_HANDLE, const char *, bool); 78 1.11 jruoho static bool acpi_wmi_input(struct wmi_t *, uint8_t, uint8_t); 79 1.1 jruoho 80 1.17 thorpej static const struct device_compatible_entry compat_data[] = { 81 1.17 thorpej { .compat = "PNP0C14" }, 82 1.17 thorpej { .compat = "pnp0c14" }, 83 1.17 thorpej DEVICE_COMPAT_EOL 84 1.1 jruoho }; 85 1.1 jruoho 86 1.8 jmcneill CFATTACH_DECL2_NEW(acpiwmi, sizeof(struct acpi_wmi_softc), 87 1.8 jmcneill acpi_wmi_match, acpi_wmi_attach, acpi_wmi_detach, NULL, 88 1.8 jmcneill acpi_wmi_rescan, acpi_wmi_childdet); 89 1.1 jruoho 90 1.1 jruoho static int 91 1.1 jruoho acpi_wmi_match(device_t parent, cfdata_t match, void *aux) 92 1.1 jruoho { 93 1.1 jruoho struct acpi_attach_args *aa = aux; 94 1.1 jruoho 95 1.17 thorpej return acpi_compatible_match(aa, compat_data); 96 1.1 jruoho } 97 1.1 jruoho 98 1.1 jruoho static void 99 1.1 jruoho acpi_wmi_attach(device_t parent, device_t self, void *aux) 100 1.1 jruoho { 101 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 102 1.1 jruoho struct acpi_attach_args *aa = aux; 103 1.1 jruoho 104 1.1 jruoho sc->sc_dev = self; 105 1.1 jruoho sc->sc_node = aa->aa_node; 106 1.1 jruoho 107 1.1 jruoho sc->sc_child = NULL; 108 1.11 jruoho sc->sc_ecdev = NULL; 109 1.1 jruoho sc->sc_handler = NULL; 110 1.1 jruoho 111 1.1 jruoho aprint_naive("\n"); 112 1.1 jruoho aprint_normal(": ACPI WMI Interface\n"); 113 1.1 jruoho 114 1.1 jruoho if (acpi_wmi_init(sc) != true) 115 1.1 jruoho return; 116 1.1 jruoho 117 1.7 jruoho acpi_wmi_dump(sc); 118 1.11 jruoho acpi_wmi_init_ec(sc); 119 1.4 jruoho acpi_wmi_event_add(sc); 120 1.8 jmcneill acpi_wmi_rescan(self, NULL, NULL); 121 1.4 jruoho 122 1.4 jruoho (void)pmf_device_register(self, acpi_wmi_suspend, acpi_wmi_resume); 123 1.1 jruoho } 124 1.1 jruoho 125 1.1 jruoho static int 126 1.1 jruoho acpi_wmi_detach(device_t self, int flags) 127 1.1 jruoho { 128 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 129 1.21 riastrad int error; 130 1.21 riastrad 131 1.21 riastrad error = config_detach_children(self, flags); 132 1.21 riastrad if (error) 133 1.21 riastrad return error; 134 1.1 jruoho 135 1.4 jruoho acpi_wmi_event_del(sc); 136 1.1 jruoho 137 1.11 jruoho if (sc->sc_ecdev != NULL) { 138 1.11 jruoho (void)AcpiRemoveAddressSpaceHandler(sc->sc_node->ad_handle, 139 1.11 jruoho ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler); 140 1.11 jruoho } 141 1.11 jruoho 142 1.1 jruoho acpi_wmi_del(sc); 143 1.1 jruoho pmf_device_deregister(self); 144 1.1 jruoho 145 1.1 jruoho return 0; 146 1.1 jruoho } 147 1.1 jruoho 148 1.1 jruoho static int 149 1.8 jmcneill acpi_wmi_rescan(device_t self, const char *ifattr, const int *locators) 150 1.8 jmcneill { 151 1.8 jmcneill struct acpi_wmi_softc *sc = device_private(self); 152 1.8 jmcneill 153 1.18 thorpej if (sc->sc_child == NULL) { 154 1.18 thorpej sc->sc_child = 155 1.19 thorpej config_found(self, NULL, acpi_wmi_print, CFARGS_NONE); 156 1.18 thorpej } 157 1.8 jmcneill 158 1.8 jmcneill return 0; 159 1.8 jmcneill } 160 1.8 jmcneill 161 1.8 jmcneill static void 162 1.8 jmcneill acpi_wmi_childdet(device_t self, device_t child) 163 1.8 jmcneill { 164 1.8 jmcneill struct acpi_wmi_softc *sc = device_private(self); 165 1.8 jmcneill 166 1.8 jmcneill if (sc->sc_child == child) 167 1.8 jmcneill sc->sc_child = NULL; 168 1.8 jmcneill } 169 1.8 jmcneill 170 1.8 jmcneill static int 171 1.1 jruoho acpi_wmi_print(void *aux, const char *pnp) 172 1.1 jruoho { 173 1.1 jruoho 174 1.1 jruoho if (pnp != NULL) 175 1.1 jruoho aprint_normal("acpiwmibus at %s", pnp); 176 1.1 jruoho 177 1.1 jruoho return UNCONF; 178 1.1 jruoho } 179 1.1 jruoho 180 1.1 jruoho static bool 181 1.1 jruoho acpi_wmi_init(struct acpi_wmi_softc *sc) 182 1.1 jruoho { 183 1.1 jruoho ACPI_OBJECT *obj; 184 1.1 jruoho ACPI_BUFFER buf; 185 1.1 jruoho ACPI_STATUS rv; 186 1.1 jruoho uint32_t len; 187 1.1 jruoho 188 1.1 jruoho rv = acpi_eval_struct(sc->sc_node->ad_handle, "_WDG", &buf); 189 1.1 jruoho 190 1.1 jruoho if (ACPI_FAILURE(rv)) 191 1.1 jruoho goto fail; 192 1.1 jruoho 193 1.1 jruoho obj = buf.Pointer; 194 1.1 jruoho 195 1.1 jruoho if (obj->Type != ACPI_TYPE_BUFFER) { 196 1.1 jruoho rv = AE_TYPE; 197 1.1 jruoho goto fail; 198 1.1 jruoho } 199 1.1 jruoho 200 1.1 jruoho len = obj->Buffer.Length; 201 1.1 jruoho 202 1.1 jruoho if (len != obj->Package.Count) { 203 1.1 jruoho rv = AE_BAD_VALUE; 204 1.1 jruoho goto fail; 205 1.1 jruoho } 206 1.1 jruoho 207 1.1 jruoho CTASSERT(sizeof(struct guid_t) == 20); 208 1.1 jruoho 209 1.1 jruoho if (len < sizeof(struct guid_t) || 210 1.1 jruoho len % sizeof(struct guid_t) != 0) { 211 1.1 jruoho rv = AE_BAD_DATA; 212 1.1 jruoho goto fail; 213 1.1 jruoho } 214 1.1 jruoho 215 1.15 chs acpi_wmi_add(sc, obj); 216 1.15 chs return true; 217 1.1 jruoho 218 1.1 jruoho fail: 219 1.1 jruoho aprint_error_dev(sc->sc_dev, "failed to evaluate _WDG: %s\n", 220 1.1 jruoho AcpiFormatException(rv)); 221 1.1 jruoho 222 1.1 jruoho if (buf.Pointer != NULL) 223 1.1 jruoho ACPI_FREE(buf.Pointer); 224 1.1 jruoho 225 1.1 jruoho return false; 226 1.1 jruoho } 227 1.1 jruoho 228 1.15 chs static void 229 1.1 jruoho acpi_wmi_add(struct acpi_wmi_softc *sc, ACPI_OBJECT *obj) 230 1.1 jruoho { 231 1.1 jruoho struct wmi_t *wmi; 232 1.1 jruoho size_t i, n, offset, siz; 233 1.1 jruoho 234 1.1 jruoho siz = sizeof(struct guid_t); 235 1.1 jruoho n = obj->Buffer.Length / siz; 236 1.1 jruoho 237 1.1 jruoho SIMPLEQ_INIT(&sc->wmi_head); 238 1.1 jruoho 239 1.1 jruoho for (i = offset = 0; i < n; ++i) { 240 1.1 jruoho 241 1.15 chs wmi = kmem_zalloc(sizeof(*wmi), KM_SLEEP); 242 1.1 jruoho (void)memcpy(&wmi->guid, obj->Buffer.Pointer + offset, siz); 243 1.1 jruoho 244 1.1 jruoho wmi->eevent = false; 245 1.1 jruoho offset = offset + siz; 246 1.1 jruoho 247 1.1 jruoho SIMPLEQ_INSERT_TAIL(&sc->wmi_head, wmi, wmi_link); 248 1.1 jruoho } 249 1.1 jruoho 250 1.1 jruoho ACPI_FREE(obj); 251 1.1 jruoho } 252 1.1 jruoho 253 1.1 jruoho static void 254 1.1 jruoho acpi_wmi_del(struct acpi_wmi_softc *sc) 255 1.1 jruoho { 256 1.1 jruoho struct wmi_t *wmi; 257 1.1 jruoho 258 1.1 jruoho while (SIMPLEQ_FIRST(&sc->wmi_head) != NULL) { 259 1.1 jruoho wmi = SIMPLEQ_FIRST(&sc->wmi_head); 260 1.1 jruoho SIMPLEQ_REMOVE_HEAD(&sc->wmi_head, wmi_link); 261 1.1 jruoho kmem_free(wmi, sizeof(*wmi)); 262 1.1 jruoho } 263 1.1 jruoho } 264 1.1 jruoho 265 1.7 jruoho static void 266 1.7 jruoho acpi_wmi_dump(struct acpi_wmi_softc *sc) 267 1.7 jruoho { 268 1.7 jruoho struct wmi_t *wmi; 269 1.7 jruoho 270 1.7 jruoho KASSERT(SIMPLEQ_EMPTY(&sc->wmi_head) == 0); 271 1.7 jruoho 272 1.7 jruoho SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { 273 1.7 jruoho 274 1.7 jruoho aprint_debug_dev(sc->sc_dev, "{%08X-%04X-%04X-", 275 1.7 jruoho wmi->guid.data1, wmi->guid.data2, wmi->guid.data3); 276 1.7 jruoho 277 1.7 jruoho aprint_debug("%02X%02X-%02X%02X%02X%02X%02X%02X} ", 278 1.7 jruoho wmi->guid.data4[0], wmi->guid.data4[1], 279 1.7 jruoho wmi->guid.data4[2], wmi->guid.data4[3], 280 1.7 jruoho wmi->guid.data4[4], wmi->guid.data4[5], 281 1.7 jruoho wmi->guid.data4[6], wmi->guid.data4[7]); 282 1.7 jruoho 283 1.7 jruoho aprint_debug("oid %04X count %02X flags %02X\n", 284 1.7 jruoho UGET16(wmi->guid.oid), wmi->guid.count, wmi->guid.flags); 285 1.7 jruoho } 286 1.7 jruoho } 287 1.7 jruoho 288 1.11 jruoho static void 289 1.11 jruoho acpi_wmi_init_ec(struct acpi_wmi_softc *sc) 290 1.11 jruoho { 291 1.11 jruoho ACPI_STATUS rv; 292 1.11 jruoho deviter_t i; 293 1.11 jruoho device_t d; 294 1.11 jruoho 295 1.11 jruoho d = deviter_first(&i, DEVITER_F_ROOT_FIRST); 296 1.11 jruoho 297 1.11 jruoho for (; d != NULL; d = deviter_next(&i)) { 298 1.11 jruoho 299 1.11 jruoho if (device_is_a(d, "acpiec") != false || 300 1.11 jruoho device_is_a(d, "acpiecdt") != false) { 301 1.11 jruoho sc->sc_ecdev = d; 302 1.11 jruoho break; 303 1.11 jruoho } 304 1.11 jruoho } 305 1.11 jruoho 306 1.11 jruoho deviter_release(&i); 307 1.11 jruoho 308 1.11 jruoho if (sc->sc_ecdev == NULL) 309 1.11 jruoho return; 310 1.11 jruoho 311 1.11 jruoho rv = AcpiInstallAddressSpaceHandler(sc->sc_node->ad_handle, 312 1.11 jruoho ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler, NULL, sc); 313 1.11 jruoho 314 1.11 jruoho if (ACPI_FAILURE(rv)) 315 1.11 jruoho sc->sc_ecdev = NULL; 316 1.11 jruoho } 317 1.11 jruoho 318 1.1 jruoho static ACPI_STATUS 319 1.1 jruoho acpi_wmi_guid_get(struct acpi_wmi_softc *sc, 320 1.1 jruoho const char *src, struct wmi_t **out) 321 1.1 jruoho { 322 1.1 jruoho struct wmi_t *wmi; 323 1.23 riastrad struct guid_t guid; 324 1.23 riastrad char bin[16]; 325 1.16 bouyer char hex[3]; 326 1.1 jruoho const char *ptr; 327 1.1 jruoho uint8_t i; 328 1.1 jruoho 329 1.1 jruoho if (sc == NULL || src == NULL || strlen(src) != 36) 330 1.1 jruoho return AE_BAD_PARAMETER; 331 1.1 jruoho 332 1.1 jruoho for (ptr = src, i = 0; i < 16; i++) { 333 1.1 jruoho 334 1.1 jruoho if (*ptr == '-') 335 1.1 jruoho ptr++; 336 1.1 jruoho 337 1.1 jruoho (void)memcpy(hex, ptr, 2); 338 1.16 bouyer hex[2] = '\0'; 339 1.1 jruoho 340 1.2 jruoho if (HEXCHAR(hex[0]) == 0 || HEXCHAR(hex[1]) == 0) 341 1.1 jruoho return AE_BAD_HEX_CONSTANT; 342 1.1 jruoho 343 1.1 jruoho bin[i] = strtoul(hex, NULL, 16) & 0xFF; 344 1.1 jruoho 345 1.1 jruoho ptr++; 346 1.1 jruoho ptr++; 347 1.1 jruoho } 348 1.1 jruoho 349 1.23 riastrad guid.data1 = be32dec(&bin[0]); 350 1.23 riastrad guid.data2 = be16dec(&bin[4]); 351 1.23 riastrad guid.data3 = be16dec(&bin[6]); 352 1.23 riastrad memcpy(guid.data4, &bin[8], 8); 353 1.1 jruoho 354 1.1 jruoho SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { 355 1.1 jruoho 356 1.23 riastrad if (GUIDCMP(&guid, &wmi->guid) != 0) { 357 1.1 jruoho 358 1.1 jruoho if (out != NULL) 359 1.1 jruoho *out = wmi; 360 1.1 jruoho 361 1.1 jruoho return AE_OK; 362 1.1 jruoho } 363 1.1 jruoho } 364 1.1 jruoho 365 1.1 jruoho return AE_NOT_FOUND; 366 1.1 jruoho } 367 1.1 jruoho 368 1.1 jruoho /* 369 1.1 jruoho * Checks if a GUID is present. Child devices 370 1.1 jruoho * can use this in their autoconf(9) routines. 371 1.1 jruoho */ 372 1.1 jruoho int 373 1.1 jruoho acpi_wmi_guid_match(device_t self, const char *guid) 374 1.1 jruoho { 375 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 376 1.1 jruoho ACPI_STATUS rv; 377 1.1 jruoho 378 1.1 jruoho rv = acpi_wmi_guid_get(sc, guid, NULL); 379 1.1 jruoho 380 1.1 jruoho if (ACPI_SUCCESS(rv)) 381 1.1 jruoho return 1; 382 1.1 jruoho 383 1.1 jruoho return 0; 384 1.1 jruoho } 385 1.1 jruoho 386 1.1 jruoho /* 387 1.1 jruoho * Adds internal event handler. 388 1.1 jruoho */ 389 1.4 jruoho static void 390 1.1 jruoho acpi_wmi_event_add(struct acpi_wmi_softc *sc) 391 1.1 jruoho { 392 1.1 jruoho struct wmi_t *wmi; 393 1.1 jruoho ACPI_STATUS rv; 394 1.1 jruoho 395 1.4 jruoho if (acpi_register_notify(sc->sc_node, acpi_wmi_event_handler) != true) 396 1.4 jruoho return; 397 1.1 jruoho 398 1.6 jruoho /* 399 1.13 jakllsch * Enable possible events, expensive or otherwise. 400 1.6 jruoho */ 401 1.1 jruoho SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { 402 1.1 jruoho 403 1.13 jakllsch if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0) { 404 1.1 jruoho 405 1.13 jakllsch rv = acpi_wmi_enable_event(sc->sc_node->ad_handle, 406 1.13 jakllsch wmi->guid.nid, true); 407 1.1 jruoho 408 1.1 jruoho if (ACPI_SUCCESS(rv)) { 409 1.1 jruoho wmi->eevent = true; 410 1.1 jruoho continue; 411 1.1 jruoho } 412 1.1 jruoho 413 1.6 jruoho aprint_debug_dev(sc->sc_dev, "failed to enable " 414 1.1 jruoho "expensive WExx: %s\n", AcpiFormatException(rv)); 415 1.1 jruoho } 416 1.1 jruoho } 417 1.1 jruoho } 418 1.1 jruoho 419 1.1 jruoho /* 420 1.1 jruoho * Removes the internal event handler. 421 1.1 jruoho */ 422 1.4 jruoho static void 423 1.1 jruoho acpi_wmi_event_del(struct acpi_wmi_softc *sc) 424 1.1 jruoho { 425 1.1 jruoho struct wmi_t *wmi; 426 1.1 jruoho ACPI_STATUS rv; 427 1.1 jruoho 428 1.4 jruoho acpi_deregister_notify(sc->sc_node); 429 1.1 jruoho 430 1.1 jruoho SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { 431 1.1 jruoho 432 1.1 jruoho if (wmi->eevent != true) 433 1.1 jruoho continue; 434 1.1 jruoho 435 1.1 jruoho KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0); 436 1.1 jruoho 437 1.13 jakllsch rv = acpi_wmi_enable_event(sc->sc_node->ad_handle, 438 1.13 jakllsch wmi->guid.nid, false); 439 1.1 jruoho 440 1.1 jruoho if (ACPI_SUCCESS(rv)) { 441 1.1 jruoho wmi->eevent = false; 442 1.1 jruoho continue; 443 1.1 jruoho } 444 1.1 jruoho 445 1.6 jruoho aprint_debug_dev(sc->sc_dev, "failed to disable " 446 1.1 jruoho "expensive WExx: %s\n", AcpiFormatException(rv)); 447 1.1 jruoho } 448 1.1 jruoho } 449 1.1 jruoho 450 1.1 jruoho /* 451 1.1 jruoho * Returns extra information possibly associated with an event. 452 1.1 jruoho */ 453 1.1 jruoho ACPI_STATUS 454 1.1 jruoho acpi_wmi_event_get(device_t self, uint32_t event, ACPI_BUFFER *obuf) 455 1.1 jruoho { 456 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 457 1.1 jruoho struct wmi_t *wmi; 458 1.1 jruoho ACPI_OBJECT_LIST arg; 459 1.1 jruoho ACPI_OBJECT obj; 460 1.2 jruoho ACPI_HANDLE hdl; 461 1.2 jruoho 462 1.1 jruoho if (sc == NULL || obuf == NULL) 463 1.1 jruoho return AE_BAD_PARAMETER; 464 1.1 jruoho 465 1.1 jruoho if (sc->sc_handler == NULL) 466 1.1 jruoho return AE_ABORT_METHOD; 467 1.1 jruoho 468 1.3 jruoho hdl = sc->sc_node->ad_handle; 469 1.3 jruoho 470 1.1 jruoho obj.Type = ACPI_TYPE_INTEGER; 471 1.1 jruoho obj.Integer.Value = event; 472 1.1 jruoho 473 1.1 jruoho arg.Count = 0x01; 474 1.1 jruoho arg.Pointer = &obj; 475 1.1 jruoho 476 1.1 jruoho obuf->Pointer = NULL; 477 1.1 jruoho obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; 478 1.1 jruoho 479 1.1 jruoho SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { 480 1.1 jruoho 481 1.1 jruoho if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) == 0) 482 1.1 jruoho continue; 483 1.1 jruoho 484 1.1 jruoho if (wmi->guid.nid != event) 485 1.1 jruoho continue; 486 1.1 jruoho 487 1.2 jruoho return AcpiEvaluateObject(hdl, "_WED", &arg, obuf); 488 1.1 jruoho } 489 1.1 jruoho 490 1.1 jruoho return AE_NOT_FOUND; 491 1.1 jruoho } 492 1.1 jruoho 493 1.1 jruoho /* 494 1.1 jruoho * Forwards events to the external handler through the internal one. 495 1.1 jruoho */ 496 1.1 jruoho static void 497 1.1 jruoho acpi_wmi_event_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 498 1.1 jruoho { 499 1.4 jruoho struct acpi_wmi_softc *sc; 500 1.4 jruoho device_t self = aux; 501 1.4 jruoho 502 1.4 jruoho sc = device_private(self); 503 1.1 jruoho 504 1.1 jruoho if (sc->sc_child == NULL) 505 1.1 jruoho return; 506 1.1 jruoho 507 1.1 jruoho if (sc->sc_handler == NULL) 508 1.1 jruoho return; 509 1.1 jruoho 510 1.1 jruoho (*sc->sc_handler)(NULL, evt, sc->sc_child); 511 1.1 jruoho } 512 1.1 jruoho 513 1.1 jruoho ACPI_STATUS 514 1.1 jruoho acpi_wmi_event_register(device_t self, ACPI_NOTIFY_HANDLER handler) 515 1.1 jruoho { 516 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 517 1.1 jruoho 518 1.1 jruoho if (sc == NULL) 519 1.1 jruoho return AE_BAD_PARAMETER; 520 1.1 jruoho 521 1.1 jruoho if (handler != NULL && sc->sc_handler != NULL) 522 1.1 jruoho return AE_ALREADY_EXISTS; 523 1.1 jruoho 524 1.1 jruoho sc->sc_handler = handler; 525 1.1 jruoho 526 1.1 jruoho return AE_OK; 527 1.1 jruoho } 528 1.1 jruoho 529 1.1 jruoho ACPI_STATUS 530 1.1 jruoho acpi_wmi_event_deregister(device_t self) 531 1.1 jruoho { 532 1.1 jruoho return acpi_wmi_event_register(self, NULL); 533 1.1 jruoho } 534 1.1 jruoho 535 1.1 jruoho /* 536 1.11 jruoho * Handler for EC regions, which may be embedded in WMI. 537 1.11 jruoho */ 538 1.11 jruoho static ACPI_STATUS 539 1.11 jruoho acpi_wmi_ec_handler(uint32_t func, ACPI_PHYSICAL_ADDRESS addr, 540 1.11 jruoho uint32_t width, ACPI_INTEGER *val, void *setup, void *aux) 541 1.11 jruoho { 542 1.11 jruoho struct acpi_wmi_softc *sc = aux; 543 1.11 jruoho 544 1.11 jruoho if (aux == NULL || val == NULL) 545 1.11 jruoho return AE_BAD_PARAMETER; 546 1.11 jruoho 547 1.11 jruoho if (addr > 0xFF || width % 8 != 0) 548 1.11 jruoho return AE_BAD_ADDRESS; 549 1.11 jruoho 550 1.11 jruoho switch (func) { 551 1.11 jruoho 552 1.11 jruoho case ACPI_READ: 553 1.11 jruoho (void)acpiec_bus_read(sc->sc_ecdev, addr, val, width); 554 1.11 jruoho break; 555 1.11 jruoho 556 1.11 jruoho case ACPI_WRITE: 557 1.11 jruoho (void)acpiec_bus_write(sc->sc_ecdev, addr, *val, width); 558 1.11 jruoho break; 559 1.11 jruoho 560 1.11 jruoho default: 561 1.11 jruoho return AE_BAD_PARAMETER; 562 1.11 jruoho } 563 1.11 jruoho 564 1.11 jruoho return AE_OK; 565 1.11 jruoho } 566 1.11 jruoho 567 1.11 jruoho /* 568 1.1 jruoho * As there is no prior knowledge about the expensive 569 1.1 jruoho * events that cause "significant overhead", try to 570 1.1 jruoho * disable (enable) these before suspending (resuming). 571 1.1 jruoho */ 572 1.1 jruoho static bool 573 1.1 jruoho acpi_wmi_suspend(device_t self, const pmf_qual_t *qual) 574 1.1 jruoho { 575 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 576 1.1 jruoho 577 1.1 jruoho acpi_wmi_event_del(sc); 578 1.1 jruoho 579 1.1 jruoho return true; 580 1.1 jruoho } 581 1.1 jruoho 582 1.1 jruoho static bool 583 1.1 jruoho acpi_wmi_resume(device_t self, const pmf_qual_t *qual) 584 1.1 jruoho { 585 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 586 1.1 jruoho 587 1.1 jruoho acpi_wmi_event_add(sc); 588 1.1 jruoho 589 1.1 jruoho return true; 590 1.1 jruoho } 591 1.1 jruoho 592 1.1 jruoho static ACPI_STATUS 593 1.13 jakllsch acpi_wmi_enable_event(ACPI_HANDLE hdl, uint8_t nid, bool flag) 594 1.1 jruoho { 595 1.1 jruoho char path[5]; 596 1.1 jruoho 597 1.13 jakllsch snprintf(path, sizeof(path), "WE%02X", nid); 598 1.13 jakllsch 599 1.13 jakllsch return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00); 600 1.13 jakllsch } 601 1.13 jakllsch 602 1.13 jakllsch static ACPI_STATUS 603 1.13 jakllsch acpi_wmi_enable_collection(ACPI_HANDLE hdl, const char *oid, bool flag) 604 1.13 jakllsch { 605 1.13 jakllsch char path[5]; 606 1.1 jruoho 607 1.13 jakllsch strlcpy(path, "WC", sizeof(path)); 608 1.13 jakllsch strlcat(path, oid, sizeof(path)); 609 1.1 jruoho 610 1.1 jruoho return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00); 611 1.1 jruoho } 612 1.1 jruoho 613 1.1 jruoho static bool 614 1.1 jruoho acpi_wmi_input(struct wmi_t *wmi, uint8_t flag, uint8_t idx) 615 1.1 jruoho { 616 1.16 bouyer /* A data block may have no flags at all */ 617 1.16 bouyer if ((wmi->guid.flags & flag) == 0 && 618 1.16 bouyer (flag == ACPI_WMI_FLAG_DATA && 619 1.16 bouyer (wmi->guid.flags & ~ACPI_WMI_FLAG_EXPENSIVE) != 0)) 620 1.1 jruoho return false; 621 1.1 jruoho 622 1.1 jruoho if (wmi->guid.count == 0x00) 623 1.1 jruoho return false; 624 1.1 jruoho 625 1.1 jruoho if (wmi->guid.count < idx) 626 1.1 jruoho return false; 627 1.1 jruoho 628 1.1 jruoho return true; 629 1.1 jruoho } 630 1.1 jruoho 631 1.1 jruoho /* 632 1.1 jruoho * Makes a WMI data block query (WQxx). The corresponding control 633 1.1 jruoho * method for data collection will be invoked if it is available. 634 1.1 jruoho */ 635 1.1 jruoho ACPI_STATUS 636 1.1 jruoho acpi_wmi_data_query(device_t self, const char *guid, 637 1.1 jruoho uint8_t idx, ACPI_BUFFER *obuf) 638 1.1 jruoho { 639 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 640 1.1 jruoho struct wmi_t *wmi; 641 1.1 jruoho char path[5] = "WQ"; 642 1.1 jruoho ACPI_OBJECT_LIST arg; 643 1.1 jruoho ACPI_STATUS rv, rvxx; 644 1.1 jruoho ACPI_OBJECT obj; 645 1.1 jruoho 646 1.1 jruoho rvxx = AE_SUPPORT; 647 1.1 jruoho 648 1.1 jruoho if (obuf == NULL) 649 1.1 jruoho return AE_BAD_PARAMETER; 650 1.1 jruoho 651 1.1 jruoho rv = acpi_wmi_guid_get(sc, guid, &wmi); 652 1.1 jruoho 653 1.1 jruoho if (ACPI_FAILURE(rv)) 654 1.1 jruoho return rv; 655 1.1 jruoho 656 1.1 jruoho if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true) 657 1.1 jruoho return AE_BAD_DATA; 658 1.1 jruoho 659 1.1 jruoho (void)strlcat(path, wmi->guid.oid, sizeof(path)); 660 1.1 jruoho 661 1.1 jruoho obj.Type = ACPI_TYPE_INTEGER; 662 1.1 jruoho obj.Integer.Value = idx; 663 1.1 jruoho 664 1.1 jruoho arg.Count = 0x01; 665 1.1 jruoho arg.Pointer = &obj; 666 1.1 jruoho 667 1.1 jruoho obuf->Pointer = NULL; 668 1.1 jruoho obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; 669 1.1 jruoho 670 1.1 jruoho /* 671 1.1 jruoho * If the expensive flag is set, we should enable 672 1.1 jruoho * data collection before evaluating the WQxx buffer. 673 1.1 jruoho */ 674 1.1 jruoho if ((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) { 675 1.1 jruoho 676 1.13 jakllsch rvxx = acpi_wmi_enable_collection(sc->sc_node->ad_handle, 677 1.13 jakllsch wmi->guid.oid, true); 678 1.1 jruoho } 679 1.1 jruoho 680 1.1 jruoho rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf); 681 1.1 jruoho 682 1.1 jruoho /* No longer needed. */ 683 1.1 jruoho if (ACPI_SUCCESS(rvxx)) { 684 1.1 jruoho 685 1.13 jakllsch (void)acpi_wmi_enable_collection(sc->sc_node->ad_handle, 686 1.13 jakllsch wmi->guid.oid, false); 687 1.1 jruoho } 688 1.1 jruoho 689 1.1 jruoho #ifdef DIAGNOSTIC 690 1.1 jruoho /* 691 1.1 jruoho * XXX: It appears that quite a few laptops have WQxx 692 1.1 jruoho * methods that are declared as expensive, but lack the 693 1.1 jruoho * corresponding WCxx control method. 694 1.1 jruoho * 695 1.1 jruoho * -- Acer Aspire One is one example <jruohonen (at) iki.fi>. 696 1.1 jruoho */ 697 1.1 jruoho if (ACPI_FAILURE(rvxx) && rvxx != AE_SUPPORT) 698 1.1 jruoho aprint_error_dev(sc->sc_dev, "failed to evaluate WCxx " 699 1.1 jruoho "for %s: %s\n", path, AcpiFormatException(rvxx)); 700 1.1 jruoho #endif 701 1.1 jruoho return rv; 702 1.1 jruoho } 703 1.1 jruoho 704 1.1 jruoho /* 705 1.1 jruoho * Writes to a data block (WSxx). 706 1.1 jruoho */ 707 1.1 jruoho ACPI_STATUS 708 1.1 jruoho acpi_wmi_data_write(device_t self, const char *guid, 709 1.1 jruoho uint8_t idx, ACPI_BUFFER *ibuf) 710 1.1 jruoho { 711 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 712 1.1 jruoho struct wmi_t *wmi; 713 1.1 jruoho ACPI_OBJECT_LIST arg; 714 1.1 jruoho ACPI_OBJECT obj[2]; 715 1.1 jruoho char path[5] = "WS"; 716 1.1 jruoho ACPI_STATUS rv; 717 1.1 jruoho 718 1.1 jruoho if (ibuf == NULL) 719 1.1 jruoho return AE_BAD_PARAMETER; 720 1.1 jruoho 721 1.1 jruoho rv = acpi_wmi_guid_get(sc, guid, &wmi); 722 1.1 jruoho 723 1.1 jruoho if (ACPI_FAILURE(rv)) 724 1.1 jruoho return rv; 725 1.1 jruoho 726 1.1 jruoho if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true) 727 1.1 jruoho return AE_BAD_DATA; 728 1.1 jruoho 729 1.1 jruoho (void)strlcat(path, wmi->guid.oid, sizeof(path)); 730 1.1 jruoho 731 1.1 jruoho obj[0].Integer.Value = idx; 732 1.1 jruoho obj[0].Type = ACPI_TYPE_INTEGER; 733 1.1 jruoho 734 1.1 jruoho obj[1].Buffer.Length = ibuf->Length; 735 1.1 jruoho obj[1].Buffer.Pointer = ibuf->Pointer; 736 1.1 jruoho 737 1.1 jruoho obj[1].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ? 738 1.1 jruoho ACPI_TYPE_STRING : ACPI_TYPE_BUFFER; 739 1.1 jruoho 740 1.1 jruoho arg.Count = 0x02; 741 1.1 jruoho arg.Pointer = obj; 742 1.1 jruoho 743 1.1 jruoho return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, NULL); 744 1.1 jruoho } 745 1.1 jruoho 746 1.1 jruoho /* 747 1.1 jruoho * Executes a method (WMxx). 748 1.1 jruoho */ 749 1.1 jruoho ACPI_STATUS 750 1.1 jruoho acpi_wmi_method(device_t self, const char *guid, uint8_t idx, 751 1.1 jruoho uint32_t mid, ACPI_BUFFER *ibuf, ACPI_BUFFER *obuf) 752 1.1 jruoho { 753 1.1 jruoho struct acpi_wmi_softc *sc = device_private(self); 754 1.1 jruoho struct wmi_t *wmi; 755 1.1 jruoho ACPI_OBJECT_LIST arg; 756 1.1 jruoho ACPI_OBJECT obj[3]; 757 1.1 jruoho char path[5] = "WM"; 758 1.1 jruoho ACPI_STATUS rv; 759 1.1 jruoho 760 1.1 jruoho if (ibuf == NULL || obuf == NULL) 761 1.1 jruoho return AE_BAD_PARAMETER; 762 1.1 jruoho 763 1.1 jruoho rv = acpi_wmi_guid_get(sc, guid, &wmi); 764 1.1 jruoho 765 1.1 jruoho if (ACPI_FAILURE(rv)) 766 1.1 jruoho return rv; 767 1.1 jruoho 768 1.1 jruoho if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_METHOD, idx) != true) 769 1.1 jruoho return AE_BAD_DATA; 770 1.1 jruoho 771 1.1 jruoho (void)strlcat(path, wmi->guid.oid, sizeof(path)); 772 1.1 jruoho 773 1.1 jruoho obj[0].Integer.Value = idx; 774 1.1 jruoho obj[1].Integer.Value = mid; 775 1.1 jruoho obj[0].Type = obj[1].Type = ACPI_TYPE_INTEGER; 776 1.1 jruoho 777 1.1 jruoho obj[2].Buffer.Length = ibuf->Length; 778 1.1 jruoho obj[2].Buffer.Pointer = ibuf->Pointer; 779 1.1 jruoho 780 1.1 jruoho obj[2].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ? 781 1.1 jruoho ACPI_TYPE_STRING : ACPI_TYPE_BUFFER; 782 1.1 jruoho 783 1.1 jruoho arg.Count = 0x03; 784 1.1 jruoho arg.Pointer = obj; 785 1.1 jruoho 786 1.1 jruoho obuf->Pointer = NULL; 787 1.1 jruoho obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; 788 1.1 jruoho 789 1.1 jruoho return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf); 790 1.1 jruoho } 791 1.9 jmcneill 792 1.9 jmcneill MODULE(MODULE_CLASS_DRIVER, acpiwmi, NULL); 793 1.9 jmcneill 794 1.12 jruoho #ifdef _MODULE 795 1.12 jruoho #include "ioconf.c" 796 1.12 jruoho #endif 797 1.9 jmcneill 798 1.9 jmcneill static int 799 1.12 jruoho acpiwmi_modcmd(modcmd_t cmd, void *aux) 800 1.9 jmcneill { 801 1.12 jruoho int rv = 0; 802 1.9 jmcneill 803 1.9 jmcneill switch (cmd) { 804 1.9 jmcneill 805 1.9 jmcneill case MODULE_CMD_INIT: 806 1.9 jmcneill 807 1.12 jruoho #ifdef _MODULE 808 1.12 jruoho rv = config_init_component(cfdriver_ioconf_acpiwmi, 809 1.12 jruoho cfattach_ioconf_acpiwmi, cfdata_ioconf_acpiwmi); 810 1.12 jruoho #endif 811 1.12 jruoho break; 812 1.9 jmcneill 813 1.9 jmcneill case MODULE_CMD_FINI: 814 1.9 jmcneill 815 1.12 jruoho #ifdef _MODULE 816 1.12 jruoho rv = config_fini_component(cfdriver_ioconf_acpiwmi, 817 1.12 jruoho cfattach_ioconf_acpiwmi, cfdata_ioconf_acpiwmi); 818 1.12 jruoho #endif 819 1.12 jruoho break; 820 1.9 jmcneill 821 1.9 jmcneill default: 822 1.12 jruoho rv = ENOTTY; 823 1.9 jmcneill } 824 1.12 jruoho 825 1.12 jruoho return rv; 826 1.9 jmcneill } 827