1*3993e0d1Sjruoho /* $NetBSD: wmi_msi.c,v 1.5 2011/02/16 13:15:49 jruoho Exp $ */
2117351d1Sjruoho
3117351d1Sjruoho /*-
4117351d1Sjruoho * Copyright (c) 2010 The NetBSD Foundation, Inc.
5117351d1Sjruoho * All rights reserved.
6117351d1Sjruoho *
7117351d1Sjruoho * This code is derived from software contributed to The NetBSD Foundation
8117351d1Sjruoho * by Jukka Ruohonen.
9117351d1Sjruoho *
10117351d1Sjruoho * Redistribution and use in source and binary forms, with or without
11117351d1Sjruoho * modification, are permitted provided that the following conditions
12117351d1Sjruoho * are met:
13117351d1Sjruoho *
14117351d1Sjruoho * 1. Redistributions of source code must retain the above copyright
15117351d1Sjruoho * notice, this list of conditions and the following disclaimer.
16117351d1Sjruoho * 2. Redistributions in binary form must reproduce the above copyright
17117351d1Sjruoho * notice, this list of conditions and the following disclaimer in the
18117351d1Sjruoho * documentation and/or other materials provided with the distribution.
19117351d1Sjruoho *
20117351d1Sjruoho * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21117351d1Sjruoho * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22117351d1Sjruoho * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23117351d1Sjruoho * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24117351d1Sjruoho * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25117351d1Sjruoho * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26117351d1Sjruoho * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27117351d1Sjruoho * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28117351d1Sjruoho * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29117351d1Sjruoho * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30117351d1Sjruoho * SUCH DAMAGE.
31117351d1Sjruoho */
32117351d1Sjruoho
33117351d1Sjruoho #include <sys/cdefs.h>
34*3993e0d1Sjruoho __KERNEL_RCSID(0, "$NetBSD: wmi_msi.c,v 1.5 2011/02/16 13:15:49 jruoho Exp $");
35117351d1Sjruoho
36117351d1Sjruoho #include <sys/param.h>
37117351d1Sjruoho #include <sys/device.h>
384e449995Sjmcneill #include <sys/module.h>
39117351d1Sjruoho
40117351d1Sjruoho #include <dev/acpi/acpireg.h>
41117351d1Sjruoho #include <dev/acpi/acpivar.h>
42117351d1Sjruoho #include <dev/acpi/wmi/wmi_acpivar.h>
43117351d1Sjruoho
44117351d1Sjruoho #define _COMPONENT ACPI_RESOURCE_COMPONENT
45117351d1Sjruoho ACPI_MODULE_NAME ("wmi_msi")
46117351d1Sjruoho
47117351d1Sjruoho #define WMI_MSI_HOTKEY_BRIGHTNESS_UP 0xD0
48117351d1Sjruoho #define WMI_MSI_HOTKEY_BRIGHTNESS_DOWN 0xD1
49117351d1Sjruoho #define WMI_MSI_HOTKEY_VOLUME_UP 0xD2
50117351d1Sjruoho #define WMI_MSI_HOTKEY_VOLUME_DOWN 0xD3
51117351d1Sjruoho /* WMI_MSI_HOTKEY_UNKNOWN 0xXXXX */
52117351d1Sjruoho
53117351d1Sjruoho #define WMI_MSI_GUID_EVENT "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
54117351d1Sjruoho
55117351d1Sjruoho struct wmi_msi_softc {
56117351d1Sjruoho device_t sc_dev;
57117351d1Sjruoho device_t sc_parent;
58117351d1Sjruoho };
59117351d1Sjruoho
60117351d1Sjruoho static int wmi_msi_match(device_t, cfdata_t, void *);
61117351d1Sjruoho static void wmi_msi_attach(device_t, device_t, void *);
62117351d1Sjruoho static int wmi_msi_detach(device_t, int);
63117351d1Sjruoho static void wmi_msi_notify_handler(ACPI_HANDLE, uint32_t, void *);
64117351d1Sjruoho static bool wmi_msi_suspend(device_t, const pmf_qual_t *);
65117351d1Sjruoho static bool wmi_msi_resume(device_t, const pmf_qual_t *);
66117351d1Sjruoho
67117351d1Sjruoho CFATTACH_DECL_NEW(wmimsi, sizeof(struct wmi_msi_softc),
68117351d1Sjruoho wmi_msi_match, wmi_msi_attach, wmi_msi_detach, NULL);
69117351d1Sjruoho
70117351d1Sjruoho static int
wmi_msi_match(device_t parent,cfdata_t match,void * aux)71117351d1Sjruoho wmi_msi_match(device_t parent, cfdata_t match, void *aux)
72117351d1Sjruoho {
73117351d1Sjruoho return acpi_wmi_guid_match(parent, WMI_MSI_GUID_EVENT);
74117351d1Sjruoho }
75117351d1Sjruoho
76117351d1Sjruoho static void
wmi_msi_attach(device_t parent,device_t self,void * aux)77117351d1Sjruoho wmi_msi_attach(device_t parent, device_t self, void *aux)
78117351d1Sjruoho {
79117351d1Sjruoho struct wmi_msi_softc *sc = device_private(self);
80117351d1Sjruoho ACPI_STATUS rv;
81117351d1Sjruoho
82117351d1Sjruoho sc->sc_dev = self;
83117351d1Sjruoho sc->sc_parent = parent;
84117351d1Sjruoho
85117351d1Sjruoho rv = acpi_wmi_event_register(parent, wmi_msi_notify_handler);
86117351d1Sjruoho
87117351d1Sjruoho if (ACPI_FAILURE(rv)) {
88117351d1Sjruoho aprint_error(": failed to install WMI notify handler\n");
89117351d1Sjruoho return;
90117351d1Sjruoho }
91117351d1Sjruoho
92117351d1Sjruoho aprint_naive("\n");
93117351d1Sjruoho aprint_normal(": MSI WMI mappings\n");
94117351d1Sjruoho
95117351d1Sjruoho (void)pmf_device_register(self, wmi_msi_suspend, wmi_msi_resume);
96117351d1Sjruoho }
97117351d1Sjruoho
98117351d1Sjruoho static int
wmi_msi_detach(device_t self,int flags)99117351d1Sjruoho wmi_msi_detach(device_t self, int flags)
100117351d1Sjruoho {
101117351d1Sjruoho struct wmi_msi_softc *sc = device_private(self);
102117351d1Sjruoho device_t parent = sc->sc_parent;
103117351d1Sjruoho
104117351d1Sjruoho (void)pmf_device_deregister(self);
105117351d1Sjruoho (void)acpi_wmi_event_deregister(parent);
106117351d1Sjruoho
107117351d1Sjruoho return 0;
108117351d1Sjruoho }
109117351d1Sjruoho
110117351d1Sjruoho static bool
wmi_msi_suspend(device_t self,const pmf_qual_t * qual)111117351d1Sjruoho wmi_msi_suspend(device_t self, const pmf_qual_t *qual)
112117351d1Sjruoho {
113117351d1Sjruoho struct wmi_msi_softc *sc = device_private(self);
114117351d1Sjruoho device_t parent = sc->sc_parent;
115117351d1Sjruoho
116117351d1Sjruoho (void)acpi_wmi_event_deregister(parent);
117117351d1Sjruoho
118117351d1Sjruoho return true;
119117351d1Sjruoho }
120117351d1Sjruoho
121117351d1Sjruoho static bool
wmi_msi_resume(device_t self,const pmf_qual_t * qual)122117351d1Sjruoho wmi_msi_resume(device_t self, const pmf_qual_t *qual)
123117351d1Sjruoho {
124117351d1Sjruoho struct wmi_msi_softc *sc = device_private(self);
125117351d1Sjruoho device_t parent = sc->sc_parent;
126117351d1Sjruoho
127117351d1Sjruoho (void)acpi_wmi_event_register(parent, wmi_msi_notify_handler);
128117351d1Sjruoho
129117351d1Sjruoho return true;
130117351d1Sjruoho }
131117351d1Sjruoho
132117351d1Sjruoho static void
wmi_msi_notify_handler(ACPI_HANDLE hdl,uint32_t evt,void * aux)133117351d1Sjruoho wmi_msi_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
134117351d1Sjruoho {
135117351d1Sjruoho struct wmi_msi_softc *sc;
136117351d1Sjruoho device_t self = aux;
137117351d1Sjruoho ACPI_OBJECT *obj;
138117351d1Sjruoho ACPI_BUFFER buf;
139117351d1Sjruoho ACPI_STATUS rv;
140117351d1Sjruoho uint32_t val;
141117351d1Sjruoho
142117351d1Sjruoho buf.Pointer = NULL;
143117351d1Sjruoho
144117351d1Sjruoho sc = device_private(self);
145117351d1Sjruoho rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf);
146117351d1Sjruoho
147117351d1Sjruoho if (ACPI_FAILURE(rv))
148117351d1Sjruoho goto out;
149117351d1Sjruoho
150117351d1Sjruoho obj = buf.Pointer;
151117351d1Sjruoho
152117351d1Sjruoho if (obj->Type != ACPI_TYPE_INTEGER) {
153117351d1Sjruoho rv = AE_TYPE;
154117351d1Sjruoho goto out;
155117351d1Sjruoho }
156117351d1Sjruoho
157117351d1Sjruoho if (obj->Integer.Value > UINT32_MAX) {
158117351d1Sjruoho rv = AE_AML_NUMERIC_OVERFLOW;
159117351d1Sjruoho goto out;
160117351d1Sjruoho }
161117351d1Sjruoho
162117351d1Sjruoho val = obj->Integer.Value;
163117351d1Sjruoho
164117351d1Sjruoho switch (val) {
165117351d1Sjruoho
166117351d1Sjruoho case WMI_MSI_HOTKEY_BRIGHTNESS_DOWN:
167117351d1Sjruoho pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
168117351d1Sjruoho break;
169117351d1Sjruoho
170117351d1Sjruoho case WMI_MSI_HOTKEY_BRIGHTNESS_UP:
171117351d1Sjruoho pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
172117351d1Sjruoho break;
173117351d1Sjruoho
174117351d1Sjruoho case WMI_MSI_HOTKEY_VOLUME_DOWN:
175117351d1Sjruoho pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN);
176117351d1Sjruoho break;
177117351d1Sjruoho
178117351d1Sjruoho case WMI_MSI_HOTKEY_VOLUME_UP:
179117351d1Sjruoho pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP);
180117351d1Sjruoho break;
181117351d1Sjruoho
182117351d1Sjruoho default:
183117351d1Sjruoho aprint_normal_dev(sc->sc_dev,
184117351d1Sjruoho "unknown key 0x%02X for event 0x%02X\n", val, evt);
185117351d1Sjruoho break;
186117351d1Sjruoho }
187117351d1Sjruoho
188117351d1Sjruoho out:
189117351d1Sjruoho if (buf.Pointer != NULL)
190117351d1Sjruoho ACPI_FREE(buf.Pointer);
191117351d1Sjruoho
192117351d1Sjruoho if (ACPI_FAILURE(rv))
193117351d1Sjruoho aprint_error_dev(sc->sc_dev, "failed to get data for "
194117351d1Sjruoho "event 0x%02X: %s\n", evt, AcpiFormatException(rv));
195117351d1Sjruoho }
1964e449995Sjmcneill
197*3993e0d1Sjruoho MODULE(MODULE_CLASS_DRIVER, wmimsi, "acpiwmi");
1984e449995Sjmcneill
199b9f301d5Sjruoho #ifdef _MODULE
200b9f301d5Sjruoho #include "ioconf.c"
201b9f301d5Sjruoho #endif
2024e449995Sjmcneill
2034e449995Sjmcneill static int
wmimsi_modcmd(modcmd_t cmd,void * aux)204b9f301d5Sjruoho wmimsi_modcmd(modcmd_t cmd, void *aux)
2054e449995Sjmcneill {
206b9f301d5Sjruoho int rv = 0;
2074e449995Sjmcneill
2084e449995Sjmcneill switch (cmd) {
2094e449995Sjmcneill
2104e449995Sjmcneill case MODULE_CMD_INIT:
2114e449995Sjmcneill
212b9f301d5Sjruoho #ifdef _MODULE
213b9f301d5Sjruoho rv = config_init_component(cfdriver_ioconf_wmimsi,
214b9f301d5Sjruoho cfattach_ioconf_wmimsi, cfdata_ioconf_wmimsi);
215b9f301d5Sjruoho #endif
216b9f301d5Sjruoho break;
2174e449995Sjmcneill
2184e449995Sjmcneill case MODULE_CMD_FINI:
2194e449995Sjmcneill
220b9f301d5Sjruoho #ifdef _MODULE
221b9f301d5Sjruoho rv = config_fini_component(cfdriver_ioconf_wmimsi,
222b9f301d5Sjruoho cfattach_ioconf_wmimsi, cfdata_ioconf_wmimsi);
223b9f301d5Sjruoho #endif
224b9f301d5Sjruoho break;
2254e449995Sjmcneill
2264e449995Sjmcneill default:
227b9f301d5Sjruoho rv = ENOTTY;
2284e449995Sjmcneill }
2294e449995Sjmcneill
230b9f301d5Sjruoho return rv;
231b9f301d5Sjruoho }
232