1 /* $NetBSD: wmi_eeepc.c,v 1.2 2011/02/16 13:15:49 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jukka Ruohonen. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: wmi_eeepc.c,v 1.2 2011/02/16 13:15:49 jruoho Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/device.h> 38 #include <sys/module.h> 39 40 #include <dev/acpi/acpireg.h> 41 #include <dev/acpi/acpivar.h> 42 #include <dev/acpi/wmi/wmi_acpivar.h> 43 44 #include <dev/sysmon/sysmonvar.h> 45 46 #define _COMPONENT ACPI_RESOURCE_COMPONENT 47 ACPI_MODULE_NAME ("wmi_eeepc") 48 49 #define WMI_EEEPC_HK_VOLUME_UP 0x30 50 #define WMI_EEEPC_HK_VOLUME_DOWN 0x31 51 #define WMI_EEEPC_HK_VOLUME_MUTE 0x32 52 #define WMI_EEEPC_HK_DISPLAY_CYCLE 0xCC 53 #define WMI_EEEPC_HK_DISPLAY_OFF 0xE9 54 /* WMI_EEEPC_HK_UNKNOWN 0xXX */ 55 56 #define WMI_EEEPC_PSW_DISPLAY_CYCLE 0 57 #define WMI_EEEPC_PSW_COUNT 1 58 59 #define WMI_EEEPC_GUID_EVENT "ABBC0F72-8EA1-11D1-00A0-C90629100000" 60 61 struct wmi_eeepc_softc { 62 device_t sc_dev; 63 device_t sc_parent; 64 struct sysmon_pswitch sc_smpsw[WMI_EEEPC_PSW_COUNT]; 65 bool sc_smpsw_valid; 66 }; 67 68 static int wmi_eeepc_match(device_t, cfdata_t, void *); 69 static void wmi_eeepc_attach(device_t, device_t, void *); 70 static int wmi_eeepc_detach(device_t, int); 71 static void wmi_eeepc_notify_handler(ACPI_HANDLE, uint32_t, void *); 72 static bool wmi_eeepc_suspend(device_t, const pmf_qual_t *); 73 static bool wmi_eeepc_resume(device_t, const pmf_qual_t *); 74 75 CFATTACH_DECL_NEW(wmieeepc, sizeof(struct wmi_eeepc_softc), 76 wmi_eeepc_match, wmi_eeepc_attach, wmi_eeepc_detach, NULL); 77 78 static int 79 wmi_eeepc_match(device_t parent, cfdata_t match, void *aux) 80 { 81 return acpi_wmi_guid_match(parent, WMI_EEEPC_GUID_EVENT); 82 } 83 84 static void 85 wmi_eeepc_attach(device_t parent, device_t self, void *aux) 86 { 87 static const int dc = WMI_EEEPC_PSW_DISPLAY_CYCLE; 88 struct wmi_eeepc_softc *sc = device_private(self); 89 ACPI_STATUS rv; 90 91 sc->sc_dev = self; 92 sc->sc_parent = parent; 93 sc->sc_smpsw_valid = false; 94 95 rv = acpi_wmi_event_register(parent, wmi_eeepc_notify_handler); 96 97 if (ACPI_FAILURE(rv)) { 98 aprint_error(": failed to install WMI notify handler\n"); 99 return; 100 } 101 102 aprint_naive("\n"); 103 aprint_normal(": Asus Eee PC WMI mappings\n"); 104 105 sc->sc_smpsw[dc].smpsw_type = PSWITCH_TYPE_HOTKEY; 106 sc->sc_smpsw[dc].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 107 108 if (sysmon_pswitch_register(&sc->sc_smpsw[dc]) == 0) 109 sc->sc_smpsw_valid = true; 110 111 (void)pmf_device_register(self, wmi_eeepc_suspend, wmi_eeepc_resume); 112 } 113 114 static int 115 wmi_eeepc_detach(device_t self, int flags) 116 { 117 struct wmi_eeepc_softc *sc = device_private(self); 118 device_t parent = sc->sc_parent; 119 120 (void)pmf_device_deregister(self); 121 (void)acpi_wmi_event_deregister(parent); 122 123 return 0; 124 } 125 126 static bool 127 wmi_eeepc_suspend(device_t self, const pmf_qual_t *qual) 128 { 129 struct wmi_eeepc_softc *sc = device_private(self); 130 device_t parent = sc->sc_parent; 131 132 (void)acpi_wmi_event_deregister(parent); 133 134 return true; 135 } 136 137 static bool 138 wmi_eeepc_resume(device_t self, const pmf_qual_t *qual) 139 { 140 struct wmi_eeepc_softc *sc = device_private(self); 141 device_t parent = sc->sc_parent; 142 143 (void)acpi_wmi_event_register(parent, wmi_eeepc_notify_handler); 144 145 return true; 146 } 147 148 static void 149 wmi_eeepc_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 150 { 151 static const int dc = WMI_EEEPC_PSW_DISPLAY_CYCLE; 152 struct wmi_eeepc_softc *sc; 153 device_t self = aux; 154 ACPI_OBJECT *obj; 155 ACPI_BUFFER buf; 156 ACPI_STATUS rv; 157 uint32_t val; 158 159 buf.Pointer = NULL; 160 161 sc = device_private(self); 162 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 163 164 if (ACPI_FAILURE(rv)) 165 goto out; 166 167 obj = buf.Pointer; 168 169 if (obj->Type != ACPI_TYPE_INTEGER) { 170 rv = AE_TYPE; 171 goto out; 172 } 173 174 if (obj->Integer.Value > UINT32_MAX) { 175 rv = AE_LIMIT; 176 goto out; 177 } 178 179 val = obj->Integer.Value; 180 181 switch (val) { 182 183 case WMI_EEEPC_HK_VOLUME_UP: 184 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP); 185 break; 186 187 case WMI_EEEPC_HK_VOLUME_DOWN: 188 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN); 189 break; 190 191 case WMI_EEEPC_HK_VOLUME_MUTE: 192 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE); 193 break; 194 195 case WMI_EEEPC_HK_DISPLAY_CYCLE: 196 197 if (sc->sc_smpsw_valid != true) { 198 rv = AE_ABORT_METHOD; 199 break; 200 } 201 202 sysmon_pswitch_event(&sc->sc_smpsw[dc], PSWITCH_EVENT_PRESSED); 203 break; 204 205 case WMI_EEEPC_HK_DISPLAY_OFF: 206 break; 207 208 default: 209 aprint_debug_dev(sc->sc_dev, 210 "unknown key 0x%02X for event 0x%02X\n", val, evt); 211 break; 212 } 213 214 out: 215 if (buf.Pointer != NULL) 216 ACPI_FREE(buf.Pointer); 217 218 if (ACPI_FAILURE(rv)) 219 aprint_error_dev(sc->sc_dev, "failed to get data for " 220 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 221 } 222 223 MODULE(MODULE_CLASS_DRIVER, wmieeepc, "acpiwmi"); 224 225 #ifdef _MODULE 226 #include "ioconf.c" 227 #endif 228 229 static int 230 wmieeepc_modcmd(modcmd_t cmd, void *aux) 231 { 232 int rv = 0; 233 234 switch (cmd) { 235 236 case MODULE_CMD_INIT: 237 238 #ifdef _MODULE 239 rv = config_init_component(cfdriver_ioconf_wmieeepc, 240 cfattach_ioconf_wmieeepc, cfdata_ioconf_wmieeepc); 241 #endif 242 break; 243 244 case MODULE_CMD_FINI: 245 246 #ifdef _MODULE 247 rv = config_fini_component(cfdriver_ioconf_wmieeepc, 248 cfattach_ioconf_wmieeepc, cfdata_ioconf_wmieeepc); 249 #endif 250 break; 251 252 default: 253 rv = ENOTTY; 254 } 255 256 return rv; 257 } 258