1 /* $OpenBSD: acpihid.c,v 1.2 2020/06/02 19:26:36 jcs 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 extern int pwr_action; 129 130 struct cfattach acpihid_ca = { 131 sizeof(struct acpihid_softc), 132 acpihid_match, 133 acpihid_attach, 134 NULL, 135 NULL, 136 }; 137 138 struct cfdriver acpihid_cd = { 139 NULL, "acpihid", DV_DULL 140 }; 141 142 const char *acpihid_hids[] = { 143 "INT33D5", 144 NULL 145 }; 146 147 /* eeec56b3-4442-408f-a792-4edd4d758054 */ 148 static uint8_t acpihid_guid[] = { 149 0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40, 150 0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54, 151 }; 152 153 int 154 acpihid_match(struct device *parent, void *match, void *aux) 155 { 156 struct acpi_attach_args *aa = aux; 157 struct cfdata *cf = match; 158 159 return (acpi_matchhids(aa, acpihid_hids, cf->cf_driver->cd_name)); 160 } 161 162 void 163 acpihid_attach(struct device *parent, struct device *self, void *aux) 164 { 165 struct acpihid_softc *sc = (struct acpihid_softc *)self; 166 struct acpi_attach_args *aa = aux; 167 uint64_t val; 168 169 sc->sc_acpi = (struct acpi_softc *)parent; 170 sc->sc_devnode = aa->aaa_node; 171 172 printf(": %s", sc->sc_devnode->name); 173 174 acpihid_init_dsm(sc); 175 176 if (acpihid_eval(sc, ACPIHID_FUNC_HDMM, 0, &val) != 0) { 177 printf(", failed reading mode\n"); 178 return; 179 } else if (val != 0) { 180 printf(", unknown mode %lld\n", val); 181 return; 182 } 183 184 if ((acpihid_eval(sc, ACPIHID_FUNC_HEBC_V2, 0, &val) == 0 && 185 (val & 0x60000)) || 186 (acpihid_eval(sc, ACPIHID_FUNC_HEBC_V1, 0, &val) == 0 && 187 (val & 0x20000))) 188 sc->sc_5_button = 1; 189 190 aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpihid_notify, 191 sc, ACPIDEV_NOPOLL); 192 193 /* enable hid set */ 194 acpihid_eval(sc, ACPIHID_FUNC_HDSM, 1, NULL); 195 196 if (sc->sc_5_button) { 197 acpihid_button_array_enable(sc, 1); 198 199 if (acpihid_eval(sc, ACPIHID_FUNC_BTNL, 0, NULL) == 0) 200 printf(", 5 button array"); 201 else 202 printf(", failed enabling HID power button"); 203 } 204 205 printf("\n"); 206 } 207 208 void 209 acpihid_init_dsm(struct acpihid_softc *sc) 210 { 211 struct aml_value cmd[4], res; 212 213 sc->sc_dsm_fn_mask = 0; 214 215 if (!aml_searchname(sc->sc_devnode, "_DSM")) { 216 DPRINTF(("%s: no _DSM support\n", sc->sc_dev.dv_xname)); 217 return; 218 } 219 220 bzero(&cmd, sizeof(cmd)); 221 cmd[0].type = AML_OBJTYPE_BUFFER; 222 cmd[0].v_buffer = (uint8_t *)&acpihid_guid; 223 cmd[0].length = sizeof(acpihid_guid); 224 /* rev */ 225 cmd[1].type = AML_OBJTYPE_INTEGER; 226 cmd[1].v_integer = 1; 227 cmd[1].length = 1; 228 /* func */ 229 cmd[2].type = AML_OBJTYPE_INTEGER; 230 cmd[2].v_integer = 0; 231 cmd[2].length = 1; 232 /* not used */ 233 cmd[3].type = AML_OBJTYPE_BUFFER; 234 cmd[3].length = 0; 235 236 if (aml_evalname(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd, 237 &res)) { 238 printf("%s: eval of _DSM at %s failed\n", 239 sc->sc_dev.dv_xname, aml_nodename(sc->sc_devnode)); 240 return; 241 } 242 243 if (res.type != AML_OBJTYPE_BUFFER) { 244 printf("%s: bad _DSM result at %s: %d\n", sc->sc_dev.dv_xname, 245 aml_nodename(sc->sc_devnode), res.type); 246 aml_freevalue(&res); 247 return; 248 } 249 250 sc->sc_dsm_fn_mask = *res.v_buffer; 251 DPRINTF(("%s: _DSM function mask 0x%x\n", sc->sc_dev.dv_xname, 252 sc->sc_dsm_fn_mask)); 253 254 aml_freevalue(&res); 255 } 256 257 int 258 acpihid_eval(struct acpihid_softc *sc, int idx, int64_t arg, int64_t *ret) 259 { 260 struct aml_value cmd[4], pkg, *ppkg; 261 int64_t tret; 262 const char *dsm_func; 263 264 if (idx <= ACPIHID_FUNC_INVALID || idx >= ACPIHID_FUNC_MAX) { 265 printf("%s: _DSM func index %d out of bounds\n", 266 sc->sc_dev.dv_xname, idx); 267 return 1; 268 } 269 270 dsm_func = acpihid_dsm_funcs[idx]; 271 272 DPRINTF(("%s: executing _DSM %s\n", sc->sc_dev.dv_xname, dsm_func)); 273 274 if (!(sc->sc_dsm_fn_mask & idx)) { 275 DPRINTF(("%s: _DSM mask does not support %s (%d), executing " 276 "directly\n", sc->sc_dev.dv_xname, dsm_func, idx)); 277 goto eval_direct; 278 } 279 280 bzero(&pkg, sizeof(pkg)); 281 pkg.type = AML_OBJTYPE_INTEGER; 282 pkg.v_integer = arg; 283 pkg.length = 1; 284 ppkg = &pkg; 285 286 bzero(&cmd, sizeof(cmd)); 287 cmd[0].type = AML_OBJTYPE_BUFFER; 288 cmd[0].v_buffer = (uint8_t *)&acpihid_guid; 289 cmd[0].length = sizeof(acpihid_guid); 290 /* rev */ 291 cmd[1].type = AML_OBJTYPE_INTEGER; 292 cmd[1].v_integer = 1; 293 cmd[1].length = 1; 294 /* func */ 295 cmd[2].type = AML_OBJTYPE_INTEGER; 296 cmd[2].v_integer = idx; 297 cmd[2].length = 1; 298 /* arg */ 299 cmd[3].type = AML_OBJTYPE_PACKAGE; 300 cmd[3].length = 1; 301 cmd[3].v_package = &ppkg; 302 303 if (aml_evalinteger(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd, 304 &tret)) { 305 DPRINTF(("%s: _DSM %s failed\n", sc->sc_dev.dv_xname, 306 dsm_func)); 307 return 1; 308 } 309 310 DPRINTF(("%s: _DSM eval of %s succeeded\n", sc->sc_dev.dv_xname, 311 dsm_func)); 312 313 if (ret != NULL) 314 *ret = tret; 315 316 return 0; 317 318 eval_direct: 319 cmd[0].type = AML_OBJTYPE_INTEGER; 320 cmd[0].v_integer = arg; 321 cmd[0].length = 1; 322 323 if (aml_evalinteger(acpi_softc, sc->sc_devnode, dsm_func, 1, cmd, 324 &tret) != 0) { 325 printf("%s: exec of %s failed\n", sc->sc_dev.dv_xname, 326 dsm_func); 327 return 1; 328 } 329 330 if (ret != NULL) 331 *ret = tret; 332 333 return 0; 334 } 335 336 int 337 acpihid_button_array_enable(struct acpihid_softc *sc, int enable) 338 { 339 int64_t cap; 340 341 if (aml_evalinteger(acpi_softc, sc->sc_devnode, "BTNC", 0, NULL, 342 &cap) != 0) { 343 printf("%s: failed getting button array capability\n", 344 sc->sc_dev.dv_xname); 345 return 1; 346 } 347 348 if (acpihid_eval(sc, ACPIHID_FUNC_BTNE, enable ? cap : 1, NULL) != 0) { 349 printf("%s: failed enabling button array\n", 350 sc->sc_dev.dv_xname); 351 return 1; 352 } 353 354 return 0; 355 } 356 357 int 358 acpihid_notify(struct aml_node *node, int notify_type, void *arg) 359 { 360 #ifdef ACPIHID_DEBUG 361 struct acpihid_softc *sc = arg; 362 363 DPRINTF(("%s: %s: %.2x\n", sc->sc_dev.dv_xname, __func__, 364 notify_type)); 365 #endif 366 367 switch (notify_type) { 368 case 0xc2: /* left meta press */ 369 break; 370 case 0xc3: /* left meta release */ 371 break; 372 case 0xc4: /* volume up press */ 373 #if NAUDIO > 0 && NWSKBD > 0 374 wskbd_set_mixervolume(1, 1); 375 #endif 376 break; 377 case 0xc5: /* volume up release */ 378 break; 379 case 0xc6: /* volume down press */ 380 #if NAUDIO > 0 && NWSKBD > 0 381 wskbd_set_mixervolume(-1, 1); 382 #endif 383 break; 384 case 0xc7: /* volume down release */ 385 break; 386 case 0xc8: /* rotate lock toggle press */ 387 break; 388 case 0xc9: /* rotate lock toggle release */ 389 break; 390 case 0xce: /* power button press */ 391 break; 392 case 0xcf: /* power button release */ 393 break; 394 default: 395 DPRINTF(("%s: unhandled button 0x%x\n", sc->sc_dev.dv_xname, 396 notify_type)); 397 } 398 399 return 0; 400 } 401