1 /* $NetBSD: wmi_msi.c,v 1.5 2011/02/16 13:15:49 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.5 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 #define _COMPONENT ACPI_RESOURCE_COMPONENT
45 ACPI_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
55 struct wmi_msi_softc {
56 device_t sc_dev;
57 device_t sc_parent;
58 };
59
60 static int wmi_msi_match(device_t, cfdata_t, void *);
61 static void wmi_msi_attach(device_t, device_t, void *);
62 static int wmi_msi_detach(device_t, int);
63 static void wmi_msi_notify_handler(ACPI_HANDLE, uint32_t, void *);
64 static bool wmi_msi_suspend(device_t, const pmf_qual_t *);
65 static bool wmi_msi_resume(device_t, const pmf_qual_t *);
66
67 CFATTACH_DECL_NEW(wmimsi, sizeof(struct wmi_msi_softc),
68 wmi_msi_match, wmi_msi_attach, wmi_msi_detach, NULL);
69
70 static int
wmi_msi_match(device_t parent,cfdata_t match,void * aux)71 wmi_msi_match(device_t parent, cfdata_t match, void *aux)
72 {
73 return acpi_wmi_guid_match(parent, WMI_MSI_GUID_EVENT);
74 }
75
76 static void
wmi_msi_attach(device_t parent,device_t self,void * aux)77 wmi_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
98 static int
wmi_msi_detach(device_t self,int flags)99 wmi_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
110 static bool
wmi_msi_suspend(device_t self,const pmf_qual_t * qual)111 wmi_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
121 static bool
wmi_msi_resume(device_t self,const pmf_qual_t * qual)122 wmi_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
132 static void
wmi_msi_notify_handler(ACPI_HANDLE hdl,uint32_t evt,void * aux)133 wmi_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
188 out:
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
197 MODULE(MODULE_CLASS_DRIVER, wmimsi, "acpiwmi");
198
199 #ifdef _MODULE
200 #include "ioconf.c"
201 #endif
202
203 static int
wmimsi_modcmd(modcmd_t cmd,void * aux)204 wmimsi_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