jcs's openbsd hax
openbsd
at jcs 398 lines 9.1 kB view raw
1/* $OpenBSD: inthid.c,v 1.1 2025/12/27 12:58:23 kettenis Exp $ */ 2/* 3 * Intel HID event and 5-button array driver 4 * 5 * Copyright (c) 2018, 2020 joshua stein <jcs@jcs.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/param.h> 21#include <sys/signalvar.h> 22#include <sys/systm.h> 23#include <sys/device.h> 24#include <sys/malloc.h> 25 26#include <machine/bus.h> 27#include <machine/apmvar.h> 28 29#include <dev/acpi/acpireg.h> 30#include <dev/acpi/acpivar.h> 31#include <dev/acpi/acpidev.h> 32#include <dev/acpi/amltypes.h> 33#include <dev/acpi/dsdt.h> 34 35#include "audio.h" 36#include "wskbd.h" 37 38/* #define INTHID_DEBUG */ 39 40#ifdef INTHID_DEBUG 41#define DPRINTF(x) printf x 42#else 43#define DPRINTF(x) 44#endif 45 46struct inthid_softc { 47 struct device sc_dev; 48 49 bus_space_tag_t sc_iot; 50 bus_space_handle_t sc_ioh; 51 52 struct acpi_softc *sc_acpi; 53 struct aml_node *sc_devnode; 54 int sc_5_button; 55 56 /* 57 * HEBC v1 58 * 0 - Rotation Lock, Num Lock, Home, End, Page Up, Page Down 59 * 1 - Wireless Radio Control 60 * 2 - System Power Down 61 * 3 - System Hibernate 62 * 4 - System Sleep/ System Wake 63 * 5 - Scan Next Track 64 * 6 - Scan Previous Track 65 * 7 - Stop 66 * 8 - Play/Pause 67 * 9 - Mute 68 * 10 - Volume Increment 69 * 11 - Volume Decrement 70 * 12 - Display Brightness Increment 71 * 13 - Display Brightness Decrement 72 * 14 - Lock Tablet 73 * 15 - Release Tablet 74 * 16 - Toggle Bezel 75 * 17 - 5 button array 76 * 18-31 - reserved 77 * 78 * HEBC v2 79 * 0-17 - Same as v1 version 80 * 18 – Power Button 81 * 19 - W Home Button 82 * 20 - Volume Up Button 83 * 21 - Volume Down Button 84 * 22 – Rotation Lock Button 85 * 23-31 – reserved 86 */ 87 uint32_t sc_dsm_fn_mask; 88}; 89 90enum { 91 INTHID_FUNC_INVALID, 92 INTHID_FUNC_BTNL, 93 INTHID_FUNC_HDMM, 94 INTHID_FUNC_HDSM, 95 INTHID_FUNC_HDEM, 96 INTHID_FUNC_BTNS, 97 INTHID_FUNC_BTNE, 98 INTHID_FUNC_HEBC_V1, 99 INTHID_FUNC_VGBS, 100 INTHID_FUNC_HEBC_V2, 101 INTHID_FUNC_MAX, 102}; 103 104static const char *inthid_dsm_funcs[] = { 105 NULL, 106 "BTNL", 107 "HDMM", 108 "HDSM", 109 "HDEM", 110 "BTNS", 111 "BTNE", 112 "HEBC", 113 "VGBS", 114 "HEBC", 115}; 116 117int inthid_match(struct device *, void *, void *); 118void inthid_attach(struct device *, struct device *, void *); 119void inthid_init_dsm(struct inthid_softc *); 120int inthid_button_array_enable(struct inthid_softc *, int); 121int inthid_eval(struct inthid_softc *, int, int64_t, int64_t *); 122int inthid_notify(struct aml_node *, int, void *); 123 124#if NAUDIO > 0 && NWSKBD > 0 125extern int wskbd_set_mixervolume(long, long); 126#endif 127 128const struct cfattach inthid_ca = { 129 sizeof(struct inthid_softc), 130 inthid_match, 131 inthid_attach, 132 NULL, 133 NULL, 134}; 135 136struct cfdriver inthid_cd = { 137 NULL, "inthid", DV_DULL 138}; 139 140const char *inthid_hids[] = { 141 "INT33D5", 142 NULL 143}; 144 145/* eeec56b3-4442-408f-a792-4edd4d758054 */ 146static uint8_t inthid_guid[] = { 147 0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40, 148 0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54, 149}; 150 151int 152inthid_match(struct device *parent, void *match, void *aux) 153{ 154 struct acpi_attach_args *aa = aux; 155 struct cfdata *cf = match; 156 157 return (acpi_matchhids(aa, inthid_hids, cf->cf_driver->cd_name)); 158} 159 160void 161inthid_attach(struct device *parent, struct device *self, void *aux) 162{ 163 struct inthid_softc *sc = (struct inthid_softc *)self; 164 struct acpi_attach_args *aa = aux; 165 uint64_t val; 166 167 sc->sc_acpi = (struct acpi_softc *)parent; 168 sc->sc_devnode = aa->aaa_node; 169 170 printf(": %s", sc->sc_devnode->name); 171 172 inthid_init_dsm(sc); 173 174 if (inthid_eval(sc, INTHID_FUNC_HDMM, 0, &val) != 0) { 175 printf(", failed reading mode\n"); 176 return; 177 } else if (val != 0) { 178 printf(", unknown mode %lld\n", val); 179 return; 180 } 181 182 if ((inthid_eval(sc, INTHID_FUNC_HEBC_V2, 0, &val) == 0 && 183 (val & 0x60000)) || 184 (inthid_eval(sc, INTHID_FUNC_HEBC_V1, 0, &val) == 0 && 185 (val & 0x20000))) 186 sc->sc_5_button = 1; 187 188 aml_register_notify(sc->sc_devnode, aa->aaa_dev, inthid_notify, 189 sc, ACPIDEV_NOPOLL); 190 191 /* enable hid set */ 192 inthid_eval(sc, INTHID_FUNC_HDSM, 1, NULL); 193 194 if (sc->sc_5_button) { 195 inthid_button_array_enable(sc, 1); 196 197 if (inthid_eval(sc, INTHID_FUNC_BTNL, 0, NULL) == 0) 198 printf(", 5 button array"); 199 else 200 printf(", failed enabling HID power button"); 201 } 202 203 printf("\n"); 204} 205 206void 207inthid_init_dsm(struct inthid_softc *sc) 208{ 209 struct aml_value cmd[4], res; 210 211 sc->sc_dsm_fn_mask = 0; 212 213 if (!aml_searchname(sc->sc_devnode, "_DSM")) { 214 DPRINTF(("%s: no _DSM support\n", sc->sc_dev.dv_xname)); 215 return; 216 } 217 218 bzero(&cmd, sizeof(cmd)); 219 cmd[0].type = AML_OBJTYPE_BUFFER; 220 cmd[0].v_buffer = (uint8_t *)&inthid_guid; 221 cmd[0].length = sizeof(inthid_guid); 222 /* rev */ 223 cmd[1].type = AML_OBJTYPE_INTEGER; 224 cmd[1].v_integer = 1; 225 cmd[1].length = 1; 226 /* func */ 227 cmd[2].type = AML_OBJTYPE_INTEGER; 228 cmd[2].v_integer = 0; 229 cmd[2].length = 1; 230 /* not used */ 231 cmd[3].type = AML_OBJTYPE_BUFFER; 232 cmd[3].length = 0; 233 234 if (aml_evalname(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd, 235 &res)) { 236 printf("%s: eval of _DSM at %s failed\n", 237 sc->sc_dev.dv_xname, aml_nodename(sc->sc_devnode)); 238 return; 239 } 240 241 if (res.type != AML_OBJTYPE_BUFFER) { 242 printf("%s: bad _DSM result at %s: %d\n", sc->sc_dev.dv_xname, 243 aml_nodename(sc->sc_devnode), res.type); 244 aml_freevalue(&res); 245 return; 246 } 247 248 sc->sc_dsm_fn_mask = *res.v_buffer; 249 DPRINTF(("%s: _DSM function mask 0x%x\n", sc->sc_dev.dv_xname, 250 sc->sc_dsm_fn_mask)); 251 252 aml_freevalue(&res); 253} 254 255int 256inthid_eval(struct inthid_softc *sc, int idx, int64_t arg, int64_t *ret) 257{ 258 struct aml_value cmd[4], pkg, *ppkg; 259 int64_t tret; 260 const char *dsm_func; 261 262 if (idx <= INTHID_FUNC_INVALID || idx >= INTHID_FUNC_MAX) { 263 printf("%s: _DSM func index %d out of bounds\n", 264 sc->sc_dev.dv_xname, idx); 265 return 1; 266 } 267 268 dsm_func = inthid_dsm_funcs[idx]; 269 270 DPRINTF(("%s: executing _DSM %s\n", sc->sc_dev.dv_xname, dsm_func)); 271 272 if (!(sc->sc_dsm_fn_mask & idx)) { 273 DPRINTF(("%s: _DSM mask does not support %s (%d), executing " 274 "directly\n", sc->sc_dev.dv_xname, dsm_func, idx)); 275 goto eval_direct; 276 } 277 278 bzero(&pkg, sizeof(pkg)); 279 pkg.type = AML_OBJTYPE_INTEGER; 280 pkg.v_integer = arg; 281 pkg.length = 1; 282 ppkg = &pkg; 283 284 bzero(&cmd, sizeof(cmd)); 285 cmd[0].type = AML_OBJTYPE_BUFFER; 286 cmd[0].v_buffer = (uint8_t *)&inthid_guid; 287 cmd[0].length = sizeof(inthid_guid); 288 /* rev */ 289 cmd[1].type = AML_OBJTYPE_INTEGER; 290 cmd[1].v_integer = 1; 291 cmd[1].length = 1; 292 /* func */ 293 cmd[2].type = AML_OBJTYPE_INTEGER; 294 cmd[2].v_integer = idx; 295 cmd[2].length = 1; 296 /* arg */ 297 cmd[3].type = AML_OBJTYPE_PACKAGE; 298 cmd[3].length = 1; 299 cmd[3].v_package = &ppkg; 300 301 if (aml_evalinteger(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd, 302 &tret)) { 303 DPRINTF(("%s: _DSM %s failed\n", sc->sc_dev.dv_xname, 304 dsm_func)); 305 return 1; 306 } 307 308 DPRINTF(("%s: _DSM eval of %s succeeded\n", sc->sc_dev.dv_xname, 309 dsm_func)); 310 311 if (ret != NULL) 312 *ret = tret; 313 314 return 0; 315 316eval_direct: 317 cmd[0].type = AML_OBJTYPE_INTEGER; 318 cmd[0].v_integer = arg; 319 cmd[0].length = 1; 320 321 if (aml_evalinteger(acpi_softc, sc->sc_devnode, dsm_func, 1, cmd, 322 &tret) != 0) { 323 printf("%s: exec of %s failed\n", sc->sc_dev.dv_xname, 324 dsm_func); 325 return 1; 326 } 327 328 if (ret != NULL) 329 *ret = tret; 330 331 return 0; 332} 333 334int 335inthid_button_array_enable(struct inthid_softc *sc, int enable) 336{ 337 int64_t cap; 338 339 if (aml_evalinteger(acpi_softc, sc->sc_devnode, "BTNC", 0, NULL, 340 &cap) != 0) { 341 printf("%s: failed getting button array capability\n", 342 sc->sc_dev.dv_xname); 343 return 1; 344 } 345 346 if (inthid_eval(sc, INTHID_FUNC_BTNE, enable ? cap : 1, NULL) != 0) { 347 printf("%s: failed enabling button array\n", 348 sc->sc_dev.dv_xname); 349 return 1; 350 } 351 352 return 0; 353} 354 355int 356inthid_notify(struct aml_node *node, int notify_type, void *arg) 357{ 358#ifdef INTHID_DEBUG 359 struct inthid_softc *sc = arg; 360 361 DPRINTF(("%s: %s: %.2x\n", sc->sc_dev.dv_xname, __func__, 362 notify_type)); 363#endif 364 365 switch (notify_type) { 366 case 0xc2: /* left meta press */ 367 break; 368 case 0xc3: /* left meta release */ 369 break; 370 case 0xc4: /* volume up press */ 371#if NAUDIO > 0 && NWSKBD > 0 372 wskbd_set_mixervolume(1, 1); 373#endif 374 break; 375 case 0xc5: /* volume up release */ 376 break; 377 case 0xc6: /* volume down press */ 378#if NAUDIO > 0 && NWSKBD > 0 379 wskbd_set_mixervolume(-1, 1); 380#endif 381 break; 382 case 0xc7: /* volume down release */ 383 break; 384 case 0xc8: /* rotate lock toggle press */ 385 break; 386 case 0xc9: /* rotate lock toggle release */ 387 break; 388 case 0xce: /* power button press */ 389 break; 390 case 0xcf: /* power button release */ 391 break; 392 default: 393 DPRINTF(("%s: unhandled button 0x%x\n", sc->sc_dev.dv_xname, 394 notify_type)); 395 } 396 397 return 0; 398}