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 48ACPI_MODULE_NAME ("hpqlb_acpi") 49 50struct 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 85static int hpqlb_match(device_t, cfdata_t, void *); 86static void hpqlb_attach(device_t, device_t, void *); 87static int hpqlb_detach(device_t, int); 88 89static int hpqlb_finalize(device_t); 90static int hpqlb_hotkey_handler(struct wskbd_softc *, void *, u_int, int); 91 92static void hpqlb_init(device_t); 93static bool hpqlb_resume(device_t, const pmf_qual_t *); 94 95CFATTACH_DECL_NEW(hpqlb, sizeof(struct hpqlb_softc), 96 hpqlb_match, hpqlb_attach, hpqlb_detach, NULL); 97 98static const struct device_compatible_entry compat_data[] = { 99 { .compat = "HPQ0006" }, 100 { .compat = "HPQ0007" }, 101 DEVICE_COMPAT_EOL 102}; 103 104static int 105hpqlb_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 112static void 113hpqlb_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 150static int 151hpqlb_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 167static int 168hpqlb_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 237static void 238hpqlb_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 251static int 252hpqlb_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 294static bool 295hpqlb_resume(device_t self, const pmf_qual_t *qual) 296{ 297 298 hpqlb_init(self); 299 300 return true; 301} 302 303MODULE(MODULE_CLASS_DRIVER, hpqlb, "sysmon_power"); 304 305#ifdef _MODULE 306#include "ioconf.c" 307#endif 308 309static int 310hpqlb_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