xref: /netbsd/sys/dev/acpi/wmi/wmi_msi.c (revision 631959b8)
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