1 /* $NetBSD: hpqlb_acpi.c,v 1.11 2021/01/29 15:49:55 thorpej 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.11 2021/01/29 15:49:55 thorpej 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 struct device_compatible_entry compat_data[] = { 99 { .compat = "HPQ0006" }, 100 { .compat = "HPQ0007" }, 101 DEVICE_COMPAT_EOL 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 return acpi_compatible_match(aa, compat_data); 110 } 111 112 static void 113 hpqlb_attach(device_t parent, device_t self, void *opaque) 114 { 115 struct hpqlb_softc *sc = device_private(self); 116 struct acpi_attach_args *aa = opaque; 117 118 sc->sc_node = aa->aa_node; 119 sc->sc_dev = self; 120 121 aprint_naive("\n"); 122 aprint_normal(": HP Quick Launch Buttons\n"); 123 124 hpqlb_init(self); 125 126 if (config_finalize_register(self, hpqlb_finalize) != 0) 127 aprint_error_dev(self, "unable to register hpqlb finalizer\n"); 128 129 sc->sc_smpsw_displaycycle_valid = true; 130 131 sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE].smpsw_name = 132 PSWITCH_HK_DISPLAY_CYCLE; 133 134 sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE].smpsw_type = 135 PSWITCH_TYPE_HOTKEY; 136 137 if (sysmon_pswitch_register(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE]) != 0) 138 sc->sc_smpsw_displaycycle_valid = false; 139 140 sc->sc_smpsw_sleep_valid = true; 141 sc->sc_smpsw[HP_PSW_SLEEP].smpsw_name = device_xname(self); 142 sc->sc_smpsw[HP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 143 144 if (sysmon_pswitch_register(&sc->sc_smpsw[HP_PSW_SLEEP]) != 0) 145 sc->sc_smpsw_sleep_valid = false; 146 147 (void)pmf_device_register(self, NULL, hpqlb_resume); 148 } 149 150 static int 151 hpqlb_detach(device_t self, int flags) 152 { 153 struct hpqlb_softc *sc = device_private(self); 154 155 pmf_device_deregister(self); 156 wskbd_hotkey_deregister(sc->sc_wskbddev); 157 158 if (sc->sc_smpsw_sleep_valid != false) 159 sysmon_pswitch_unregister(&sc->sc_smpsw[HP_PSW_SLEEP]); 160 161 if (sc->sc_smpsw_displaycycle_valid != false) 162 sysmon_pswitch_unregister(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE]); 163 164 return 0; 165 } 166 167 static int 168 hpqlb_hotkey_handler(struct wskbd_softc *wskbd_sc, void *cookie, 169 u_int type, int value) 170 { 171 struct hpqlb_softc *sc = cookie; 172 int ret = 1; 173 174 switch (value) { 175 case HP_QLB_VolumeMute: 176 if (type != WSCONS_EVENT_KEY_DOWN) 177 break; 178 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE); 179 break; 180 case HP_QLB_VolumeDown: 181 if (type != WSCONS_EVENT_KEY_DOWN) 182 break; 183 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN); 184 break; 185 case HP_QLB_VolumeUp: 186 if (type != WSCONS_EVENT_KEY_DOWN) 187 break; 188 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP); 189 break; 190 191 #if 0 192 case HP_QLB_DisplayCycle: /* ??? */ 193 if (type != WSCONS_EVENT_KEY_DOWN) 194 break; 195 if (sc->sc_smpsw_displaycycle_valid == false) 196 break; 197 sysmon_pswitch_event(&sc->sc_smpsw[HP_PSW_DISPLAY_CYCLE], 198 PSWITCH_EVENT_PRESSED); 199 break; 200 #endif 201 case HP_QLB_Sleep: 202 if (type != WSCONS_EVENT_KEY_DOWN) 203 break; 204 if (sc->sc_smpsw_sleep_valid == false) 205 break; 206 sysmon_pswitch_event(&sc->sc_smpsw[HP_PSW_SLEEP], 207 PSWITCH_EVENT_PRESSED); 208 break; 209 #if 0 210 case HP_QLB_BrightnessDown: /* ??? */ 211 if (type != WSCONS_EVENT_KEY_DOWN) 212 break; 213 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 214 break; 215 case HP_QLB_BrightnessUp: /* ??? */ 216 if (type != WSCONS_EVENT_KEY_DOWN) 217 break; 218 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 219 break; 220 #endif 221 case HP_QLB_ChasisOpen: 222 if (type != WSCONS_EVENT_KEY_DOWN) 223 break; 224 pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); 225 break; 226 default: 227 228 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "unknown hotkey " 229 "0x%02x\n", value)); 230 ret = 0; /* Assume, this is no hotkey */ 231 break; 232 } 233 234 return ret; 235 } 236 237 static void 238 hpqlb_init(device_t self) 239 { 240 241 /* HPQ0006: HP Quick Launch Buttons */ 242 /* HPQ0007: HP Remote Device */ 243 /* val 0, 1 or 7 == HPQ0006 */ 244 /* val not 0, 1 or 7 == HPQ0007 */ 245 246 /* Turn on Quick Launch Buttons */ 247 outb(IO_RTC+2, 0xaf); 248 outb(IO_RTC+3, 7 /* val */); 249 } 250 251 static int 252 hpqlb_finalize(device_t self) 253 { 254 device_t dv; 255 deviter_t di; 256 struct hpqlb_softc *sc = device_private(self); 257 static int done_once = 0; 258 259 /* Since we only handle real hardware, we only need to be 260 * called once. 261 */ 262 if (done_once) 263 return 0; 264 done_once = 1; 265 266 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL; 267 dv = deviter_next(&di)) { 268 if (!device_is_a(dv, "wskbd")) 269 continue; 270 271 /* Make sure, we don't get a wskbd from a USB keyboard. 272 * QLB only works on the wskbd attached on pckbd. */ 273 if (!device_is_a(device_parent(dv), "pckbd")) 274 continue; 275 276 aprint_normal_dev(self, "registering on %s\n", 277 device_xname(dv)); 278 break; 279 } 280 deviter_release(&di); 281 282 if (dv == NULL) { 283 aprint_error_dev(self, "WARNING: no matching wskbd found\n"); 284 return 1; 285 } 286 287 sc->sc_wskbddev = dv; 288 289 wskbd_hotkey_register(sc->sc_wskbddev, sc, hpqlb_hotkey_handler); 290 291 return 0; 292 } 293 294 static bool 295 hpqlb_resume(device_t self, const pmf_qual_t *qual) 296 { 297 298 hpqlb_init(self); 299 300 return true; 301 } 302 303 MODULE(MODULE_CLASS_DRIVER, hpqlb, "sysmon_power"); 304 305 #ifdef _MODULE 306 #include "ioconf.c" 307 #endif 308 309 static int 310 hpqlb_modcmd(modcmd_t cmd, void *aux) 311 { 312 int rv = 0; 313 314 switch (cmd) { 315 316 case MODULE_CMD_INIT: 317 318 #ifdef _MODULE 319 rv = config_init_component(cfdriver_ioconf_hpqlb, 320 cfattach_ioconf_hpqlb, cfdata_ioconf_hpqlb); 321 #endif 322 break; 323 324 case MODULE_CMD_FINI: 325 326 #ifdef _MODULE 327 rv = config_fini_component(cfdriver_ioconf_hpqlb, 328 cfattach_ioconf_hpqlb, cfdata_ioconf_hpqlb); 329 #endif 330 break; 331 332 default: 333 rv = ENOTTY; 334 } 335 336 return rv; 337 } 338