#include <sys/param.h>
#include <sys/signalvar.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <machine/apmvar.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpidev.h>
#include <dev/acpi/amltypes.h>
#include <dev/acpi/dsdt.h>
#include "audio.h"
#include "wskbd.h"
#ifdef INTHID_DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif
struct inthid_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct acpi_softc *sc_acpi;
struct aml_node *sc_devnode;
int sc_5_button;
uint32_t sc_dsm_fn_mask;
};
enum {
INTHID_FUNC_INVALID,
INTHID_FUNC_BTNL,
INTHID_FUNC_HDMM,
INTHID_FUNC_HDSM,
INTHID_FUNC_HDEM,
INTHID_FUNC_BTNS,
INTHID_FUNC_BTNE,
INTHID_FUNC_HEBC_V1,
INTHID_FUNC_VGBS,
INTHID_FUNC_HEBC_V2,
INTHID_FUNC_MAX,
};
static const char *inthid_dsm_funcs[] = {
NULL,
"BTNL",
"HDMM",
"HDSM",
"HDEM",
"BTNS",
"BTNE",
"HEBC",
"VGBS",
"HEBC",
};
int inthid_match(struct device *, void *, void *);
void inthid_attach(struct device *, struct device *, void *);
void inthid_init_dsm(struct inthid_softc *);
int inthid_button_array_enable(struct inthid_softc *, int);
int inthid_eval(struct inthid_softc *, int, int64_t, int64_t *);
int inthid_notify(struct aml_node *, int, void *);
#if NAUDIO > 0 && NWSKBD > 0
extern int wskbd_set_mixervolume(long, long);
#endif
const struct cfattach inthid_ca = {
sizeof(struct inthid_softc),
inthid_match,
inthid_attach,
NULL,
NULL,
};
struct cfdriver inthid_cd = {
NULL, "inthid", DV_DULL
};
const char *inthid_hids[] = {
"INT33D5",
NULL
};
static uint8_t inthid_guid[] = {
0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40,
0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54,
};
int
inthid_match(struct device *parent, void *match, void *aux)
{
struct acpi_attach_args *aa = aux;
struct cfdata *cf = match;
return (acpi_matchhids(aa, inthid_hids, cf->cf_driver->cd_name));
}
void
inthid_attach(struct device *parent, struct device *self, void *aux)
{
struct inthid_softc *sc = (struct inthid_softc *)self;
struct acpi_attach_args *aa = aux;
uint64_t val;
sc->sc_acpi = (struct acpi_softc *)parent;
sc->sc_devnode = aa->aaa_node;
printf(": %s", sc->sc_devnode->name);
inthid_init_dsm(sc);
if (inthid_eval(sc, INTHID_FUNC_HDMM, 0, &val) != 0) {
printf(", failed reading mode\n");
return;
} else if (val != 0) {
printf(", unknown mode %lld\n", val);
return;
}
if ((inthid_eval(sc, INTHID_FUNC_HEBC_V2, 0, &val) == 0 &&
(val & 0x60000)) ||
(inthid_eval(sc, INTHID_FUNC_HEBC_V1, 0, &val) == 0 &&
(val & 0x20000)))
sc->sc_5_button = 1;
aml_register_notify(sc->sc_devnode, aa->aaa_dev, inthid_notify,
sc, ACPIDEV_NOPOLL);
inthid_eval(sc, INTHID_FUNC_HDSM, 1, NULL);
if (sc->sc_5_button) {
inthid_button_array_enable(sc, 1);
if (inthid_eval(sc, INTHID_FUNC_BTNL, 0, NULL) == 0)
printf(", 5 button array");
else
printf(", failed enabling HID power button");
}
printf("\n");
}
void
inthid_init_dsm(struct inthid_softc *sc)
{
struct aml_value cmd[4], res;
sc->sc_dsm_fn_mask = 0;
if (!aml_searchname(sc->sc_devnode, "_DSM")) {
DPRINTF(("%s: no _DSM support\n", sc->sc_dev.dv_xname));
return;
}
bzero(&cmd, sizeof(cmd));
cmd[0].type = AML_OBJTYPE_BUFFER;
cmd[0].v_buffer = (uint8_t *)&inthid_guid;
cmd[0].length = sizeof(inthid_guid);
cmd[1].type = AML_OBJTYPE_INTEGER;
cmd[1].v_integer = 1;
cmd[1].length = 1;
cmd[2].type = AML_OBJTYPE_INTEGER;
cmd[2].v_integer = 0;
cmd[2].length = 1;
cmd[3].type = AML_OBJTYPE_BUFFER;
cmd[3].length = 0;
if (aml_evalname(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd,
&res)) {
printf("%s: eval of _DSM at %s failed\n",
sc->sc_dev.dv_xname, aml_nodename(sc->sc_devnode));
return;
}
if (res.type != AML_OBJTYPE_BUFFER) {
printf("%s: bad _DSM result at %s: %d\n", sc->sc_dev.dv_xname,
aml_nodename(sc->sc_devnode), res.type);
aml_freevalue(&res);
return;
}
sc->sc_dsm_fn_mask = *res.v_buffer;
DPRINTF(("%s: _DSM function mask 0x%x\n", sc->sc_dev.dv_xname,
sc->sc_dsm_fn_mask));
aml_freevalue(&res);
}
int
inthid_eval(struct inthid_softc *sc, int idx, int64_t arg, int64_t *ret)
{
struct aml_value cmd[4], pkg, *ppkg;
int64_t tret;
const char *dsm_func;
if (idx <= INTHID_FUNC_INVALID || idx >= INTHID_FUNC_MAX) {
printf("%s: _DSM func index %d out of bounds\n",
sc->sc_dev.dv_xname, idx);
return 1;
}
dsm_func = inthid_dsm_funcs[idx];
DPRINTF(("%s: executing _DSM %s\n", sc->sc_dev.dv_xname, dsm_func));
if (!(sc->sc_dsm_fn_mask & idx)) {
DPRINTF(("%s: _DSM mask does not support %s (%d), executing "
"directly\n", sc->sc_dev.dv_xname, dsm_func, idx));
goto eval_direct;
}
bzero(&pkg, sizeof(pkg));
pkg.type = AML_OBJTYPE_INTEGER;
pkg.v_integer = arg;
pkg.length = 1;
ppkg = &pkg;
bzero(&cmd, sizeof(cmd));
cmd[0].type = AML_OBJTYPE_BUFFER;
cmd[0].v_buffer = (uint8_t *)&inthid_guid;
cmd[0].length = sizeof(inthid_guid);
cmd[1].type = AML_OBJTYPE_INTEGER;
cmd[1].v_integer = 1;
cmd[1].length = 1;
cmd[2].type = AML_OBJTYPE_INTEGER;
cmd[2].v_integer = idx;
cmd[2].length = 1;
cmd[3].type = AML_OBJTYPE_PACKAGE;
cmd[3].length = 1;
cmd[3].v_package = &ppkg;
if (aml_evalinteger(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd,
&tret)) {
DPRINTF(("%s: _DSM %s failed\n", sc->sc_dev.dv_xname,
dsm_func));
return 1;
}
DPRINTF(("%s: _DSM eval of %s succeeded\n", sc->sc_dev.dv_xname,
dsm_func));
if (ret != NULL)
*ret = tret;
return 0;
eval_direct:
cmd[0].type = AML_OBJTYPE_INTEGER;
cmd[0].v_integer = arg;
cmd[0].length = 1;
if (aml_evalinteger(acpi_softc, sc->sc_devnode, dsm_func, 1, cmd,
&tret) != 0) {
printf("%s: exec of %s failed\n", sc->sc_dev.dv_xname,
dsm_func);
return 1;
}
if (ret != NULL)
*ret = tret;
return 0;
}
int
inthid_button_array_enable(struct inthid_softc *sc, int enable)
{
int64_t cap;
if (aml_evalinteger(acpi_softc, sc->sc_devnode, "BTNC", 0, NULL,
&cap) != 0) {
printf("%s: failed getting button array capability\n",
sc->sc_dev.dv_xname);
return 1;
}
if (inthid_eval(sc, INTHID_FUNC_BTNE, enable ? cap : 1, NULL) != 0) {
printf("%s: failed enabling button array\n",
sc->sc_dev.dv_xname);
return 1;
}
return 0;
}
int
inthid_notify(struct aml_node *node, int notify_type, void *arg)
{
#ifdef INTHID_DEBUG
struct inthid_softc *sc = arg;
DPRINTF(("%s: %s: %.2x\n", sc->sc_dev.dv_xname, __func__,
notify_type));
#endif
switch (notify_type) {
case 0xc2:
break;
case 0xc3:
break;
case 0xc4:
#if NAUDIO > 0 && NWSKBD > 0
wskbd_set_mixervolume(1, 1);
#endif
break;
case 0xc5:
break;
case 0xc6:
#if NAUDIO > 0 && NWSKBD > 0
wskbd_set_mixervolume(-1, 1);
#endif
break;
case 0xc7:
break;
case 0xc8:
break;
case 0xc9:
break;
case 0xce:
break;
case 0xcf:
break;
default:
DPRINTF(("%s: unhandled button 0x%x\n", sc->sc_dev.dv_xname,
notify_type));
}
return 0;
}