#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/sensors.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>
#define ATK_ID_MUX_HWMON 0x00000006
#define ATK_CLASS(x) (((x) >> 24) & 0xff)
#define ATK_CLASS_FREQ_CTL 3
#define ATK_CLASS_FAN_CTL 4
#define ATK_CLASS_HWMON 6
#define ATK_CLASS_MGMT 17
#define ATK_TYPE(x) (((x) >> 16) & 0xff)
#define ATK_TYPE_VOLT 2
#define ATK_TYPE_TEMP 3
#define ATK_TYPE_FAN 4
#define AIBS_MORE_SENSORS
struct aibs_sensor {
struct ksensor s;
int64_t i;
int64_t l;
int64_t h;
SIMPLEQ_ENTRY(aibs_sensor) entry;
};
struct aibs_softc {
struct device sc_dev;
struct acpi_softc *sc_acpi;
struct aml_node *sc_devnode;
struct aml_node *sc_ggrpnode;
struct aml_node *sc_gitmnode;
struct aml_node *sc_sitmnode;
struct aml_node *sc_rtmpnode;
struct aml_node *sc_rvltnode;
struct aml_node *sc_rfannode;
SIMPLEQ_HEAD(, aibs_sensor) sc_sensorlist;
struct ksensordev sc_sensordev;
int sc_mode;
};
struct aibs_cmd_buffer {
uint32_t id;
uint32_t param1;
uint32_t param2;
};
struct aibs_ret_buffer {
uint32_t flags;
uint32_t value;
};
int aibs_match(struct device *, void *, void *);
void aibs_attach(struct device *, struct device *, void *);
int aibs_notify(struct aml_node *, int, void *);
void aibs_refresh(void *);
void aibs_attach_sif(struct aibs_softc *, enum sensor_type);
void aibs_attach_new(struct aibs_softc *);
void aibs_add_sensor(struct aibs_softc *, const char *);
void aibs_refresh_r(struct aibs_softc *, struct aibs_sensor *);
int aibs_getvalue(struct aibs_softc *, int64_t, int64_t *);
int aibs_getpack(struct aibs_softc *, struct aml_node *, int64_t,
struct aml_value *);
void aibs_probe(struct aibs_softc *);
int aibs_find_cb(struct aml_node *, void *);
const struct cfattach aibs_ca = {
sizeof(struct aibs_softc), aibs_match, aibs_attach
};
struct cfdriver aibs_cd = {
NULL, "aibs", DV_DULL
};
static const char* aibs_hids[] = {
"ATK0110",
NULL
};
int
aibs_match(struct device *parent, void *match, void *aux)
{
struct acpi_attach_args *aa = aux;
struct cfdata *cf = match;
return acpi_matchhids(aa, aibs_hids, cf->cf_driver->cd_name);
}
void
aibs_attach(struct device *parent, struct device *self, void *aux)
{
struct aibs_softc *sc = (struct aibs_softc *)self;
struct acpi_attach_args *aa = aux;
sc->sc_acpi = (struct acpi_softc *)parent;
sc->sc_devnode = aa->aaa_node;
strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sc_sensordev.xname));
SIMPLEQ_INIT(&sc->sc_sensorlist);
aibs_probe(sc);
printf("\n");
if (sc->sc_mode)
aibs_attach_new(sc);
else {
aibs_attach_sif(sc, SENSOR_TEMP);
aibs_attach_sif(sc, SENSOR_FANRPM);
aibs_attach_sif(sc, SENSOR_VOLTS_DC);
}
if (sc->sc_sensordev.sensors_count == 0) {
printf("%s: no sensors found\n", DEVNAME(sc));
return;
}
sensordev_install(&sc->sc_sensordev);
aml_register_notify(sc->sc_devnode, aa->aaa_dev,
aibs_notify, sc, ACPIDEV_POLL);
}
void
aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
{
struct aml_value res;
struct aml_value **v;
int i, n;
char name[] = "?SIF";
switch (st) {
case SENSOR_TEMP:
name[0] = 'T';
break;
case SENSOR_FANRPM:
name[0] = 'F';
break;
case SENSOR_VOLTS_DC:
name[0] = 'V';
break;
default:
return;
}
if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) {
printf("%s: %s not found\n", DEVNAME(sc), name);
aml_freevalue(&res);
return;
}
if (res.type != AML_OBJTYPE_PACKAGE) {
printf("%s: %s: not a package\n", DEVNAME(sc), name);
aml_freevalue(&res);
return;
}
v = res.v_package;
if (v[0]->type != AML_OBJTYPE_INTEGER) {
printf("%s: %s[0]: invalid type\n", DEVNAME(sc), name);
aml_freevalue(&res);
return;
}
n = v[0]->v_integer;
if (res.length - 1 < n) {
printf("%s: %s: invalid package\n", DEVNAME(sc), name);
aml_freevalue(&res);
return;
} else if (res.length - 1 > n) {
printf("%s: %s: malformed package: %i/%i",
DEVNAME(sc), name, n, res.length - 1);
#ifdef AIBS_MORE_SENSORS
n = res.length - 1;
#endif
printf(", assume %i\n", n);
}
if (n < 1) {
printf("%s: %s: no members in the package\n",
DEVNAME(sc), name);
aml_freevalue(&res);
return;
}
for (i = 0, v++; i < n; i++, v++) {
if(v[0]->type != AML_OBJTYPE_NAMEREF) {
printf("%s: %s: %i: not a nameref: %i type\n",
DEVNAME(sc), name, i, v[0]->type);
continue;
}
aibs_add_sensor(sc, aml_getname(v[0]->v_nameref));
}
aml_freevalue(&res);
}
void
aibs_attach_new(struct aibs_softc *sc)
{
struct aml_value res;
int i;
if (aibs_getpack(sc, sc->sc_ggrpnode, ATK_ID_MUX_HWMON, &res)) {
printf("%s: GGRP: sensor enumeration failed\n", DEVNAME(sc));
return;
}
for (i = 0; i < res.length; i++) {
struct aml_value *r;
r = res.v_package[i];
if (r->type != AML_OBJTYPE_STRING) {
printf("%s: %s: %i: not a string (type %i)\n",
DEVNAME(sc), "GGRP", i, r->type);
continue;
}
aibs_add_sensor(sc, r->v_string);
}
aml_freevalue(&res);
}
void
aibs_add_sensor(struct aibs_softc *sc, const char *name)
{
struct aml_value ri;
struct aibs_sensor *as;
int len, lim1, lim2, ena;
if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
0, NULL, &ri)) {
printf("%s: aibs_add_sensor: %s not found\n",
DEVNAME(sc), name);
aml_freevalue(&ri);
return;
}
if (ri.type != AML_OBJTYPE_PACKAGE) {
printf("%s: aibs_add_sensor: %s: not a package\n",
DEVNAME(sc), name);
aml_freevalue(&ri);
return;
}
if (sc->sc_mode) {
len = 7;
lim1 = 4;
lim2 = 5;
ena = 6;
} else {
len = 5;
lim1 = 2;
lim2 = 3;
ena = 4;
}
if (ri.length != len ||
ri.v_package[0]->type != AML_OBJTYPE_INTEGER ||
ri.v_package[1]->type != AML_OBJTYPE_STRING ||
ri.v_package[lim1]->type != AML_OBJTYPE_INTEGER ||
ri.v_package[lim2]->type != AML_OBJTYPE_INTEGER ||
ri.v_package[ena]->type != AML_OBJTYPE_INTEGER) {
printf("%s: aibs_add_sensor: %s: invalid package\n",
DEVNAME(sc), name);
aml_freevalue(&ri);
return;
}
as = malloc(sizeof(*as), M_DEVBUF, M_NOWAIT | M_ZERO);
if (!as) {
printf("%s: aibs_add_sensor: %s: failed to allocate sensor\n",
DEVNAME(sc), name);
aml_freevalue(&ri);
return;
}
as->i = ri.v_package[0]->v_integer;
switch (ATK_TYPE(as->i)) {
case ATK_TYPE_VOLT:
as->s.type = SENSOR_VOLTS_DC;
break;
case ATK_TYPE_TEMP:
as->s.type = SENSOR_TEMP;
break;
case ATK_TYPE_FAN:
as->s.type = SENSOR_FANRPM;
break;
default:
printf("%s: aibs_add_sensor: %s: unknown sensor type %llx\n",
DEVNAME(sc), name, ri.v_package[0]->v_integer);
aml_freevalue(&ri);
free(as, M_DEVBUF, sizeof(*as));
return;
}
strlcpy(as->s.desc, ri.v_package[1]->v_string,
sizeof(as->s.desc));
as->l = ri.v_package[lim1]->v_integer;
if (sc->sc_mode)
as->h = as->l + ri.v_package[lim2]->v_integer;
else
as->h = ri.v_package[lim2]->v_integer;
#ifdef AIBS_VERBOSE
printf("%s: %4s: %s 0x%08llx %5lli / %5lli 0x%llx\n",
DEVNAME(sc), name, as->s.desc, as->i, as->l, as->h,
ri.v_package[ena]->v_integer);
#endif
SIMPLEQ_INSERT_TAIL(&sc->sc_sensorlist, as, entry);
sensor_attach(&sc->sc_sensordev, &as->s);
aml_freevalue(&ri);
return;
}
void
aibs_refresh(void *arg)
{
struct aibs_softc *sc = arg;
struct aibs_sensor *as;
SIMPLEQ_FOREACH(as, &sc->sc_sensorlist, entry)
aibs_refresh_r(sc, as);
}
void
aibs_refresh_r(struct aibs_softc *sc, struct aibs_sensor *as)
{
struct ksensor *s = &as->s;
int64_t v;
const int64_t l = as->l, h = as->h;
if (aibs_getvalue(sc, as->i, &v)) {
s->flags |= SENSOR_FINVALID;
return;
}
switch (s->type) {
case SENSOR_TEMP:
s->value = v * 100 * 1000 + 273150000;
if (v == 0) {
s->status = SENSOR_S_UNKNOWN;
s->flags |= SENSOR_FINVALID;
} else {
if (v > h)
s->status = SENSOR_S_CRIT;
else if (v > l)
s->status = SENSOR_S_WARN;
else
s->status = SENSOR_S_OK;
s->flags &= ~SENSOR_FINVALID;
}
break;
case SENSOR_FANRPM:
s->value = v;
if ((l != 0 && l < v && v < h) ||
(l == 0 && v > h))
s->status = SENSOR_S_OK;
else
s->status = SENSOR_S_WARN;
s->flags &= ~SENSOR_FINVALID;
break;
case SENSOR_VOLTS_DC:
s->value = v * 1000;
if (l < v && v < h)
s->status = SENSOR_S_OK;
else
s->status = SENSOR_S_WARN;
s->flags &= ~SENSOR_FINVALID;
break;
default:
break;
}
}
int
aibs_getvalue(struct aibs_softc *sc, int64_t i, int64_t *v)
{
struct aml_node *n = sc->sc_gitmnode;
struct aml_value req, res;
struct aibs_cmd_buffer cmd;
struct aibs_ret_buffer ret;
enum aml_objecttype type;
if (sc->sc_mode) {
cmd.id = i;
cmd.param1 = 0;
cmd.param2 = 0;
type = req.type = AML_OBJTYPE_BUFFER;
req.v_buffer = (uint8_t *)&cmd;
req.length = sizeof(cmd);
} else {
switch (ATK_TYPE(i)) {
case ATK_TYPE_TEMP:
n = sc->sc_rtmpnode;
break;
case ATK_TYPE_FAN:
n = sc->sc_rfannode;
break;
case ATK_TYPE_VOLT:
n = sc->sc_rvltnode;
break;
default:
return (-1);
}
type = req.type = AML_OBJTYPE_INTEGER;
req.v_integer = i;
}
if (aml_evalnode(sc->sc_acpi, n, 1, &req, &res)) {
dprintf("%s: %s: %lld: evaluation failed\n",
DEVNAME(sc), n->name, i);
aml_freevalue(&res);
return (-1);
}
if (res.type != type) {
dprintf("%s: %s: %lld: not an integer: type %i\n",
DEVNAME(sc), n->name, i, res.type);
aml_freevalue(&res);
return (-1);
}
if (sc->sc_mode) {
if (res.length < sizeof(ret)) {
dprintf("%s: %s: %lld: result buffer too small\n",
DEVNAME(sc), n->name, i);
aml_freevalue(&res);
return (-1);
}
memcpy(&ret, res.v_buffer, sizeof(ret));
if (ret.flags == 0) {
dprintf("%s: %s: %lld: bad flags in result\n",
DEVNAME(sc), n->name, i);
aml_freevalue(&res);
return (-1);
}
*v = ret.value;
} else {
*v = res.v_integer;
}
aml_freevalue(&res);
return (0);
}
int
aibs_getpack(struct aibs_softc *sc, struct aml_node *n, int64_t i,
struct aml_value *res)
{
struct aml_value req;
req.type = AML_OBJTYPE_INTEGER;
req.v_integer = i;
if (aml_evalnode(sc->sc_acpi, n, 1, &req, res)) {
dprintf("%s: %s: %lld: evaluation failed\n",
DEVNAME(sc), n->name, i);
aml_freevalue(res);
return (-1);
}
if (res->type != AML_OBJTYPE_PACKAGE) {
dprintf("%s: %s: %lld: not a package: type %i\n",
DEVNAME(sc), n->name, i, res->type);
aml_freevalue(res);
return (-1);
}
return (0);
}
void
aibs_probe(struct aibs_softc *sc)
{
aml_find_node(sc->sc_devnode, "RTMP", aibs_find_cb, &sc->sc_rtmpnode);
aml_find_node(sc->sc_devnode, "RVLT", aibs_find_cb, &sc->sc_rvltnode);
aml_find_node(sc->sc_devnode, "RFAN", aibs_find_cb, &sc->sc_rfannode);
aml_find_node(sc->sc_devnode, "GGRP", aibs_find_cb, &sc->sc_ggrpnode);
aml_find_node(sc->sc_devnode, "GITM", aibs_find_cb, &sc->sc_gitmnode);
aml_find_node(sc->sc_devnode, "SITM", aibs_find_cb, &sc->sc_sitmnode);
if (sc->sc_ggrpnode && sc->sc_gitmnode && sc->sc_sitmnode &&
!sc->sc_rtmpnode && !sc->sc_rvltnode && !sc->sc_rfannode)
sc->sc_mode = 1;
}
int
aibs_find_cb(struct aml_node *node, void *arg)
{
struct aml_node **np = arg;
printf(" %s", node->name);
*np = node;
return (1);
}
int
aibs_notify(struct aml_node *node, int notify_type, void *arg)
{
struct aibs_softc *sc = arg;
if (notify_type == 0x00) {
aibs_refresh(sc);
}
return (0);
}