1/* $NetBSD: wmi_msi.c,v 1.4 2011/02/16 08:19:56 jruoho Exp $ */ 2 3/*- 4 * Copyright (c) 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_msi.c,v 1.4 2011/02/16 08:19:56 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#define _COMPONENT ACPI_RESOURCE_COMPONENT 45ACPI_MODULE_NAME ("wmi_msi") 46 47#define WMI_MSI_HOTKEY_BRIGHTNESS_UP 0xD0 48#define WMI_MSI_HOTKEY_BRIGHTNESS_DOWN 0xD1 49#define WMI_MSI_HOTKEY_VOLUME_UP 0xD2 50#define WMI_MSI_HOTKEY_VOLUME_DOWN 0xD3 51/* WMI_MSI_HOTKEY_UNKNOWN 0xXXXX */ 52 53#define WMI_MSI_GUID_EVENT "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" 54 55struct wmi_msi_softc { 56 device_t sc_dev; 57 device_t sc_parent; 58}; 59 60static int wmi_msi_match(device_t, cfdata_t, void *); 61static void wmi_msi_attach(device_t, device_t, void *); 62static int wmi_msi_detach(device_t, int); 63static void wmi_msi_notify_handler(ACPI_HANDLE, uint32_t, void *); 64static bool wmi_msi_suspend(device_t, const pmf_qual_t *); 65static bool wmi_msi_resume(device_t, const pmf_qual_t *); 66 67CFATTACH_DECL_NEW(wmimsi, sizeof(struct wmi_msi_softc), 68 wmi_msi_match, wmi_msi_attach, wmi_msi_detach, NULL); 69 70static int 71wmi_msi_match(device_t parent, cfdata_t match, void *aux) 72{ 73 return acpi_wmi_guid_match(parent, WMI_MSI_GUID_EVENT); 74} 75 76static void 77wmi_msi_attach(device_t parent, device_t self, void *aux) 78{ 79 struct wmi_msi_softc *sc = device_private(self); 80 ACPI_STATUS rv; 81 82 sc->sc_dev = self; 83 sc->sc_parent = parent; 84 85 rv = acpi_wmi_event_register(parent, wmi_msi_notify_handler); 86 87 if (ACPI_FAILURE(rv)) { 88 aprint_error(": failed to install WMI notify handler\n"); 89 return; 90 } 91 92 aprint_naive("\n"); 93 aprint_normal(": MSI WMI mappings\n"); 94 95 (void)pmf_device_register(self, wmi_msi_suspend, wmi_msi_resume); 96} 97 98static int 99wmi_msi_detach(device_t self, int flags) 100{ 101 struct wmi_msi_softc *sc = device_private(self); 102 device_t parent = sc->sc_parent; 103 104 (void)pmf_device_deregister(self); 105 (void)acpi_wmi_event_deregister(parent); 106 107 return 0; 108} 109 110static bool 111wmi_msi_suspend(device_t self, const pmf_qual_t *qual) 112{ 113 struct wmi_msi_softc *sc = device_private(self); 114 device_t parent = sc->sc_parent; 115 116 (void)acpi_wmi_event_deregister(parent); 117 118 return true; 119} 120 121static bool 122wmi_msi_resume(device_t self, const pmf_qual_t *qual) 123{ 124 struct wmi_msi_softc *sc = device_private(self); 125 device_t parent = sc->sc_parent; 126 127 (void)acpi_wmi_event_register(parent, wmi_msi_notify_handler); 128 129 return true; 130} 131 132static void 133wmi_msi_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 134{ 135 struct wmi_msi_softc *sc; 136 device_t self = aux; 137 ACPI_OBJECT *obj; 138 ACPI_BUFFER buf; 139 ACPI_STATUS rv; 140 uint32_t val; 141 142 buf.Pointer = NULL; 143 144 sc = device_private(self); 145 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 146 147 if (ACPI_FAILURE(rv)) 148 goto out; 149 150 obj = buf.Pointer; 151 152 if (obj->Type != ACPI_TYPE_INTEGER) { 153 rv = AE_TYPE; 154 goto out; 155 } 156 157 if (obj->Integer.Value > UINT32_MAX) { 158 rv = AE_AML_NUMERIC_OVERFLOW; 159 goto out; 160 } 161 162 val = obj->Integer.Value; 163 164 switch (val) { 165 166 case WMI_MSI_HOTKEY_BRIGHTNESS_DOWN: 167 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 168 break; 169 170 case WMI_MSI_HOTKEY_BRIGHTNESS_UP: 171 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 172 break; 173 174 case WMI_MSI_HOTKEY_VOLUME_DOWN: 175 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN); 176 break; 177 178 case WMI_MSI_HOTKEY_VOLUME_UP: 179 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP); 180 break; 181 182 default: 183 aprint_normal_dev(sc->sc_dev, 184 "unknown key 0x%02X for event 0x%02X\n", val, evt); 185 break; 186 } 187 188out: 189 if (buf.Pointer != NULL) 190 ACPI_FREE(buf.Pointer); 191 192 if (ACPI_FAILURE(rv)) 193 aprint_error_dev(sc->sc_dev, "failed to get data for " 194 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 195} 196 197MODULE(MODULE_CLASS_DRIVER, wmimsi, "acpiwmi"); 198 199#ifdef _MODULE 200#include "ioconf.c" 201#endif 202 203static int 204wmimsi_modcmd(modcmd_t cmd, void *aux) 205{ 206 int rv = 0; 207 208 switch (cmd) { 209 210 case MODULE_CMD_INIT: 211 212#ifdef _MODULE 213 rv = config_init_component(cfdriver_ioconf_wmimsi, 214 cfattach_ioconf_wmimsi, cfdata_ioconf_wmimsi); 215#endif 216 break; 217 218 case MODULE_CMD_FINI: 219 220#ifdef _MODULE 221 rv = config_fini_component(cfdriver_ioconf_wmimsi, 222 cfattach_ioconf_wmimsi, cfdata_ioconf_wmimsi); 223#endif 224 break; 225 226 default: 227 rv = ENOTTY; 228 } 229 230 return rv; 231} 232