1 /* $NetBSD: wmi_dell.c,v 1.6 2010/10/25 07:53:22 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010 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_dell.c,v 1.6 2010/10/25 07:53:22 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_dell") 48 49 #define WMI_DELL_HOTKEY_BRIGHTNESS_DOWN 0xE005 50 #define WMI_DELL_HOTKEY_BRIGHTNESS_UP 0xE006 51 #define WMI_DELL_HOTKEY_DISPLAY_CYCLE 0xE00B 52 #define WMI_DELL_HOTKEY_VOLUME_MUTE 0xE020 53 #define WMI_DELL_HOTKEY_VOLUME_DOWN 0xE02E 54 #define WMI_DELL_HOTKEY_VOLUME_UP 0xE030 55 /* WMI_DELL_HOTKEY_UNKNOWN 0xXXXX */ 56 57 #define WMI_DELL_PSW_DISPLAY_CYCLE 0 58 #define WMI_DELL_PSW_COUNT 1 59 60 #define WMI_DELL_GUID_EVENT "9DBB5994-A997-11DA-B012-B622A1EF5492" 61 62 struct wmi_dell_softc { 63 device_t sc_dev; 64 device_t sc_parent; 65 struct sysmon_pswitch sc_smpsw[WMI_DELL_PSW_COUNT]; 66 bool sc_smpsw_valid; 67 }; 68 69 static int wmi_dell_match(device_t, cfdata_t, void *); 70 static void wmi_dell_attach(device_t, device_t, void *); 71 static int wmi_dell_detach(device_t, int); 72 static void wmi_dell_notify_handler(ACPI_HANDLE, uint32_t, void *); 73 static bool wmi_dell_suspend(device_t, const pmf_qual_t *); 74 static bool wmi_dell_resume(device_t, const pmf_qual_t *); 75 76 CFATTACH_DECL_NEW(wmidell, sizeof(struct wmi_dell_softc), 77 wmi_dell_match, wmi_dell_attach, wmi_dell_detach, NULL); 78 79 static int 80 wmi_dell_match(device_t parent, cfdata_t match, void *aux) 81 { 82 return acpi_wmi_guid_match(parent, WMI_DELL_GUID_EVENT); 83 } 84 85 static void 86 wmi_dell_attach(device_t parent, device_t self, void *aux) 87 { 88 struct wmi_dell_softc *sc = device_private(self); 89 ACPI_STATUS rv; 90 int e; 91 92 sc->sc_dev = self; 93 sc->sc_parent = parent; 94 sc->sc_smpsw_valid = true; 95 96 rv = acpi_wmi_event_register(parent, wmi_dell_notify_handler); 97 98 if (ACPI_FAILURE(rv)) { 99 aprint_error(": failed to install WMI notify handler\n"); 100 return; 101 } 102 103 aprint_naive("\n"); 104 aprint_normal(": Dell WMI mappings\n"); 105 106 sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_name = 107 PSWITCH_HK_DISPLAY_CYCLE; 108 109 sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_type = 110 PSWITCH_TYPE_HOTKEY; 111 112 e = sysmon_pswitch_register(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE]); 113 114 if (e != 0) 115 sc->sc_smpsw_valid = false; 116 117 (void)pmf_device_register(self, wmi_dell_suspend, wmi_dell_resume); 118 } 119 120 static int 121 wmi_dell_detach(device_t self, int flags) 122 { 123 struct wmi_dell_softc *sc = device_private(self); 124 device_t parent = sc->sc_parent; 125 size_t i; 126 127 (void)pmf_device_deregister(self); 128 (void)acpi_wmi_event_deregister(parent); 129 130 if (sc->sc_smpsw_valid != true) 131 return 0; 132 133 for (i = 0; i < __arraycount(sc->sc_smpsw); i++) 134 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 135 136 return 0; 137 } 138 139 static bool 140 wmi_dell_suspend(device_t self, const pmf_qual_t *qual) 141 { 142 struct wmi_dell_softc *sc = device_private(self); 143 device_t parent = sc->sc_parent; 144 145 (void)acpi_wmi_event_deregister(parent); 146 147 return true; 148 } 149 150 static bool 151 wmi_dell_resume(device_t self, const pmf_qual_t *qual) 152 { 153 struct wmi_dell_softc *sc = device_private(self); 154 device_t parent = sc->sc_parent; 155 156 (void)acpi_wmi_event_register(parent, wmi_dell_notify_handler); 157 158 return true; 159 } 160 161 static void 162 wmi_dell_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 163 { 164 struct wmi_dell_softc *sc; 165 device_t self = aux; 166 ACPI_OBJECT *obj; 167 ACPI_BUFFER buf; 168 ACPI_STATUS rv; 169 uint32_t val; 170 171 buf.Pointer = NULL; 172 173 sc = device_private(self); 174 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 175 176 if (ACPI_FAILURE(rv)) 177 goto out; 178 179 obj = buf.Pointer; 180 181 if (obj->Type != ACPI_TYPE_BUFFER) { 182 rv = AE_TYPE; 183 goto out; 184 } 185 186 val = obj->Buffer.Pointer[1] & 0xFFFF; 187 188 switch (val) { 189 190 case WMI_DELL_HOTKEY_BRIGHTNESS_DOWN: 191 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 192 break; 193 194 case WMI_DELL_HOTKEY_BRIGHTNESS_UP: 195 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 196 break; 197 198 case WMI_DELL_HOTKEY_DISPLAY_CYCLE: 199 200 if (sc->sc_smpsw_valid != true) { 201 rv = AE_ABORT_METHOD; 202 break; 203 } 204 205 sysmon_pswitch_event(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE], 206 PSWITCH_EVENT_PRESSED); 207 break; 208 209 case WMI_DELL_HOTKEY_VOLUME_MUTE: 210 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE); 211 break; 212 213 case WMI_DELL_HOTKEY_VOLUME_DOWN: 214 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN); 215 break; 216 217 case WMI_DELL_HOTKEY_VOLUME_UP: 218 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP); 219 break; 220 221 default: 222 aprint_debug_dev(sc->sc_dev, 223 "unknown key 0x%02X for event 0x%02X\n", val, evt); 224 break; 225 } 226 227 out: 228 if (buf.Pointer != NULL) 229 ACPI_FREE(buf.Pointer); 230 231 if (ACPI_FAILURE(rv)) 232 aprint_error_dev(sc->sc_dev, "failed to get data for " 233 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 234 } 235 236 #ifdef _MODULE 237 238 MODULE(MODULE_CLASS_DRIVER, wmidell, NULL); 239 CFDRIVER_DECL(wmidell, DV_DULL, NULL); 240 241 static int wmidellloc[] = { -1 }; 242 extern struct cfattach wmidell_ca; 243 244 static struct cfparent wmiparent = { 245 "acpiwmibus", NULL, DVUNIT_ANY 246 }; 247 248 static struct cfdata wmidell_cfdata[] = { 249 { 250 .cf_name = "wmidell", 251 .cf_atname = "wmidell", 252 .cf_unit = 0, 253 .cf_fstate = FSTATE_STAR, 254 .cf_loc = wmidellloc, 255 .cf_flags = 0, 256 .cf_pspec = &wmiparent, 257 }, 258 259 { NULL, NULL, 0, 0, NULL, 0, NULL } 260 }; 261 262 static int 263 wmidell_modcmd(modcmd_t cmd, void *opaque) 264 { 265 int err; 266 267 switch (cmd) { 268 269 case MODULE_CMD_INIT: 270 271 err = config_cfdriver_attach(&wmidell_cd); 272 273 if (err != 0) 274 return err; 275 276 err = config_cfattach_attach("wmidell", &wmidell_ca); 277 278 if (err != 0) { 279 config_cfdriver_detach(&wmidell_cd); 280 return err; 281 } 282 283 err = config_cfdata_attach(wmidell_cfdata, 1); 284 285 if (err != 0) { 286 config_cfattach_detach("wmidell", &wmidell_ca); 287 config_cfdriver_detach(&wmidell_cd); 288 return err; 289 } 290 291 return 0; 292 293 case MODULE_CMD_FINI: 294 295 err = config_cfdata_detach(wmidell_cfdata); 296 297 if (err != 0) 298 return err; 299 300 config_cfattach_detach("wmidell", &wmidell_ca); 301 config_cfdriver_detach(&wmidell_cd); 302 303 return 0; 304 305 default: 306 return ENOTTY; 307 } 308 } 309 310 #endif /* _MODULE */ 311