1 1.12 bouyer /* $NetBSD: wmi_dell.c,v 1.12 2019/12/04 19:51:32 bouyer Exp $ */ 2 1.1 jruoho 3 1.1 jruoho /*- 4 1.1 jruoho * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. 5 1.1 jruoho * All rights reserved. 6 1.1 jruoho * 7 1.1 jruoho * This code is derived from software contributed to The NetBSD Foundation 8 1.1 jruoho * by Jukka Ruohonen. 9 1.1 jruoho * 10 1.1 jruoho * Redistribution and use in source and binary forms, with or without 11 1.1 jruoho * modification, are permitted provided that the following conditions 12 1.1 jruoho * are met: 13 1.1 jruoho * 14 1.1 jruoho * 1. Redistributions of source code must retain the above copyright 15 1.1 jruoho * notice, this list of conditions and the following disclaimer. 16 1.1 jruoho * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 jruoho * notice, this list of conditions and the following disclaimer in the 18 1.1 jruoho * documentation and/or other materials provided with the distribution. 19 1.1 jruoho * 20 1.1 jruoho * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 1.1 jruoho * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 1.1 jruoho * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 1.1 jruoho * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 1.1 jruoho * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 1.1 jruoho * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 1.1 jruoho * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 1.1 jruoho * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 1.1 jruoho * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 1.1 jruoho * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 1.1 jruoho * SUCH DAMAGE. 31 1.1 jruoho */ 32 1.1 jruoho 33 1.1 jruoho #include <sys/cdefs.h> 34 1.12 bouyer __KERNEL_RCSID(0, "$NetBSD: wmi_dell.c,v 1.12 2019/12/04 19:51:32 bouyer Exp $"); 35 1.1 jruoho 36 1.1 jruoho #include <sys/param.h> 37 1.1 jruoho #include <sys/device.h> 38 1.5 jmcneill #include <sys/module.h> 39 1.1 jruoho 40 1.1 jruoho #include <dev/acpi/acpireg.h> 41 1.1 jruoho #include <dev/acpi/acpivar.h> 42 1.3 jruoho #include <dev/acpi/wmi/wmi_acpivar.h> 43 1.1 jruoho 44 1.1 jruoho #include <dev/sysmon/sysmonvar.h> 45 1.1 jruoho 46 1.10 bouyer #ifdef WMI_DEBUG 47 1.10 bouyer #define DPRINTF(x) printf x 48 1.10 bouyer #else 49 1.10 bouyer #define DPRINTF(x) 50 1.10 bouyer #endif 51 1.10 bouyer 52 1.1 jruoho #define _COMPONENT ACPI_RESOURCE_COMPONENT 53 1.1 jruoho ACPI_MODULE_NAME ("wmi_dell") 54 1.1 jruoho 55 1.1 jruoho #define WMI_DELL_PSW_DISPLAY_CYCLE 0 56 1.12 bouyer #define WMI_DELL_PSW_RADIO_TOGGLE 1 57 1.12 bouyer #define WMI_DELL_PSW_COUNT 2 58 1.1 jruoho 59 1.1 jruoho #define WMI_DELL_GUID_EVENT "9DBB5994-A997-11DA-B012-B622A1EF5492" 60 1.10 bouyer #define WMI_DELL_GUID_DESC "8D9DDCBC-A997-11DA-B012-B622A1EF5492" 61 1.1 jruoho 62 1.1 jruoho struct wmi_dell_softc { 63 1.1 jruoho device_t sc_dev; 64 1.1 jruoho device_t sc_parent; 65 1.10 bouyer int sc_version; 66 1.1 jruoho struct sysmon_pswitch sc_smpsw[WMI_DELL_PSW_COUNT]; 67 1.1 jruoho bool sc_smpsw_valid; 68 1.1 jruoho }; 69 1.1 jruoho 70 1.10 bouyer #define WMI_DELLA_PMF 0x0 71 1.10 bouyer #define WMI_DELLA_PSW 0x1 72 1.10 bouyer #define WMI_DELLA_IGN 0x2 73 1.10 bouyer 74 1.10 bouyer const struct wmi_dell_actions { 75 1.10 bouyer u_int wda_action; 76 1.10 bouyer u_int wda_type; 77 1.10 bouyer u_int wda_subtype; 78 1.10 bouyer u_int wda_data; 79 1.10 bouyer } wmi_dell_actions[] = { 80 1.10 bouyer /* type 0 */ 81 1.10 bouyer /* brightness control */ 82 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe005, PMFE_DISPLAY_BRIGHTNESS_DOWN}, 83 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe006, PMFE_DISPLAY_BRIGHTNESS_UP}, 84 1.10 bouyer {WMI_DELLA_PSW, 0x0000, 0xe00b, WMI_DELL_PSW_DISPLAY_CYCLE}, 85 1.10 bouyer 86 1.12 bouyer {WMI_DELLA_PSW, 0x0000, 0xe008, WMI_DELL_PSW_RADIO_TOGGLE}, 87 1.10 bouyer {WMI_DELLA_IGN, 0x0000, 0xe00c, 0}, /* keyboard illumination */ 88 1.10 bouyer 89 1.10 bouyer /* volume control */ 90 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe020, PMFE_AUDIO_VOLUME_TOGGLE}, 91 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe02e, PMFE_AUDIO_VOLUME_DOWN}, 92 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe030, PMFE_AUDIO_VOLUME_UP}, 93 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe0f8, PMFE_AUDIO_VOLUME_DOWN}, 94 1.10 bouyer {WMI_DELLA_PMF, 0x0000, 0xe0f9, PMFE_AUDIO_VOLUME_UP}, 95 1.10 bouyer 96 1.10 bouyer 97 1.10 bouyer /* type 0x10 */ 98 1.10 bouyer {WMI_DELLA_PMF, 0x0010, 0x0057, PMFE_DISPLAY_BRIGHTNESS_DOWN}, 99 1.10 bouyer {WMI_DELLA_PMF, 0x0010, 0x0058, PMFE_DISPLAY_BRIGHTNESS_UP}, 100 1.12 bouyer {WMI_DELLA_IGN, 0x0010, 0x0150, 0}, /* XXX microphone toggle */ 101 1.10 bouyer {WMI_DELLA_IGN, 0x0010, 0x0151, 0}, /* Fn-lock */ 102 1.10 bouyer {WMI_DELLA_IGN, 0x0010, 0x0152, 0}, /* keyboard illumination */ 103 1.12 bouyer {WMI_DELLA_PSW, 0x0010, 0x0153, WMI_DELL_PSW_RADIO_TOGGLE}, 104 1.10 bouyer {WMI_DELLA_IGN, 0x0010, 0x0155, 0}, /* Stealth mode toggle */ 105 1.12 bouyer {WMI_DELLA_PSW, 0x0010, 0xE008, WMI_DELL_PSW_RADIO_TOGGLE}, 106 1.10 bouyer {WMI_DELLA_IGN, 0x0010, 0xE035, 0}, /* Fn-lock */ 107 1.10 bouyer 108 1.10 bouyer /* type 0x11 */ 109 1.12 bouyer {WMI_DELLA_IGN, 0x0011, 0x02eb, 0}, /* keyboard illumination */ 110 1.10 bouyer }; 111 1.10 bouyer 112 1.1 jruoho static int wmi_dell_match(device_t, cfdata_t, void *); 113 1.1 jruoho static void wmi_dell_attach(device_t, device_t, void *); 114 1.1 jruoho static int wmi_dell_detach(device_t, int); 115 1.1 jruoho static void wmi_dell_notify_handler(ACPI_HANDLE, uint32_t, void *); 116 1.1 jruoho static bool wmi_dell_suspend(device_t, const pmf_qual_t *); 117 1.1 jruoho static bool wmi_dell_resume(device_t, const pmf_qual_t *); 118 1.1 jruoho 119 1.1 jruoho CFATTACH_DECL_NEW(wmidell, sizeof(struct wmi_dell_softc), 120 1.1 jruoho wmi_dell_match, wmi_dell_attach, wmi_dell_detach, NULL); 121 1.1 jruoho 122 1.1 jruoho static int 123 1.1 jruoho wmi_dell_match(device_t parent, cfdata_t match, void *aux) 124 1.1 jruoho { 125 1.1 jruoho return acpi_wmi_guid_match(parent, WMI_DELL_GUID_EVENT); 126 1.1 jruoho } 127 1.1 jruoho 128 1.1 jruoho static void 129 1.1 jruoho wmi_dell_attach(device_t parent, device_t self, void *aux) 130 1.1 jruoho { 131 1.1 jruoho struct wmi_dell_softc *sc = device_private(self); 132 1.1 jruoho ACPI_STATUS rv; 133 1.10 bouyer ACPI_BUFFER obuf; 134 1.10 bouyer ACPI_OBJECT *obj; 135 1.10 bouyer uint32_t *data; 136 1.1 jruoho int e; 137 1.1 jruoho 138 1.1 jruoho sc->sc_dev = self; 139 1.1 jruoho sc->sc_parent = parent; 140 1.1 jruoho sc->sc_smpsw_valid = true; 141 1.1 jruoho 142 1.1 jruoho rv = acpi_wmi_event_register(parent, wmi_dell_notify_handler); 143 1.1 jruoho 144 1.1 jruoho if (ACPI_FAILURE(rv)) { 145 1.1 jruoho aprint_error(": failed to install WMI notify handler\n"); 146 1.1 jruoho return; 147 1.1 jruoho } 148 1.1 jruoho 149 1.10 bouyer memset(&obuf, 0, sizeof(obuf)); 150 1.10 bouyer rv = acpi_wmi_data_query(parent, WMI_DELL_GUID_DESC, 0, &obuf); 151 1.10 bouyer if (ACPI_FAILURE(rv)) { 152 1.10 bouyer aprint_error(": failed to query WMI descriptor: %s\n", 153 1.10 bouyer AcpiFormatException(rv)); 154 1.10 bouyer return; 155 1.10 bouyer } 156 1.10 bouyer obj = obuf.Pointer; 157 1.10 bouyer if (obj->Type != ACPI_TYPE_BUFFER) { 158 1.10 bouyer aprint_error(": wrong type %d for WMI descriptor\n", obj->Type); 159 1.10 bouyer return; 160 1.10 bouyer } 161 1.10 bouyer if (obj->Buffer.Length != 128) { 162 1.10 bouyer aprint_error(": wrong len %d for WMI descriptor", 163 1.10 bouyer obj->Buffer.Length); 164 1.10 bouyer if (obj->Buffer.Length < 16) { 165 1.10 bouyer aprint_error("\n"); 166 1.10 bouyer return; 167 1.10 bouyer } 168 1.10 bouyer } 169 1.10 bouyer data = (uint32_t *)obj->Buffer.Pointer; 170 1.11 christos #define WMI_LLED 0x4C4C4544 171 1.11 christos #define WMI_IMWsp 0x494D5720 172 1.11 christos if (data[0] != WMI_LLED || data[1] != WMI_IMWsp) { 173 1.11 christos aprint_error(": wrong WMI descriptor signature %#x %#x", 174 1.10 bouyer data[0], data[1]); 175 1.10 bouyer } 176 1.10 bouyer sc->sc_version = data[2]; 177 1.1 jruoho aprint_naive("\n"); 178 1.10 bouyer aprint_normal(": Dell WMI mappings version %d\n", sc->sc_version); 179 1.1 jruoho 180 1.1 jruoho sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_name = 181 1.1 jruoho PSWITCH_HK_DISPLAY_CYCLE; 182 1.1 jruoho 183 1.1 jruoho sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_type = 184 1.1 jruoho PSWITCH_TYPE_HOTKEY; 185 1.1 jruoho 186 1.1 jruoho e = sysmon_pswitch_register(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE]); 187 1.1 jruoho 188 1.12 bouyer if (e != 0) { 189 1.12 bouyer sc->sc_smpsw_valid = false; 190 1.12 bouyer goto end; 191 1.12 bouyer } 192 1.12 bouyer 193 1.12 bouyer sc->sc_smpsw[WMI_DELL_PSW_RADIO_TOGGLE].smpsw_name = 194 1.12 bouyer PSWITCH_HK_WIRELESS_BUTTON; 195 1.12 bouyer 196 1.12 bouyer sc->sc_smpsw[WMI_DELL_PSW_RADIO_TOGGLE].smpsw_type = 197 1.12 bouyer PSWITCH_TYPE_HOTKEY; 198 1.12 bouyer 199 1.12 bouyer e = sysmon_pswitch_register(&sc->sc_smpsw[WMI_DELL_PSW_RADIO_TOGGLE]); 200 1.12 bouyer 201 1.1 jruoho if (e != 0) 202 1.1 jruoho sc->sc_smpsw_valid = false; 203 1.1 jruoho 204 1.12 bouyer end: 205 1.1 jruoho (void)pmf_device_register(self, wmi_dell_suspend, wmi_dell_resume); 206 1.1 jruoho } 207 1.1 jruoho 208 1.1 jruoho static int 209 1.1 jruoho wmi_dell_detach(device_t self, int flags) 210 1.1 jruoho { 211 1.1 jruoho struct wmi_dell_softc *sc = device_private(self); 212 1.1 jruoho device_t parent = sc->sc_parent; 213 1.6 jruoho size_t i; 214 1.1 jruoho 215 1.1 jruoho (void)pmf_device_deregister(self); 216 1.2 jruoho (void)acpi_wmi_event_deregister(parent); 217 1.1 jruoho 218 1.1 jruoho if (sc->sc_smpsw_valid != true) 219 1.1 jruoho return 0; 220 1.1 jruoho 221 1.1 jruoho for (i = 0; i < __arraycount(sc->sc_smpsw); i++) 222 1.1 jruoho sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 223 1.1 jruoho 224 1.1 jruoho return 0; 225 1.1 jruoho } 226 1.1 jruoho 227 1.1 jruoho static bool 228 1.1 jruoho wmi_dell_suspend(device_t self, const pmf_qual_t *qual) 229 1.1 jruoho { 230 1.1 jruoho struct wmi_dell_softc *sc = device_private(self); 231 1.1 jruoho device_t parent = sc->sc_parent; 232 1.1 jruoho 233 1.2 jruoho (void)acpi_wmi_event_deregister(parent); 234 1.1 jruoho 235 1.1 jruoho return true; 236 1.1 jruoho } 237 1.1 jruoho 238 1.1 jruoho static bool 239 1.1 jruoho wmi_dell_resume(device_t self, const pmf_qual_t *qual) 240 1.1 jruoho { 241 1.1 jruoho struct wmi_dell_softc *sc = device_private(self); 242 1.1 jruoho device_t parent = sc->sc_parent; 243 1.1 jruoho 244 1.1 jruoho (void)acpi_wmi_event_register(parent, wmi_dell_notify_handler); 245 1.1 jruoho 246 1.1 jruoho return true; 247 1.1 jruoho } 248 1.1 jruoho 249 1.1 jruoho static void 250 1.10 bouyer wmi_dell_action(struct wmi_dell_softc *sc, uint16_t *data, int len) 251 1.10 bouyer { 252 1.11 christos size_t i; 253 1.10 bouyer for (i = 0; i < __arraycount(wmi_dell_actions); i++) { 254 1.10 bouyer const struct wmi_dell_actions *wda = &wmi_dell_actions[i]; 255 1.10 bouyer if (wda->wda_type == data[0] && 256 1.10 bouyer wda->wda_subtype == data[1]) { 257 1.10 bouyer switch(wda->wda_action) { 258 1.10 bouyer case WMI_DELLA_IGN: 259 1.10 bouyer DPRINTF((" ignored")); 260 1.10 bouyer return; 261 1.10 bouyer case WMI_DELLA_PMF: 262 1.11 christos DPRINTF((" pmf %d", wda->wda_data)); 263 1.11 christos pmf_event_inject(NULL, wda->wda_data); 264 1.10 bouyer return; 265 1.10 bouyer case WMI_DELLA_PSW: 266 1.11 christos DPRINTF((" psw %d", wda->wda_data)); 267 1.10 bouyer sysmon_pswitch_event( 268 1.10 bouyer &sc->sc_smpsw[wda->wda_data], 269 1.10 bouyer PSWITCH_EVENT_PRESSED); 270 1.10 bouyer return; 271 1.10 bouyer default: 272 1.11 christos aprint_debug_dev(sc->sc_dev, 273 1.11 christos "unknown dell wmi action %d\n", 274 1.10 bouyer wda->wda_action); 275 1.10 bouyer return; 276 1.10 bouyer } 277 1.10 bouyer 278 1.10 bouyer } 279 1.10 bouyer } 280 1.11 christos aprint_debug_dev(sc->sc_dev, "unknown event %#4X %#4X\n", 281 1.10 bouyer data[0], data[1]); 282 1.10 bouyer } 283 1.10 bouyer 284 1.10 bouyer static void 285 1.1 jruoho wmi_dell_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 286 1.1 jruoho { 287 1.1 jruoho struct wmi_dell_softc *sc; 288 1.1 jruoho device_t self = aux; 289 1.1 jruoho ACPI_OBJECT *obj; 290 1.1 jruoho ACPI_BUFFER buf; 291 1.1 jruoho ACPI_STATUS rv; 292 1.10 bouyer uint16_t *data, *end; 293 1.10 bouyer int i, len; 294 1.1 jruoho 295 1.4 jruoho buf.Pointer = NULL; 296 1.4 jruoho 297 1.1 jruoho sc = device_private(self); 298 1.1 jruoho rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 299 1.1 jruoho 300 1.1 jruoho if (ACPI_FAILURE(rv)) 301 1.1 jruoho goto out; 302 1.1 jruoho 303 1.1 jruoho obj = buf.Pointer; 304 1.1 jruoho 305 1.1 jruoho if (obj->Type != ACPI_TYPE_BUFFER) { 306 1.1 jruoho rv = AE_TYPE; 307 1.1 jruoho goto out; 308 1.1 jruoho } 309 1.1 jruoho 310 1.10 bouyer data = (void *)(&obj->Buffer.Pointer[0]); 311 1.10 bouyer end = (void *)(&obj->Buffer.Pointer[obj->Buffer.Length]); 312 1.1 jruoho 313 1.10 bouyer DPRINTF(("wmi_dell_notify_handler buffer len %d\n", 314 1.10 bouyer obj->Buffer.Length)); 315 1.10 bouyer while (data < end) { 316 1.10 bouyer DPRINTF(("wmi_dell_notify_handler len %d", data[0])); 317 1.10 bouyer if (data[0] == 0) { 318 1.10 bouyer DPRINTF(("\n")); 319 1.10 bouyer break; 320 1.10 bouyer } 321 1.10 bouyer len = data[0] + 1; 322 1.1 jruoho 323 1.10 bouyer if (&data[len] >= end) { 324 1.10 bouyer DPRINTF(("\n")); 325 1.1 jruoho break; 326 1.1 jruoho } 327 1.10 bouyer if (len < 2) { 328 1.10 bouyer DPRINTF(("\n")); 329 1.10 bouyer continue; 330 1.10 bouyer } 331 1.10 bouyer for (i = 1; i < len; i++) 332 1.11 christos DPRINTF((" %#04X", data[i])); 333 1.10 bouyer wmi_dell_action(sc, &data[1], len - 1); 334 1.10 bouyer DPRINTF(("\n")); 335 1.10 bouyer data = &data[len]; 336 1.10 bouyer /* 337 1.10 bouyer * WMI interface version 0 don't clear the buffer from previous 338 1.10 bouyer * event, so if the current event is smaller than the previous 339 1.10 bouyer * one there will be garbage after the current event. 340 1.10 bouyer * workaround by processing only the first event 341 1.10 bouyer */ 342 1.10 bouyer if (sc->sc_version == 0) 343 1.10 bouyer break; 344 1.1 jruoho } 345 1.1 jruoho 346 1.1 jruoho out: 347 1.1 jruoho if (buf.Pointer != NULL) 348 1.1 jruoho ACPI_FREE(buf.Pointer); 349 1.1 jruoho 350 1.1 jruoho if (ACPI_FAILURE(rv)) 351 1.1 jruoho aprint_error_dev(sc->sc_dev, "failed to get data for " 352 1.11 christos "event %#02X: %s\n", evt, AcpiFormatException(rv)); 353 1.1 jruoho } 354 1.5 jmcneill 355 1.9 pgoyette MODULE(MODULE_CLASS_DRIVER, wmidell, "acpiwmi,sysmon_power"); 356 1.5 jmcneill 357 1.7 jruoho #ifdef _MODULE 358 1.7 jruoho #include "ioconf.c" 359 1.7 jruoho #endif 360 1.5 jmcneill 361 1.5 jmcneill static int 362 1.7 jruoho wmidell_modcmd(modcmd_t cmd, void *aux) 363 1.5 jmcneill { 364 1.7 jruoho int rv = 0; 365 1.5 jmcneill 366 1.5 jmcneill switch (cmd) { 367 1.5 jmcneill case MODULE_CMD_INIT: 368 1.7 jruoho #ifdef _MODULE 369 1.7 jruoho rv = config_init_component(cfdriver_ioconf_wmidell, 370 1.7 jruoho cfattach_ioconf_wmidell, cfdata_ioconf_wmidell); 371 1.7 jruoho #endif 372 1.7 jruoho break; 373 1.5 jmcneill 374 1.5 jmcneill case MODULE_CMD_FINI: 375 1.7 jruoho #ifdef _MODULE 376 1.7 jruoho rv = config_fini_component(cfdriver_ioconf_wmidell, 377 1.7 jruoho cfattach_ioconf_wmidell, cfdata_ioconf_wmidell); 378 1.7 jruoho #endif 379 1.7 jruoho break; 380 1.5 jmcneill 381 1.5 jmcneill default: 382 1.7 jruoho rv = ENOTTY; 383 1.5 jmcneill } 384 1.7 jruoho 385 1.7 jruoho return rv; 386 1.5 jmcneill } 387