wmi_msi.c revision 1.2 1 /* $NetBSD: wmi_msi.c,v 1.2 2010/10/24 16:25:31 jmcneill 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.2 2010/10/24 16:25:31 jmcneill 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
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
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
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
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
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
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 #ifdef _MODULE
198
199 MODULE(MODULE_CLASS_DRIVER, wmimsi, NULL);
200 CFDRIVER_DECL(wmimsi, DV_DULL, NULL);
201
202 static int wmimsiloc[] = { -1 };
203 extern struct cfattach wmimsi_ca;
204
205 static struct cfparent wmiparent = {
206 "acpiwmibus", NULL, DVUNIT_ANY
207 };
208
209 static struct cfdata wmimsi_cfdata[] = {
210 {
211 .cf_name = "wmimsi",
212 .cf_atname = "wmimsi",
213 .cf_unit = 0,
214 .cf_fstate = FSTATE_STAR,
215 .cf_loc = wmimsiloc,
216 .cf_flags = 0,
217 .cf_pspec = &wmiparent,
218 },
219
220 { NULL }
221 };
222
223 static int
224 wmimsi_modcmd(modcmd_t cmd, void *opaque)
225 {
226 int err;
227
228 switch (cmd) {
229
230 case MODULE_CMD_INIT:
231
232 err = config_cfdriver_attach(&wmimsi_cd);
233
234 if (err != 0)
235 return err;
236
237 err = config_cfattach_attach("wmimsi", &wmimsi_ca);
238
239 if (err != 0) {
240 config_cfdriver_detach(&wmimsi_cd);
241 return err;
242 }
243
244 err = config_cfdata_attach(wmimsi_cfdata, 1);
245
246 if (err != 0) {
247 config_cfattach_detach("wmimsi", &wmimsi_ca);
248 config_cfdriver_detach(&wmimsi_cd);
249 return err;
250 }
251
252 return 0;
253
254 case MODULE_CMD_FINI:
255
256 err = config_cfdata_detach(wmimsi_cfdata);
257
258 if (err != 0)
259 return err;
260
261 config_cfattach_detach("wmimsi", &wmimsi_ca);
262 config_cfdriver_detach(&wmimsi_cd);
263
264 return 0;
265
266 default:
267 return ENOTTY;
268 }
269 }
270
271 #endif /* _MODULE */
272