#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libnvpair.h>
#include <sys/sensors.h>
#include <sys/fm/protocol.h>
#include <fm/topo_mod.h>
#define TOPO_METH_TOPO_SENSOR_SCALAR "topo_sensor_scalar_reading"
#define TOPO_METH_TOPO_SENSOR_SCALAR_DESC "Kernel Sensor Scalar Reading"
#define TOPO_METH_TOPO_SENSOR_SCALAR_VERSION 0
static int
topo_sensor_scalar_read(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
nvlist_t *in, nvlist_t **out)
{
int fd = -1, ret;
nvlist_t *args, *nvl;
char *path;
sensor_ioctl_scalar_t scalar;
double value;
if (vers != TOPO_METH_TOPO_SENSOR_SCALAR_VERSION) {
return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
}
if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 ||
nvlist_lookup_string(args, TOPO_IO_DEV_PATH, &path) != 0) {
topo_mod_dprintf(mod, "failed to lookup sensor path from "
"property %s", TOPO_IO_DEV_PATH);
return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
}
if ((fd = open(path, O_RDONLY)) < 0) {
topo_mod_dprintf(mod, "failed to open sensor path %s: %s",
path, strerror(errno));
return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
}
(void) memset(&scalar, '\0', sizeof (scalar));
if (ioctl(fd, SENSOR_IOCTL_SCALAR, &scalar) != 0) {
topo_mod_dprintf(mod, "failed to read sensor %s: %s", path,
strerror(errno));
ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
goto out;
}
value = (double)scalar.sis_value;
if (scalar.sis_gran > 1) {
value /= (double)scalar.sis_gran;
} else if (scalar.sis_gran < -1) {
value *= (double)labs(scalar.sis_gran);
}
if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) {
topo_mod_dprintf(mod, "failed to allocate output nvl");
ret = topo_mod_seterrno(mod, EMOD_NOMEM);
goto out;
}
if (nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_SENSOR_READING) !=
0 ||
nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 ||
nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, value) != 0) {
topo_mod_dprintf(mod, "failed to add members to output "
"sensor nvlist");
nvlist_free(nvl);
ret = topo_mod_seterrno(mod, EMOD_NOMEM);
goto out;
}
*out = nvl;
ret = 0;
out:
if (fd >= 0) {
(void) close(fd);
}
return (ret);
}
static const topo_method_t topo_sensor_scalar_fac_methods[] = {
{ TOPO_METH_TOPO_SENSOR_SCALAR, TOPO_METH_TOPO_SENSOR_SCALAR_DESC,
TOPO_METH_TOPO_SENSOR_SCALAR_VERSION, TOPO_STABILITY_INTERNAL,
topo_sensor_scalar_read },
{ NULL }
};
static topo_sensor_unit_t
topo_sensor_units(const sensor_ioctl_scalar_t *scalar)
{
switch (scalar->sis_unit) {
case SENSOR_UNIT_CELSIUS:
return (TOPO_SENSOR_UNITS_DEGREES_C);
case SENSOR_UNIT_FAHRENHEIT:
return (TOPO_SENSOR_UNITS_DEGREES_F);
case SENSOR_UNIT_KELVIN:
return (TOPO_SENSOR_UNITS_DEGREES_K);
case SENSOR_UNIT_VOLTS:
return (TOPO_SENSOR_UNITS_VOLTS);
case SENSOR_UNIT_AMPS:
return (TOPO_SENSOR_UNITS_AMPS);
case SENSOR_UNIT_NONE:
return (TOPO_SENSOR_UNITS_NONE);
default:
return (TOPO_SENSOR_UNITS_UNSPECIFIED);
}
}
int
topo_sensor_create_scalar_sensor(topo_mod_t *mod, tnode_t *pnode,
const char *path, const char *fname)
{
int fd, ret, err;
sensor_ioctl_kind_t sik;
sensor_ioctl_scalar_t scalar;
uint32_t topo_type;
tnode_t *fnode = NULL;
topo_pgroup_info_t pgi;
nvlist_t *reader_arg = NULL;
topo_mod_dprintf(mod, "attempting to create sensor for %s at %s",
topo_node_name(pnode), path);
(void) memset(&sik, '\0', sizeof (sik));
(void) memset(&scalar, '\0', sizeof (scalar));
if ((fd = open(path, O_RDONLY)) < 0) {
topo_mod_dprintf(mod, "failed to open sensor path %s: %s",
path, strerror(errno));
if (errno == ENOENT) {
return (0);
}
return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
}
if (ioctl(fd, SENSOR_IOCTL_KIND, &sik) != 0) {
topo_mod_dprintf(mod, "failed to verify sensor kind for sensor "
"%s: %s", path, strerror(errno));
ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
goto out;
}
switch (sik.sik_kind) {
case SENSOR_KIND_TEMPERATURE:
topo_type = TOPO_SENSOR_TYPE_TEMP;
break;
case SENSOR_KIND_VOLTAGE:
topo_type = TOPO_SENSOR_TYPE_VOLTAGE;
break;
case SENSOR_KIND_CURRENT:
topo_type = TOPO_SENSOR_TYPE_CURRENT;
break;
case SENSOR_KIND_SYNTHETIC:
topo_type = TOPO_SENSOR_TYPE_SYNTHETIC;
break;
default:
topo_mod_dprintf(mod, "unknown sensor kind for %s, found 0x%"
PRIx64, path, sik.sik_kind);
ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
goto out;
}
if (ioctl(fd, SENSOR_IOCTL_SCALAR, &scalar) != 0) {
topo_mod_dprintf(mod, "failed to read scalar sensor %s: %s",
path, strerror(errno));
ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
goto out;
}
(void) close(fd);
fd = -1;
if ((fnode = topo_node_facbind(mod, pnode, fname,
TOPO_FAC_TYPE_SENSOR)) == NULL) {
topo_mod_dprintf(mod, "failed to bind sensor facility "
"node to %s: %d", path, topo_mod_errno(mod));
ret = -1;
goto out;
}
pgi.tpi_name = TOPO_PGROUP_FACILITY;
pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
pgi.tpi_version = 1;
if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
topo_mod_dprintf(mod, "failed to create facility pgroup: %s",
topo_strerror(err));
ret = topo_mod_seterrno(mod, err);
goto out;
}
if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY,
TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
TOPO_SENSOR_CLASS_THRESHOLD, &err) != 0 ||
topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, topo_type, &err) != 0 ||
topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, topo_sensor_units(&scalar),
&err) != 0) {
topo_mod_dprintf(mod, "failed to set properties for sensor "
"%s: %s", path, topo_strerror(err));
ret = topo_mod_seterrno(mod, err);
goto out;
}
if (topo_method_register(mod, fnode, topo_sensor_scalar_fac_methods) <
0) {
topo_mod_dprintf(mod, "failed to register reading methods on "
"%s", path);
ret = -1;
goto out;
}
if (topo_mod_nvalloc(mod, &reader_arg, NV_UNIQUE_NAME) != 0 ||
nvlist_add_string(reader_arg, TOPO_IO_DEV_PATH, path) != 0) {
topo_mod_dprintf(mod, "Failed to set up reader argument nvl");
ret = topo_mod_seterrno(mod, EMOD_NOMEM);
goto out;
}
if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, TOPO_METH_TOPO_SENSOR_SCALAR,
reader_arg, &err) != 0) {
topo_mod_dprintf(mod, "failed to set argument for sensor %s: "
"%s", path, topo_strerror(err));
ret = topo_mod_seterrno(mod, err);
goto out;
}
topo_mod_dprintf(mod, "created sensor at %s", path);
nvlist_free(reader_arg);
return (0);
out:
if (fd >= 0) {
(void) close(fd);
}
topo_node_unbind(fnode);
nvlist_free(reader_arg);
return (ret);
}