xref: /netbsd-src/sys/dev/acpi/hpqlb_acpi.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
1 /* $NetBSD: hpqlb_acpi.c,v 1.5 2010/01/30 18:35:49 jruoho Exp $ */
2 
3 /*-
4  * Copyright (c) 2008  Christoph Egger <cegger@netbsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: hpqlb_acpi.c,v 1.5 2010/01/30 18:35:49 jruoho Exp $");
31 
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/malloc.h>
35 #include <sys/buf.h>
36 #include <sys/callout.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
39 #include <sys/pmf.h>
40 
41 #include <dev/acpi/acpireg.h>
42 #include <dev/acpi/acpivar.h>
43 
44 #include <machine/pio.h>
45 #include <dev/wscons/wsconsio.h>
46 #include <dev/wscons/wskbdvar.h>
47 #include <dev/isa/isareg.h>
48 
49 #define _COMPONENT		ACPI_RESOURCE_COMPONENT
50 ACPI_MODULE_NAME		("hpqlb_acpi")
51 
52 #ifdef HPQLB_DEBUG
53 #define DPRINTF(x)		do { printf x; } while (/* CONSTCOND */0)
54 #else
55 #define DPRINTF(x)
56 #endif
57 
58 struct hpqlb_softc {
59 	device_t sc_dev;
60 	struct acpi_devnode *sc_node;
61 
62 	device_t sc_wskbddev;
63 
64 #define HP_PSW_DISPLAY_CYCLE	0
65 #define HP_PSW_BRIGHTNESS_UP	1
66 #define HP_PSW_BRIGHTNESS_DOWN	2
67 #define HP_PSW_SLEEP		3
68 #define HP_PSW_LAST		4
69 	struct sysmon_pswitch sc_smpsw[HP_PSW_LAST];
70 	bool sc_smpsw_displaycycle_valid;
71 	bool sc_smpsw_sleep_valid;
72 };
73 
74 #define HP_QLB_Quick			0x88
75 #define HP_QLB_DVD			0x8e
76 #define HP_QLB_FullBackward		0x90
77 #define HP_QLB_Play			0xa2
78 #define HP_QLB_FullForward		0x99
79 #define HP_QLB_Stop			0xa4
80 #define HP_QLB_VolumeMute		0xa0
81 #define HP_QLB_VolumeDown		0xae
82 #define HP_QLB_VolumeUp			0xb0
83 
84 #define HP_QLB_Help			0xb1
85 #define HP_QLB_WWW			0xb2
86 #define HP_QLB_DisplayCycle		/* ??? */
87 #define HP_QLB_Sleep			0xdf
88 #define HP_QLB_Lock			0x8a
89 #define HP_QLB_BrightnessDown		/* ??? */
90 #define HP_QLB_BrightnessUp		/* ??? */
91 #define HP_QLB_ChasisOpen		0xe3
92 
93 static int hpqlb_match(device_t, cfdata_t, void *);
94 static void hpqlb_attach(device_t, device_t, void *);
95 
96 static int hpqlb_finalize(device_t);
97 static int hpqlb_hotkey_handler(struct wskbd_softc *, void *, u_int, int);
98 
99 static void hpqlb_init(device_t);
100 static bool hpqlb_resume(device_t, pmf_qual_t);
101 
102 CFATTACH_DECL_NEW(hpqlb, sizeof(struct hpqlb_softc),
103     hpqlb_match, hpqlb_attach, NULL, NULL);
104 
105 static const char * const hpqlb_ids[] = {
106 	"HPQ0006",
107 	"HPQ0007",
108 	NULL
109 };
110 
111 static int
112 hpqlb_match(device_t parent, cfdata_t match, void *opaque)
113 {
114 	struct acpi_attach_args *aa = opaque;
115 
116 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
117 		return 0;
118 
119 	return acpi_match_hid(aa->aa_node->ad_devinfo, hpqlb_ids);
120 }
121 
122 static void
123 hpqlb_attach(device_t parent, device_t self, void *opaque)
124 {
125 	struct hpqlb_softc *sc = device_private(self);
126 	struct acpi_attach_args *aa = opaque;
127 
128 	sc->sc_node = aa->aa_node;
129 	sc->sc_dev = self;
130 
131 	aprint_naive("\n");
132 	aprint_normal(": HP Quick Launch Buttons\n");
133 
134 	hpqlb_init(self);
135 
136 	if (config_finalize_register(self, hpqlb_finalize) != 0)
137 		aprint_error_dev(self,
138 			"WARNING: unable to register hpqlb finalizer\n");
139 
140 	sc->sc_smpsw_displaycycle_valid = true;
141 	sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE].smpsw_name =
142 	    PSWITCH_HK_DISPLAY_CYCLE;
143 	sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE].smpsw_type =
144 	    PSWITCH_TYPE_HOTKEY;
145 	if (sysmon_pswitch_register(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE])) {
146 		aprint_error_dev(self, "couldn't register with sysmon\n");
147 		sc->sc_smpsw_displaycycle_valid = false;
148 	}
149 
150 	sc->sc_smpsw_sleep_valid = true;
151 	sc->sc_smpsw[HP_PSW_SLEEP].smpsw_name = device_xname(self);
152 	sc->sc_smpsw[HP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP;
153 	if (sysmon_pswitch_register(&sc->sc_smpsw[HP_PSW_SLEEP])) {
154 		aprint_error_dev(self, "couldn't register sleep with sysmon\n");
155 		sc->sc_smpsw_sleep_valid = false;
156 	}
157 
158 	if (!pmf_device_register(self, NULL, hpqlb_resume))
159 		aprint_error_dev(self, "couldn't establish power handler\n");
160 }
161 
162 static int
163 hpqlb_hotkey_handler(struct wskbd_softc *wskbd_sc, void *cookie,
164 		u_int type, int value)
165 {
166 	struct hpqlb_softc *sc = cookie;
167 	int ret = 1;
168 
169 	switch (value) {
170 	case HP_QLB_VolumeMute:
171 		if (type != WSCONS_EVENT_KEY_DOWN)
172 			break;
173 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE);
174 		break;
175 	case HP_QLB_VolumeDown:
176 		if (type != WSCONS_EVENT_KEY_DOWN)
177 			break;
178 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN);
179 		break;
180 	case HP_QLB_VolumeUp:
181 		if (type != WSCONS_EVENT_KEY_DOWN)
182 			break;
183 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP);
184 		break;
185 
186 #if 0
187 	case HP_QLB_DisplayCycle:		/* ??? */
188 		if (type != WSCONS_EVENT_KEY_DOWN)
189 			break;
190 		if (sc->sc_smpsw_displaycycle_valid == false)
191 			break;
192 		sysmon_pswitch_event(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE],
193 			PSWITCH_EVENT_PRESSED);
194 		break;
195 #endif
196 	case HP_QLB_Sleep:
197 		if (type != WSCONS_EVENT_KEY_DOWN)
198 			break;
199 		if (sc->sc_smpsw_sleep_valid == false) {
200 			DPRINTF(("%s: Sleep hotkey\n",
201 			    device_xname(sc->sc_dev)));
202 			break;
203 		}
204 		sysmon_pswitch_event(&sc->sc_smpsw[HP_PSW_SLEEP],
205 			PSWITCH_EVENT_PRESSED);
206 		break;
207 #if 0
208 	case HP_QLB_BrightnessDown:	/* ??? */
209 		if (type != WSCONS_EVENT_KEY_DOWN)
210 			break;
211 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
212 		break;
213 	case HP_QLB_BrightnessUp:	/* ??? */
214 		if (type != WSCONS_EVENT_KEY_DOWN)
215 			break;
216 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
217 		break;
218 #endif
219 	case HP_QLB_ChasisOpen:
220 		if (type != WSCONS_EVENT_KEY_DOWN)
221 			break;
222 		pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN);
223 		break;
224 	default:
225 		DPRINTF(("%s: unknown hotkey 0x%02x\n",
226 			device_xname(sc->sc_dev), value));
227 		ret = 0; /* Assume, this is no hotkey */
228 		break;
229 	}
230 
231 	return ret;
232 }
233 
234 static void
235 hpqlb_init(device_t self)
236 {
237 
238 	/* HPQ0006: HP Quick Launch Buttons */
239 	/* HPQ0007: HP Remote Device */
240 	/* val 0, 1 or 7 == HPQ0006 */
241 	/* val not 0, 1 or 7 == HPQ0007 */
242 
243 	/* Turn on Quick Launch Buttons */
244 	outb(IO_RTC+2, 0xaf);
245 	outb(IO_RTC+3, 7 /* val */);
246 }
247 
248 static int
249 hpqlb_finalize(device_t self)
250 {
251 	device_t dv;
252 	deviter_t di;
253 	struct hpqlb_softc *sc = device_private(self);
254 	static int done_once = 0;
255 
256 	/* Since we only handle real hardware, we only need to be
257 	 * called once.
258 	 */
259 	if (done_once)
260 		return 0;
261 	done_once = 1;
262 
263 	for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL;
264 	     dv = deviter_next(&di)) {
265 		if (!device_is_a(dv, "wskbd"))
266 			continue;
267 
268 		/* Make sure, we don't get a wskbd from a USB keyboard.
269 		 * QLB only works on the wskbd attached on pckbd. */
270 		if (!device_is_a(device_parent(dv), "pckbd"))
271 			continue;
272 
273 		aprint_normal_dev(self, "registering on %s\n",
274 				device_xname(dv));
275 		break;
276 	}
277 	deviter_release(&di);
278 
279 	if (dv == NULL) {
280 		aprint_error_dev(self, "WARNING: no matching wskbd found\n");
281 		return 1;
282 	}
283 
284 	sc->sc_wskbddev = dv;
285 
286 	wskbd_hotkey_register(sc->sc_wskbddev, sc, hpqlb_hotkey_handler);
287 
288 	return 0;
289 }
290 
291 static bool
292 hpqlb_resume(device_t self, pmf_qual_t qual)
293 {
294 
295 	hpqlb_init(self);
296 
297 	return true;
298 }
299