1 /* $OpenBSD: acpihid.c,v 1.4 2022/05/29 22:03:44 jca Exp $ */ 2 /* 3 * ACPI 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 ACPIHID_DEBUG */ 39 40 #ifdef ACPIHID_DEBUG 41 #define DPRINTF(x) printf x 42 #else 43 #define DPRINTF(x) 44 #endif 45 46 struct acpihid_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 90 enum { 91 ACPIHID_FUNC_INVALID, 92 ACPIHID_FUNC_BTNL, 93 ACPIHID_FUNC_HDMM, 94 ACPIHID_FUNC_HDSM, 95 ACPIHID_FUNC_HDEM, 96 ACPIHID_FUNC_BTNS, 97 ACPIHID_FUNC_BTNE, 98 ACPIHID_FUNC_HEBC_V1, 99 ACPIHID_FUNC_VGBS, 100 ACPIHID_FUNC_HEBC_V2, 101 ACPIHID_FUNC_MAX, 102 }; 103 104 static const char *acpihid_dsm_funcs[] = { 105 NULL, 106 "BTNL", 107 "HDMM", 108 "HDSM", 109 "HDEM", 110 "BTNS", 111 "BTNE", 112 "HEBC", 113 "VGBS", 114 "HEBC", 115 }; 116 117 int acpihid_match(struct device *, void *, void *); 118 void acpihid_attach(struct device *, struct device *, void *); 119 void acpihid_init_dsm(struct acpihid_softc *); 120 int acpihid_button_array_enable(struct acpihid_softc *, int); 121 int acpihid_eval(struct acpihid_softc *, int, int64_t, int64_t *); 122 int acpihid_notify(struct aml_node *, int, void *); 123 124 #if NAUDIO > 0 && NWSKBD > 0 125 extern int wskbd_set_mixervolume(long, long); 126 #endif 127 128 const struct cfattach acpihid_ca = { 129 sizeof(struct acpihid_softc), 130 acpihid_match, 131 acpihid_attach, 132 NULL, 133 NULL, 134 }; 135 136 struct cfdriver acpihid_cd = { 137 NULL, "acpihid", DV_DULL 138 }; 139 140 const char *acpihid_hids[] = { 141 "INT33D5", 142 NULL 143 }; 144 145 /* eeec56b3-4442-408f-a792-4edd4d758054 */ 146 static uint8_t acpihid_guid[] = { 147 0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40, 148 0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54, 149 }; 150 151 int 152 acpihid_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, acpihid_hids, cf->cf_driver->cd_name)); 158 } 159 160 void 161 acpihid_attach(struct device *parent, struct device *self, void *aux) 162 { 163 struct acpihid_softc *sc = (struct acpihid_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 acpihid_init_dsm(sc); 173 174 if (acpihid_eval(sc, ACPIHID_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 ((acpihid_eval(sc, ACPIHID_FUNC_HEBC_V2, 0, &val) == 0 && 183 (val & 0x60000)) || 184 (acpihid_eval(sc, ACPIHID_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, acpihid_notify, 189 sc, ACPIDEV_NOPOLL); 190 191 /* enable hid set */ 192 acpihid_eval(sc, ACPIHID_FUNC_HDSM, 1, NULL); 193 194 if (sc->sc_5_button) { 195 acpihid_button_array_enable(sc, 1); 196 197 if (acpihid_eval(sc, ACPIHID_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 206 void 207 acpihid_init_dsm(struct acpihid_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 *)&acpihid_guid; 221 cmd[0].length = sizeof(acpihid_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 255 int 256 acpihid_eval(struct acpihid_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 <= ACPIHID_FUNC_INVALID || idx >= ACPIHID_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 = acpihid_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 *)&acpihid_guid; 287 cmd[0].length = sizeof(acpihid_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 316 eval_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 334 int 335 acpihid_button_array_enable(struct acpihid_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 (acpihid_eval(sc, ACPIHID_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 355 int 356 acpihid_notify(struct aml_node *node, int notify_type, void *arg) 357 { 358 #ifdef ACPIHID_DEBUG 359 struct acpihid_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 } 399