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