1 /* $NetBSD: hpqlb_acpi.c,v 1.2 2008/05/02 01:53:33 simonb 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.2 2008/05/02 01:53:33 simonb 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/acpivar.h> 42 43 #include <machine/pio.h> 44 #include <dev/wscons/wsconsio.h> 45 #include <dev/wscons/wskbdvar.h> 46 #include <dev/isa/isareg.h> 47 48 #ifdef HPQLB_DEBUG 49 #define DPRINTF(x) do { printf x; } while (/* CONSTCOND */0) 50 #else 51 #define DPRINTF(x) 52 #endif 53 54 struct hpqlb_softc { 55 device_t sc_dev; 56 struct acpi_devnode *sc_node; 57 58 device_t sc_wskbddev; 59 60 #define HP_PSW_DISPLAY_CYCLE 0 61 #define HP_PSW_BRIGHTNESS_UP 1 62 #define HP_PSW_BRIGHTNESS_DOWN 2 63 #define HP_PSW_SLEEP 3 64 #define HP_PSW_LAST 4 65 struct sysmon_pswitch sc_smpsw[HP_PSW_LAST]; 66 bool sc_smpsw_displaycycle_valid; 67 bool sc_smpsw_sleep_valid; 68 }; 69 70 #define HP_QLB_Quick 0x88 71 #define HP_QLB_DVD 0x8e 72 #define HP_QLB_FullBackward 0x90 73 #define HP_QLB_Play 0xa2 74 #define HP_QLB_FullForward 0x99 75 #define HP_QLB_Stop 0xa4 76 #define HP_QLB_VolumeMute 0xa0 77 #define HP_QLB_VolumeDown 0xae 78 #define HP_QLB_VolumeUp 0xb0 79 80 #define HP_QLB_Help 0xb1 81 #define HP_QLB_WWW 0xb2 82 #define HP_QLB_DisplayCycle /* ??? */ 83 #define HP_QLB_Sleep 0xdf 84 #define HP_QLB_Lock 0x8a 85 #define HP_QLB_BrightnessDown /* ??? */ 86 #define HP_QLB_BrightnessUp /* ??? */ 87 #define HP_QLB_ChasisOpen 0xe3 88 89 static int hpqlb_match(device_t, cfdata_t, void *); 90 static void hpqlb_attach(device_t, device_t, void *); 91 92 static int hpqlb_finalize(device_t); 93 static int hpqlb_hotkey_handler(struct wskbd_softc *, void *, u_int, int); 94 95 static void hpqlb_init(device_t); 96 static bool hpqlb_resume(device_t PMF_FN_PROTO); 97 98 CFATTACH_DECL_NEW(hpqlb, sizeof(struct hpqlb_softc), 99 hpqlb_match, hpqlb_attach, NULL, NULL); 100 101 static const char * const hpqlb_ids[] = { 102 "HPQ0006", 103 "HPQ0007", 104 NULL 105 }; 106 107 static int 108 hpqlb_match(device_t parent, cfdata_t match, void *opaque) 109 { 110 struct acpi_attach_args *aa = opaque; 111 112 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 113 return 0; 114 115 return acpi_match_hid(aa->aa_node->ad_devinfo, hpqlb_ids); 116 } 117 118 static void 119 hpqlb_attach(device_t parent, device_t self, void *opaque) 120 { 121 struct hpqlb_softc *sc = device_private(self); 122 struct acpi_attach_args *aa = opaque; 123 124 sc->sc_node = aa->aa_node; 125 sc->sc_dev = self; 126 127 aprint_naive("\n"); 128 aprint_normal(": HP Quick Launch Buttons\n"); 129 130 hpqlb_init(self); 131 132 if (config_finalize_register(self, hpqlb_finalize) != 0) 133 aprint_error_dev(self, 134 "WARNING: unable to register hpqlb finalizer\n"); 135 136 sc->sc_smpsw_displaycycle_valid = true; 137 sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE].smpsw_name = 138 PSWITCH_HK_DISPLAY_CYCLE; 139 sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE].smpsw_type = 140 PSWITCH_TYPE_HOTKEY; 141 if (sysmon_pswitch_register(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE])) { 142 aprint_error_dev(self, "couldn't register with sysmon\n"); 143 sc->sc_smpsw_displaycycle_valid = false; 144 } 145 146 sc->sc_smpsw_sleep_valid = true; 147 sc->sc_smpsw[HP_PSW_SLEEP].smpsw_name = device_xname(self); 148 sc->sc_smpsw[HP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 149 if (sysmon_pswitch_register(&sc->sc_smpsw[HP_PSW_SLEEP])) { 150 aprint_error_dev(self, "couldn't register sleep with sysmon\n"); 151 sc->sc_smpsw_sleep_valid = false; 152 } 153 154 if (!pmf_device_register(self, NULL, hpqlb_resume)) 155 aprint_error_dev(self, "couldn't establish power handler\n"); 156 } 157 158 static int 159 hpqlb_hotkey_handler(struct wskbd_softc *wskbd_sc, void *cookie, 160 u_int type, int value) 161 { 162 struct hpqlb_softc *sc = cookie; 163 int ret = 1; 164 165 switch (value) { 166 case HP_QLB_VolumeMute: 167 if (type != WSCONS_EVENT_KEY_DOWN) 168 break; 169 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE); 170 break; 171 case HP_QLB_VolumeDown: 172 if (type != WSCONS_EVENT_KEY_DOWN) 173 break; 174 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN); 175 break; 176 case HP_QLB_VolumeUp: 177 if (type != WSCONS_EVENT_KEY_DOWN) 178 break; 179 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP); 180 break; 181 182 #if 0 183 case HP_QLB_DisplayCycle: /* ??? */ 184 if (type != WSCONS_EVENT_KEY_DOWN) 185 break; 186 if (sc->sc_smpsw_displaycycle_valid == false) 187 break; 188 sysmon_pswitch_event(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE], 189 PSWITCH_EVENT_PRESSED); 190 break; 191 #endif 192 case HP_QLB_Sleep: 193 if (type != WSCONS_EVENT_KEY_DOWN) 194 break; 195 if (sc->sc_smpsw_sleep_valid == false) { 196 DPRINTF(("%s: Sleep hotkey\n", 197 device_xname(sc->sc_dev))); 198 break; 199 } 200 sysmon_pswitch_event(&sc->sc_smpsw[HP_PSW_SLEEP], 201 PSWITCH_EVENT_PRESSED); 202 break; 203 #if 0 204 case HP_QLB_BrightnessDown: /* ??? */ 205 if (type != WSCONS_EVENT_KEY_DOWN) 206 break; 207 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 208 break; 209 case HP_QLB_BrightnessUp: /* ??? */ 210 if (type != WSCONS_EVENT_KEY_DOWN) 211 break; 212 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 213 break; 214 #endif 215 case HP_QLB_ChasisOpen: 216 if (type != WSCONS_EVENT_KEY_DOWN) 217 break; 218 pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); 219 break; 220 default: 221 DPRINTF(("%s: unknown hotkey 0x%02x\n", 222 device_xname(sc->sc_dev), value)); 223 ret = 0; /* Assume, this is no hotkey */ 224 break; 225 } 226 227 return ret; 228 } 229 230 static void 231 hpqlb_init(device_t self) 232 { 233 234 /* HPQ0006: HP Quick Launch Buttons */ 235 /* HPQ0007: HP Remote Device */ 236 /* val 0, 1 or 7 == HPQ0006 */ 237 /* val not 0, 1 or 7 == HPQ0007 */ 238 239 /* Turn on Quick Launch Buttons */ 240 outb(IO_RTC+2, 0xaf); 241 outb(IO_RTC+3, 7 /* val */); 242 } 243 244 static int 245 hpqlb_finalize(device_t self) 246 { 247 device_t dv; 248 struct hpqlb_softc *sc = device_private(self); 249 static int done_once = 0; 250 251 /* Since we only handle real hardware, we only need to be 252 * called once. 253 */ 254 if (done_once) 255 return 0; 256 done_once = 1; 257 258 TAILQ_FOREACH(dv, &alldevs, dv_list) { 259 if (!device_is_a(dv, "wskbd")) 260 continue; 261 262 /* Make sure, we don't get a wskbd from a USB keyboard. 263 * QLB only works on the wskbd attached on pckbd. */ 264 if (!device_is_a(device_parent(dv), "pckbd")) 265 continue; 266 267 aprint_normal_dev(self, "registering on %s\n", 268 device_xname(dv)); 269 break; 270 } 271 272 if (dv == NULL) { 273 aprint_error_dev(self, "WARNING: no matching wskbd found\n"); 274 return 1; 275 } 276 277 sc->sc_wskbddev = dv; 278 279 wskbd_hotkey_register(sc->sc_wskbddev, sc, hpqlb_hotkey_handler); 280 281 return 0; 282 } 283 284 static bool 285 hpqlb_resume(device_t self PMF_FN_ARGS) 286 { 287 288 hpqlb_init(self); 289 290 return true; 291 } 292