xref: /netbsd-src/sys/dev/acpi/wmi/wmi_dell.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: wmi_dell.c,v 1.4 2010/06/30 07:58:11 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.4 2010/06/30 07:58:11 jruoho Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/device.h>
38 
39 #include <dev/acpi/acpireg.h>
40 #include <dev/acpi/acpivar.h>
41 #include <dev/acpi/wmi/wmi_acpivar.h>
42 
43 #include <dev/sysmon/sysmonvar.h>
44 
45 #define _COMPONENT			ACPI_RESOURCE_COMPONENT
46 ACPI_MODULE_NAME			("wmi_dell")
47 
48 #define WMI_DELL_HOTKEY_BRIGHTNESS_DOWN	0xE005
49 #define WMI_DELL_HOTKEY_BRIGHTNESS_UP	0xE006
50 #define WMI_DELL_HOTKEY_DISPLAY_CYCLE	0xE00B
51 #define WMI_DELL_HOTKEY_VOLUME_MUTE	0xE020
52 #define WMI_DELL_HOTKEY_VOLUME_DOWN	0xE02E
53 #define WMI_DELL_HOTKEY_VOLUME_UP	0xE030
54 /*      WMI_DELL_HOTKEY_UNKNOWN		0xXXXX */
55 
56 #define WMI_DELL_PSW_DISPLAY_CYCLE	0
57 #define WMI_DELL_PSW_COUNT		1
58 
59 #define WMI_DELL_GUID_EVENT		"9DBB5994-A997-11DA-B012-B622A1EF5492"
60 
61 struct wmi_dell_softc {
62 	device_t		sc_dev;
63 	device_t		sc_parent;
64 	struct sysmon_pswitch	sc_smpsw[WMI_DELL_PSW_COUNT];
65 	bool			sc_smpsw_valid;
66 };
67 
68 static int	wmi_dell_match(device_t, cfdata_t, void *);
69 static void	wmi_dell_attach(device_t, device_t, void *);
70 static int	wmi_dell_detach(device_t, int);
71 static void	wmi_dell_notify_handler(ACPI_HANDLE, uint32_t, void *);
72 static bool	wmi_dell_suspend(device_t, const pmf_qual_t *);
73 static bool	wmi_dell_resume(device_t, const pmf_qual_t *);
74 
75 CFATTACH_DECL_NEW(wmidell, sizeof(struct wmi_dell_softc),
76     wmi_dell_match, wmi_dell_attach, wmi_dell_detach, NULL);
77 
78 static int
79 wmi_dell_match(device_t parent, cfdata_t match, void *aux)
80 {
81 	return acpi_wmi_guid_match(parent, WMI_DELL_GUID_EVENT);
82 }
83 
84 static void
85 wmi_dell_attach(device_t parent, device_t self, void *aux)
86 {
87 	struct wmi_dell_softc *sc = device_private(self);
88 	ACPI_STATUS rv;
89 	int e;
90 
91 	sc->sc_dev = self;
92 	sc->sc_parent = parent;
93 	sc->sc_smpsw_valid = true;
94 
95 	rv = acpi_wmi_event_register(parent, wmi_dell_notify_handler);
96 
97 	if (ACPI_FAILURE(rv)) {
98 		aprint_error(": failed to install WMI notify handler\n");
99 		return;
100 	}
101 
102 	aprint_naive("\n");
103 	aprint_normal(": Dell WMI mappings\n");
104 
105 	sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_name =
106 	    PSWITCH_HK_DISPLAY_CYCLE;
107 
108 	sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE].smpsw_type =
109 	    PSWITCH_TYPE_HOTKEY;
110 
111 	e = sysmon_pswitch_register(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE]);
112 
113 	if (e != 0)
114 		sc->sc_smpsw_valid = false;
115 
116 	(void)pmf_device_register(self, wmi_dell_suspend, wmi_dell_resume);
117 }
118 
119 static int
120 wmi_dell_detach(device_t self, int flags)
121 {
122 	struct wmi_dell_softc *sc = device_private(self);
123 	device_t parent = sc->sc_parent;
124 	int i;
125 
126 	(void)pmf_device_deregister(self);
127 	(void)acpi_wmi_event_deregister(parent);
128 
129 	if (sc->sc_smpsw_valid != true)
130 		return 0;
131 
132 	for (i = 0; i < __arraycount(sc->sc_smpsw); i++)
133 		sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
134 
135 	return 0;
136 }
137 
138 static bool
139 wmi_dell_suspend(device_t self, const pmf_qual_t *qual)
140 {
141 	struct wmi_dell_softc *sc = device_private(self);
142 	device_t parent = sc->sc_parent;
143 
144 	(void)acpi_wmi_event_deregister(parent);
145 
146 	return true;
147 }
148 
149 static bool
150 wmi_dell_resume(device_t self, const pmf_qual_t *qual)
151 {
152 	struct wmi_dell_softc *sc = device_private(self);
153 	device_t parent = sc->sc_parent;
154 
155 	(void)acpi_wmi_event_register(parent, wmi_dell_notify_handler);
156 
157 	return true;
158 }
159 
160 static void
161 wmi_dell_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
162 {
163 	struct wmi_dell_softc *sc;
164 	device_t self = aux;
165 	ACPI_OBJECT *obj;
166 	ACPI_BUFFER buf;
167 	ACPI_STATUS rv;
168 	uint32_t val;
169 
170 	buf.Pointer = NULL;
171 
172 	sc = device_private(self);
173 	rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf);
174 
175 	if (ACPI_FAILURE(rv))
176 		goto out;
177 
178 	obj = buf.Pointer;
179 
180 	if (obj->Type != ACPI_TYPE_BUFFER) {
181 		rv = AE_TYPE;
182 		goto out;
183 	}
184 
185 	val = obj->Buffer.Pointer[1] & 0xFFFF;
186 
187 	switch (val) {
188 
189 	case WMI_DELL_HOTKEY_BRIGHTNESS_DOWN:
190 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
191 		break;
192 
193 	case WMI_DELL_HOTKEY_BRIGHTNESS_UP:
194 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
195 		break;
196 
197 	case WMI_DELL_HOTKEY_DISPLAY_CYCLE:
198 
199 		if (sc->sc_smpsw_valid != true) {
200 			rv = AE_ABORT_METHOD;
201 			break;
202 		}
203 
204 		sysmon_pswitch_event(&sc->sc_smpsw[WMI_DELL_PSW_DISPLAY_CYCLE],
205 		    PSWITCH_EVENT_PRESSED);
206 		break;
207 
208 	case WMI_DELL_HOTKEY_VOLUME_MUTE:
209 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE);
210 		break;
211 
212 	case WMI_DELL_HOTKEY_VOLUME_DOWN:
213 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN);
214 		break;
215 
216 	case WMI_DELL_HOTKEY_VOLUME_UP:
217 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP);
218 		break;
219 
220 	default:
221 		aprint_debug_dev(sc->sc_dev,
222 		    "unknown key 0x%02X for event 0x%02X\n", val, evt);
223 		break;
224 	}
225 
226 out:
227 	if (buf.Pointer != NULL)
228 		ACPI_FREE(buf.Pointer);
229 
230 	if (ACPI_FAILURE(rv))
231 		aprint_error_dev(sc->sc_dev, "failed to get data for "
232 		    "event 0x%02X: %s\n", evt, AcpiFormatException(rv));
233 }
234