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