hpqlb_acpi.c revision 1.5 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 (at) 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