1f2dbfd3Robert Mustacchi/*
2f2dbfd3Robert Mustacchi * This file and its contents are supplied under the terms of the
3f2dbfd3Robert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4f2dbfd3Robert Mustacchi * You may only use this file in accordance with the terms of version
5f2dbfd3Robert Mustacchi * 1.0 of the CDDL.
6f2dbfd3Robert Mustacchi *
7f2dbfd3Robert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8f2dbfd3Robert Mustacchi * source.  A copy of the CDDL is also available via the Internet at
9f2dbfd3Robert Mustacchi * http://www.illumos.org/license/CDDL.
10f2dbfd3Robert Mustacchi */
11f2dbfd3Robert Mustacchi
12f2dbfd3Robert Mustacchi/*
13f2dbfd3Robert Mustacchi * Copyright 2019, Joyent, Inc.
141045e13Robert Mustacchi * Copyright 2020 Oxide Computer Company
15f2dbfd3Robert Mustacchi */
16f2dbfd3Robert Mustacchi
17f2dbfd3Robert Mustacchi/*
18f2dbfd3Robert Mustacchi * This file provides routines to interact with the kernel sensor framework.
19f2dbfd3Robert Mustacchi * Currently, modules that require interacting with a kernel sensor need to
20f2dbfd3Robert Mustacchi * build this file as part of the module. This takes care of all the work of
211045e13Robert Mustacchi * setting up and creating the sensor, given a path to that sensor.
22f2dbfd3Robert Mustacchi */
23f2dbfd3Robert Mustacchi
24f2dbfd3Robert Mustacchi#include <sys/types.h>
25f2dbfd3Robert Mustacchi#include <sys/stat.h>
26f2dbfd3Robert Mustacchi#include <fcntl.h>
27f2dbfd3Robert Mustacchi#include <stdio.h>
28f2dbfd3Robert Mustacchi#include <string.h>
29f2dbfd3Robert Mustacchi#include <unistd.h>
30f2dbfd3Robert Mustacchi#include <libnvpair.h>
31f2dbfd3Robert Mustacchi#include <sys/sensors.h>
32f2dbfd3Robert Mustacchi#include <sys/fm/protocol.h>
33f2dbfd3Robert Mustacchi#include <fm/topo_mod.h>
34f2dbfd3Robert Mustacchi
351045e13Robert Mustacchi#define	TOPO_METH_TOPO_SENSOR_SCALAR		"topo_sensor_scalar_reading"
361045e13Robert Mustacchi#define	TOPO_METH_TOPO_SENSOR_SCALAR_DESC	"Kernel Sensor Scalar Reading"
371045e13Robert Mustacchi#define	TOPO_METH_TOPO_SENSOR_SCALAR_VERSION	0
38f2dbfd3Robert Mustacchi
39f2dbfd3Robert Mustacchistatic int
401045e13Robert Mustacchitopo_sensor_scalar_read(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
41f2dbfd3Robert Mustacchi    nvlist_t *in, nvlist_t **out)
42f2dbfd3Robert Mustacchi{
43f2dbfd3Robert Mustacchi	int fd = -1, ret;
44f2dbfd3Robert Mustacchi	nvlist_t *args, *nvl;
45f2dbfd3Robert Mustacchi	char *path;
461045e13Robert Mustacchi	sensor_ioctl_scalar_t scalar;
471045e13Robert Mustacchi	double value;
48f2dbfd3Robert Mustacchi
491045e13Robert Mustacchi	if (vers != TOPO_METH_TOPO_SENSOR_SCALAR_VERSION) {
50f2dbfd3Robert Mustacchi		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
51f2dbfd3Robert Mustacchi	}
52f2dbfd3Robert Mustacchi
53f2dbfd3Robert Mustacchi	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 ||
54f2dbfd3Robert Mustacchi	    nvlist_lookup_string(args, TOPO_IO_DEV_PATH, &path) != 0) {
55f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to lookup sensor path from "
56f2dbfd3Robert Mustacchi		    "property %s", TOPO_IO_DEV_PATH);
57f2dbfd3Robert Mustacchi		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
58f2dbfd3Robert Mustacchi	}
59f2dbfd3Robert Mustacchi
60f2dbfd3Robert Mustacchi	if ((fd = open(path, O_RDONLY)) < 0) {
61f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to open sensor path %s: %s",
62f2dbfd3Robert Mustacchi		    path, strerror(errno));
63f2dbfd3Robert Mustacchi		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
64f2dbfd3Robert Mustacchi	}
65f2dbfd3Robert Mustacchi
661045e13Robert Mustacchi	(void) memset(&scalar, '\0', sizeof (scalar));
671045e13Robert Mustacchi	if (ioctl(fd, SENSOR_IOCTL_SCALAR, &scalar) != 0) {
681045e13Robert Mustacchi		topo_mod_dprintf(mod, "failed to read sensor %s: %s", path,
691045e13Robert Mustacchi		    strerror(errno));
70f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
71f2dbfd3Robert Mustacchi		goto out;
72f2dbfd3Robert Mustacchi	}
73f2dbfd3Robert Mustacchi
74f2dbfd3Robert Mustacchi	/*
75f2dbfd3Robert Mustacchi	 * Check to see if we need to change the value to get it into an
761045e13Robert Mustacchi	 * accurate reading. Positive granularities indicate that the sensor
771045e13Robert Mustacchi	 * reading is in a fractional number of units and that each unit
781045e13Robert Mustacchi	 * contains scalar.sis_gran steps. A negative number means that the
791045e13Robert Mustacchi	 * sensor reading represents scalar.sis_gran units.
80f2dbfd3Robert Mustacchi	 */
811045e13Robert Mustacchi	value = (double)scalar.sis_value;
821045e13Robert Mustacchi	if (scalar.sis_gran > 1) {
831045e13Robert Mustacchi		value /= (double)scalar.sis_gran;
841045e13Robert Mustacchi	} else if (scalar.sis_gran < -1) {
851045e13Robert Mustacchi		value *= (double)labs(scalar.sis_gran);
86f2dbfd3Robert Mustacchi	}
87f2dbfd3Robert Mustacchi
88f2dbfd3Robert Mustacchi	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) {
891045e13Robert Mustacchi		topo_mod_dprintf(mod, "failed to allocate output nvl");
90f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_NOMEM);
91f2dbfd3Robert Mustacchi		goto out;
92f2dbfd3Robert Mustacchi	}
93f2dbfd3Robert Mustacchi
94f2dbfd3Robert Mustacchi	if (nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_SENSOR_READING) !=
95f2dbfd3Robert Mustacchi	    0 ||
96f2dbfd3Robert Mustacchi	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 ||
971045e13Robert Mustacchi	    nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, value) != 0) {
98f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to add members to output "
991045e13Robert Mustacchi		    "sensor nvlist");
100f2dbfd3Robert Mustacchi		nvlist_free(nvl);
101f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_NOMEM);
102f2dbfd3Robert Mustacchi		goto out;
103f2dbfd3Robert Mustacchi	}
104f2dbfd3Robert Mustacchi
105f2dbfd3Robert Mustacchi	*out = nvl;
106f2dbfd3Robert Mustacchi	ret = 0;
107f2dbfd3Robert Mustacchiout:
108f2dbfd3Robert Mustacchi	if (fd >= 0) {
109f2dbfd3Robert Mustacchi		(void) close(fd);
110f2dbfd3Robert Mustacchi	}
111f2dbfd3Robert Mustacchi	return (ret);
112f2dbfd3Robert Mustacchi}
113f2dbfd3Robert Mustacchi
1141045e13Robert Mustacchistatic const topo_method_t topo_sensor_scalar_fac_methods[] = {
1151045e13Robert Mustacchi	{ TOPO_METH_TOPO_SENSOR_SCALAR, TOPO_METH_TOPO_SENSOR_SCALAR_DESC,
1161045e13Robert Mustacchi		TOPO_METH_TOPO_SENSOR_SCALAR_VERSION, TOPO_STABILITY_INTERNAL,
1171045e13Robert Mustacchi		topo_sensor_scalar_read },
118f2dbfd3Robert Mustacchi	{ NULL }
119f2dbfd3Robert Mustacchi};
120f2dbfd3Robert Mustacchi
121f2dbfd3Robert Mustacchistatic topo_sensor_unit_t
1221045e13Robert Mustacchitopo_sensor_units(const sensor_ioctl_scalar_t *scalar)
123f2dbfd3Robert Mustacchi{
1241045e13Robert Mustacchi	switch (scalar->sis_unit) {
125f2dbfd3Robert Mustacchi	case SENSOR_UNIT_CELSIUS:
126f2dbfd3Robert Mustacchi		return (TOPO_SENSOR_UNITS_DEGREES_C);
127f2dbfd3Robert Mustacchi	case SENSOR_UNIT_FAHRENHEIT:
128f2dbfd3Robert Mustacchi		return (TOPO_SENSOR_UNITS_DEGREES_F);
129f2dbfd3Robert Mustacchi	case SENSOR_UNIT_KELVIN:
130f2dbfd3Robert Mustacchi		return (TOPO_SENSOR_UNITS_DEGREES_K);
1311045e13Robert Mustacchi	case SENSOR_UNIT_VOLTS:
1321045e13Robert Mustacchi		return (TOPO_SENSOR_UNITS_VOLTS);
1331045e13Robert Mustacchi	case SENSOR_UNIT_AMPS:
1341045e13Robert Mustacchi		return (TOPO_SENSOR_UNITS_AMPS);
135f2dbfd3Robert Mustacchi	default:
136f2dbfd3Robert Mustacchi		return (TOPO_SENSOR_UNITS_UNSPECIFIED);
137f2dbfd3Robert Mustacchi	}
138f2dbfd3Robert Mustacchi}
139f2dbfd3Robert Mustacchi
140f2dbfd3Robert Mustacchiint
1411045e13Robert Mustacchitopo_sensor_create_scalar_sensor(topo_mod_t *mod, tnode_t *pnode,
142f2dbfd3Robert Mustacchi    const char *path, const char *fname)
143f2dbfd3Robert Mustacchi{
144f2dbfd3Robert Mustacchi	int fd, ret, err;
145f2dbfd3Robert Mustacchi	sensor_ioctl_kind_t sik;
1461045e13Robert Mustacchi	sensor_ioctl_scalar_t scalar;
1471045e13Robert Mustacchi	uint32_t topo_type;
148f2dbfd3Robert Mustacchi	tnode_t *fnode = NULL;
149f2dbfd3Robert Mustacchi	topo_pgroup_info_t pgi;
150f2dbfd3Robert Mustacchi	nvlist_t *reader_arg = NULL;
151f2dbfd3Robert Mustacchi
152f2dbfd3Robert Mustacchi	topo_mod_dprintf(mod, "attempting to create sensor for %s at %s",
153f2dbfd3Robert Mustacchi	    topo_node_name(pnode), path);
154f2dbfd3Robert Mustacchi
155f2dbfd3Robert Mustacchi	(void) memset(&sik, '\0', sizeof (sik));
1561045e13Robert Mustacchi	(void) memset(&scalar, '\0', sizeof (scalar));
157f2dbfd3Robert Mustacchi
158f2dbfd3Robert Mustacchi	if ((fd = open(path, O_RDONLY)) < 0) {
159f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to open sensor path %s: %s",
160f2dbfd3Robert Mustacchi		    path, strerror(errno));
161f2dbfd3Robert Mustacchi
162f2dbfd3Robert Mustacchi		/*
1631045e13Robert Mustacchi		 * We always try to create sensors; however, they may not exist
1641045e13Robert Mustacchi		 * or be supported on the system in question.  Therefore ENOENT
1651045e13Robert Mustacchi		 * is totally acceptable.
166f2dbfd3Robert Mustacchi		 */
167f2dbfd3Robert Mustacchi		if (errno == ENOENT) {
168f2dbfd3Robert Mustacchi			return (0);
169f2dbfd3Robert Mustacchi		}
170f2dbfd3Robert Mustacchi		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
171f2dbfd3Robert Mustacchi	}
172f2dbfd3Robert Mustacchi
1731045e13Robert Mustacchi	if (ioctl(fd, SENSOR_IOCTL_KIND, &sik) != 0) {
174f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to verify sensor kind for sensor "
175f2dbfd3Robert Mustacchi		    "%s: %s", path, strerror(errno));
176f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
177f2dbfd3Robert Mustacchi		goto out;
178f2dbfd3Robert Mustacchi	}
179f2dbfd3Robert Mustacchi
1801045e13Robert Mustacchi	switch (sik.sik_kind) {
1811045e13Robert Mustacchi	case SENSOR_KIND_TEMPERATURE:
1821045e13Robert Mustacchi		topo_type = TOPO_SENSOR_TYPE_TEMP;
1831045e13Robert Mustacchi		break;
1841045e13Robert Mustacchi	case SENSOR_KIND_VOLTAGE:
1851045e13Robert Mustacchi		topo_type = TOPO_SENSOR_TYPE_VOLTAGE;
1861045e13Robert Mustacchi		break;
1871045e13Robert Mustacchi	case SENSOR_KIND_CURRENT:
1881045e13Robert Mustacchi		topo_type = TOPO_SENSOR_TYPE_CURRENT;
1891045e13Robert Mustacchi		break;
1901045e13Robert Mustacchi	default:
1911045e13Robert Mustacchi		topo_mod_dprintf(mod, "unknown sensor kind for %s, found 0x%x",
1921045e13Robert Mustacchi		    path, sik.sik_kind);
193f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
194f2dbfd3Robert Mustacchi		goto out;
1951045e13Robert Mustacchi
196f2dbfd3Robert Mustacchi	}
197f2dbfd3Robert Mustacchi
1981045e13Robert Mustacchi	if (ioctl(fd, SENSOR_IOCTL_SCALAR, &scalar) != 0) {
1991045e13Robert Mustacchi		topo_mod_dprintf(mod, "failed to read scalar sensor %s: %s",
2001045e13Robert Mustacchi		    path, strerror(errno));
201f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
202f2dbfd3Robert Mustacchi		goto out;
203f2dbfd3Robert Mustacchi	}
204f2dbfd3Robert Mustacchi
205f2dbfd3Robert Mustacchi	(void) close(fd);
206f2dbfd3Robert Mustacchi	fd = -1;
207f2dbfd3Robert Mustacchi
208f2dbfd3Robert Mustacchi	if ((fnode = topo_node_facbind(mod, pnode, fname,
209f2dbfd3Robert Mustacchi	    TOPO_FAC_TYPE_SENSOR)) == NULL) {
2101045e13Robert Mustacchi		topo_mod_dprintf(mod, "failed to bind sensor facility "
211f2dbfd3Robert Mustacchi		    "node to %s: %d", path, topo_mod_errno(mod));
212f2dbfd3Robert Mustacchi		ret = -1;
213f2dbfd3Robert Mustacchi		goto out;
214f2dbfd3Robert Mustacchi	}
215f2dbfd3Robert Mustacchi
216f2dbfd3Robert Mustacchi	pgi.tpi_name = TOPO_PGROUP_FACILITY;
217f2dbfd3Robert Mustacchi	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
218f2dbfd3Robert Mustacchi	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
219f2dbfd3Robert Mustacchi	pgi.tpi_version = 1;
220f2dbfd3Robert Mustacchi
221f2dbfd3Robert Mustacchi	if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
222f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to create facility pgroup: %s",
223f2dbfd3Robert Mustacchi		    topo_strerror(err));
224f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, err);
225f2dbfd3Robert Mustacchi		goto out;
226f2dbfd3Robert Mustacchi	}
227f2dbfd3Robert Mustacchi
228f2dbfd3Robert Mustacchi	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY,
229f2dbfd3Robert Mustacchi	    TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
230f2dbfd3Robert Mustacchi	    TOPO_SENSOR_CLASS_THRESHOLD, &err) != 0 ||
231f2dbfd3Robert Mustacchi	    topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
2321045e13Robert Mustacchi	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, topo_type, &err) != 0 ||
233f2dbfd3Robert Mustacchi	    topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
2341045e13Robert Mustacchi	    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, topo_sensor_units(&scalar),
235f2dbfd3Robert Mustacchi	    &err) != 0) {
236f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to set properties for sensor "
237f2dbfd3Robert Mustacchi		    "%s: %s", path, topo_strerror(err));
238f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, err);
239f2dbfd3Robert Mustacchi		goto out;
240f2dbfd3Robert Mustacchi
241f2dbfd3Robert Mustacchi	}
242f2dbfd3Robert Mustacchi
2431045e13Robert Mustacchi	if (topo_method_register(mod, fnode, topo_sensor_scalar_fac_methods) <
244f2dbfd3Robert Mustacchi	    0) {
245f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to register reading methods on "
246f2dbfd3Robert Mustacchi		    "%s", path);
247f2dbfd3Robert Mustacchi		ret = -1;
248f2dbfd3Robert Mustacchi		goto out;
249f2dbfd3Robert Mustacchi	}
250f2dbfd3Robert Mustacchi
251f2dbfd3Robert Mustacchi	if (topo_mod_nvalloc(mod, &reader_arg, NV_UNIQUE_NAME) != 0 ||
252f2dbfd3Robert Mustacchi	    nvlist_add_string(reader_arg, TOPO_IO_DEV_PATH, path) != 0) {
253f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "Failed to set up reader argument nvl");
254f2dbfd3Robert Mustacchi		ret = topo_mod_seterrno(mod, EMOD_NOMEM);
255f2dbfd3Robert Mustacchi		goto out;
256f2dbfd3Robert Mustacchi	}
257f2dbfd3Robert Mustacchi
258f2dbfd3Robert Mustacchi	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
2591045e13Robert Mustacchi	    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, TOPO_METH_TOPO_SENSOR_SCALAR,
260f2dbfd3Robert Mustacchi	    reader_arg, &err) != 0) {
261f2dbfd3Robert Mustacchi		topo_mod_dprintf(mod, "failed to set argument for sensor %s: "
262f2dbfd3Robert Mustacchi		    "%s", path, topo_strerror(err));
263f2dbfd3Robert Mustacchi		err = topo_mod_seterrno(mod, err);
264f2dbfd3Robert Mustacchi		goto out;
265f2dbfd3Robert Mustacchi	}
266f2dbfd3Robert Mustacchi
2671045e13Robert Mustacchi	topo_mod_dprintf(mod, "created sensor at %s", path);
2681045e13Robert Mustacchi
269f2dbfd3Robert Mustacchi	nvlist_free(reader_arg);
270f2dbfd3Robert Mustacchi	return (0);
271f2dbfd3Robert Mustacchiout:
272f2dbfd3Robert Mustacchi	if (fd >= 0) {
273f2dbfd3Robert Mustacchi		(void) close(fd);
274f2dbfd3Robert Mustacchi	}
275f2dbfd3Robert Mustacchi
276f2dbfd3Robert Mustacchi	topo_node_unbind(fnode);
277f2dbfd3Robert Mustacchi	nvlist_free(reader_arg);
278f2dbfd3Robert Mustacchi	return (ret);
279f2dbfd3Robert Mustacchi}
280