xref: /netbsd/sys/dev/acpi/wmi/wmi_dell.c (revision 6550d01e)
1 /*	$NetBSD: wmi_dell.c,v 1.6 2010/10/25 07:53:22 jruoho Exp $ */
2 
3 /*-
4  * Copyright (c) 2009, 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_dell.c,v 1.6 2010/10/25 07:53:22 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 #include <dev/sysmon/sysmonvar.h>
45 
46 #define _COMPONENT			ACPI_RESOURCE_COMPONENT
47 ACPI_MODULE_NAME			("wmi_dell")
48 
49 #define WMI_DELL_HOTKEY_BRIGHTNESS_DOWN	0xE005
50 #define WMI_DELL_HOTKEY_BRIGHTNESS_UP	0xE006
51 #define WMI_DELL_HOTKEY_DISPLAY_CYCLE	0xE00B
52 #define WMI_DELL_HOTKEY_VOLUME_MUTE	0xE020
53 #define WMI_DELL_HOTKEY_VOLUME_DOWN	0xE02E
54 #define WMI_DELL_HOTKEY_VOLUME_UP	0xE030
55 /*      WMI_DELL_HOTKEY_UNKNOWN		0xXXXX */
56 
57 #define WMI_DELL_PSW_DISPLAY_CYCLE	0
58 #define WMI_DELL_PSW_COUNT		1
59 
60 #define WMI_DELL_GUID_EVENT		"9DBB5994-A997-11DA-B012-B622A1EF5492"
61 
62 struct wmi_dell_softc {
63 	device_t		sc_dev;
64 	device_t		sc_parent;
65 	struct sysmon_pswitch	sc_smpsw[WMI_DELL_PSW_COUNT];
66 	bool			sc_smpsw_valid;
67 };
68 
69 static int	wmi_dell_match(device_t, cfdata_t, void *);
70 static void	wmi_dell_attach(device_t, device_t, void *);
71 static int	wmi_dell_detach(device_t, int);
72 static void	wmi_dell_notify_handler(ACPI_HANDLE, uint32_t, void *);
73 static bool	wmi_dell_suspend(device_t, const pmf_qual_t *);
74 static bool	wmi_dell_resume(device_t, const pmf_qual_t *);
75 
76 CFATTACH_DECL_NEW(wmidell, sizeof(struct wmi_dell_softc),
77     wmi_dell_match, wmi_dell_attach, wmi_dell_detach, NULL);
78 
79 static int
80 wmi_dell_match(device_t parent, cfdata_t match, void *aux)
81 {
82 	return acpi_wmi_guid_match(parent, WMI_DELL_GUID_EVENT);
83 }
84 
85 static void
86 wmi_dell_attach(device_t parent, device_t self, void *aux)
87 {
88 	struct wmi_dell_softc *sc = device_private(self);
89 	ACPI_STATUS rv;
90 	int e;
91 
92 	sc->sc_dev = self;
93 	sc->sc_parent = parent;
94 	sc->sc_smpsw_valid = true;
95 
96 	rv = acpi_wmi_event_register(parent, wmi_dell_notify_handler);
97 
98 	if (ACPI_FAILURE(rv)) {
99 		aprint_error(": failed to install WMI notify handler\n");
100 		return;
101 	}
102 
103 	aprint_naive("\n");
104 	aprint_normal(": Dell WMI mappings\n");
105 
106 	sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_name =
107 	    PSWITCH_HK_DISPLAY_CYCLE;
108 
109 	sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_type =
110 	    PSWITCH_TYPE_HOTKEY;
111 
112 	e = sysmon_pswitch_register(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE]);
113 
114 	if (e != 0)
115 		sc->sc_smpsw_valid = false;
116 
117 	(void)pmf_device_register(self, wmi_dell_suspend, wmi_dell_resume);
118 }
119 
120 static int
121 wmi_dell_detach(device_t self, int flags)
122 {
123 	struct wmi_dell_softc *sc = device_private(self);
124 	device_t parent = sc->sc_parent;
125 	size_t i;
126 
127 	(void)pmf_device_deregister(self);
128 	(void)acpi_wmi_event_deregister(parent);
129 
130 	if (sc->sc_smpsw_valid != true)
131 		return 0;
132 
133 	for (i = 0; i < __arraycount(sc->sc_smpsw); i++)
134 		sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
135 
136 	return 0;
137 }
138 
139 static bool
140 wmi_dell_suspend(device_t self, const pmf_qual_t *qual)
141 {
142 	struct wmi_dell_softc *sc = device_private(self);
143 	device_t parent = sc->sc_parent;
144 
145 	(void)acpi_wmi_event_deregister(parent);
146 
147 	return true;
148 }
149 
150 static bool
151 wmi_dell_resume(device_t self, const pmf_qual_t *qual)
152 {
153 	struct wmi_dell_softc *sc = device_private(self);
154 	device_t parent = sc->sc_parent;
155 
156 	(void)acpi_wmi_event_register(parent, wmi_dell_notify_handler);
157 
158 	return true;
159 }
160 
161 static void
162 wmi_dell_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
163 {
164 	struct wmi_dell_softc *sc;
165 	device_t self = aux;
166 	ACPI_OBJECT *obj;
167 	ACPI_BUFFER buf;
168 	ACPI_STATUS rv;
169 	uint32_t val;
170 
171 	buf.Pointer = NULL;
172 
173 	sc = device_private(self);
174 	rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf);
175 
176 	if (ACPI_FAILURE(rv))
177 		goto out;
178 
179 	obj = buf.Pointer;
180 
181 	if (obj->Type != ACPI_TYPE_BUFFER) {
182 		rv = AE_TYPE;
183 		goto out;
184 	}
185 
186 	val = obj->Buffer.Pointer[1] & 0xFFFF;
187 
188 	switch (val) {
189 
190 	case WMI_DELL_HOTKEY_BRIGHTNESS_DOWN:
191 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
192 		break;
193 
194 	case WMI_DELL_HOTKEY_BRIGHTNESS_UP:
195 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
196 		break;
197 
198 	case WMI_DELL_HOTKEY_DISPLAY_CYCLE:
199 
200 		if (sc->sc_smpsw_valid != true) {
201 			rv = AE_ABORT_METHOD;
202 			break;
203 		}
204 
205 		sysmon_pswitch_event(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE],
206 		    PSWITCH_EVENT_PRESSED);
207 		break;
208 
209 	case WMI_DELL_HOTKEY_VOLUME_MUTE:
210 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE);
211 		break;
212 
213 	case WMI_DELL_HOTKEY_VOLUME_DOWN:
214 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN);
215 		break;
216 
217 	case WMI_DELL_HOTKEY_VOLUME_UP:
218 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP);
219 		break;
220 
221 	default:
222 		aprint_debug_dev(sc->sc_dev,
223 		    "unknown key 0x%02X for event 0x%02X\n", val, evt);
224 		break;
225 	}
226 
227 out:
228 	if (buf.Pointer != NULL)
229 		ACPI_FREE(buf.Pointer);
230 
231 	if (ACPI_FAILURE(rv))
232 		aprint_error_dev(sc->sc_dev, "failed to get data for "
233 		    "event 0x%02X: %s\n", evt, AcpiFormatException(rv));
234 }
235 
236 #ifdef _MODULE
237 
238 MODULE(MODULE_CLASS_DRIVER, wmidell, NULL);
239 CFDRIVER_DECL(wmidell, DV_DULL, NULL);
240 
241 static int wmidellloc[] = { -1 };
242 extern struct cfattach wmidell_ca;
243 
244 static struct cfparent wmiparent = {
245 	"acpiwmibus", NULL, DVUNIT_ANY
246 };
247 
248 static struct cfdata wmidell_cfdata[] = {
249 	{
250 		.cf_name = "wmidell",
251 		.cf_atname = "wmidell",
252 		.cf_unit = 0,
253 		.cf_fstate = FSTATE_STAR,
254 		.cf_loc = wmidellloc,
255 		.cf_flags = 0,
256 		.cf_pspec = &wmiparent,
257 	},
258 
259 	{ NULL, NULL, 0, 0, NULL, 0, NULL }
260 };
261 
262 static int
263 wmidell_modcmd(modcmd_t cmd, void *opaque)
264 {
265 	int err;
266 
267 	switch (cmd) {
268 
269 	case MODULE_CMD_INIT:
270 
271 		err = config_cfdriver_attach(&wmidell_cd);
272 
273 		if (err != 0)
274 			return err;
275 
276 		err = config_cfattach_attach("wmidell", &wmidell_ca);
277 
278 		if (err != 0) {
279 			config_cfdriver_detach(&wmidell_cd);
280 			return err;
281 		}
282 
283 		err = config_cfdata_attach(wmidell_cfdata, 1);
284 
285 		if (err != 0) {
286 			config_cfattach_detach("wmidell", &wmidell_ca);
287 			config_cfdriver_detach(&wmidell_cd);
288 			return err;
289 		}
290 
291 		return 0;
292 
293 	case MODULE_CMD_FINI:
294 
295 		err = config_cfdata_detach(wmidell_cfdata);
296 
297 		if (err != 0)
298 			return err;
299 
300 		config_cfattach_detach("wmidell", &wmidell_ca);
301 		config_cfdriver_detach(&wmidell_cd);
302 
303 		return 0;
304 
305 	default:
306 		return ENOTTY;
307 	}
308 }
309 
310 #endif	/* _MODULE */
311