1d91236feSeschrock /*
2d91236feSeschrock  * CDDL HEADER START
3d91236feSeschrock  *
4d91236feSeschrock  * The contents of this file are subject to the terms of the
5d91236feSeschrock  * Common Development and Distribution License (the "License").
6d91236feSeschrock  * You may not use this file except in compliance with the License.
7d91236feSeschrock  *
8d91236feSeschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d91236feSeschrock  * or http://www.opensolaris.org/os/licensing.
10d91236feSeschrock  * See the License for the specific language governing permissions
11d91236feSeschrock  * and limitations under the License.
12d91236feSeschrock  *
13d91236feSeschrock  * When distributing Covered Code, include this CDDL HEADER in each
14d91236feSeschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d91236feSeschrock  * If applicable, add the following below this CDDL HEADER, with the
16d91236feSeschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17d91236feSeschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18d91236feSeschrock  *
19d91236feSeschrock  * CDDL HEADER END
20d91236feSeschrock  */
21d91236feSeschrock 
22d91236feSeschrock /*
236efb64caSEric Schrock  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24d91236feSeschrock  * Use is subject to license terms.
25d91236feSeschrock  */
26d91236feSeschrock 
27db7d7001SYuri Pankov /*
28db7d7001SYuri Pankov  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
29db7d7001SYuri Pankov  */
30db7d7001SYuri Pankov 
3100d7a6fbSRob Johnston /*
3200d7a6fbSRob Johnston  * Copyright (c) 2018, Joyent, Inc.
3300d7a6fbSRob Johnston  */
3400d7a6fbSRob Johnston 
35d91236feSeschrock /*
36d91236feSeschrock  * Facility node support for SES enclosures.  We support the following facility
37d91236feSeschrock  * nodes, based on the node type:
38d91236feSeschrock  *
3900d7a6fbSRob Johnston  *      bay
4000d7a6fbSRob Johnston  *              indicator=ident
4100d7a6fbSRob Johnston  *              indicator=fail
4200d7a6fbSRob Johnston  *              indicator=ok2rm
4300d7a6fbSRob Johnston  *              sensor=fault
44d91236feSeschrock  *
4500d7a6fbSRob Johnston  *      controller
4600d7a6fbSRob Johnston  *              indicator=ident
4700d7a6fbSRob Johnston  *              indicator=fail
48d91236feSeschrock  *
4900d7a6fbSRob Johnston  *      fan
5000d7a6fbSRob Johnston  *              indicator=ident
5100d7a6fbSRob Johnston  *              indicator=fail
5200d7a6fbSRob Johnston  *              sensor=speed
5300d7a6fbSRob Johnston  *              sensor=fault
54d91236feSeschrock  *
5500d7a6fbSRob Johnston  *      psu
5600d7a6fbSRob Johnston  *              indicator=ident
5700d7a6fbSRob Johnston  *              indicator=fail
5800d7a6fbSRob Johnston  *              sensor=status
59d91236feSeschrock  *
6000d7a6fbSRob Johnston  *      ses-enclosure
6100d7a6fbSRob Johnston  *              indicator=ident
6200d7a6fbSRob Johnston  *              indicator=fail
6300d7a6fbSRob Johnston  *              sensor=fault
6400d7a6fbSRob Johnston  *              sensor=<name>   (temperature)
6500d7a6fbSRob Johnston  *              sensor=<name>   (voltage)
6600d7a6fbSRob Johnston  *              sensor=<name>   (current)
67d91236feSeschrock  *
68d91236feSeschrock  * Most of these are handled by a single method that supports getting and
69d91236feSeschrock  * setting boolean properties on the node.  The fan speed sensor requires a
70d91236feSeschrock  * special handler, while the analog enclosure sensors all have similar
71d91236feSeschrock  * behavior and can be grouped together using a common method.
72d91236feSeschrock  */
73d91236feSeschrock 
74d91236feSeschrock #include "ses.h"
75d91236feSeschrock #include "disk.h"
76d91236feSeschrock 
77d91236feSeschrock #include <string.h>
78d91236feSeschrock 
79d91236feSeschrock static int ses_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t,
80d91236feSeschrock     nvlist_t *, nvlist_t **);
81d91236feSeschrock static int ses_sensor_reading(topo_mod_t *, tnode_t *, topo_version_t,
82d91236feSeschrock     nvlist_t *, nvlist_t **);
83d91236feSeschrock static int ses_sensor_state(topo_mod_t *, tnode_t *, topo_version_t,
84d91236feSeschrock     nvlist_t *, nvlist_t **);
85d91236feSeschrock static int ses_psu_state(topo_mod_t *, tnode_t *, topo_version_t,
86d91236feSeschrock     nvlist_t *, nvlist_t **);
87d91236feSeschrock 
88d91236feSeschrock #define	SES_SUPP_WARN_UNDER	0x01
89d91236feSeschrock #define	SES_SUPP_WARN_OVER	0x02
90d91236feSeschrock #define	SES_SUPP_CRIT_UNDER	0x04
91d91236feSeschrock #define	SES_SUPP_CRIT_OVER	0x08
92d91236feSeschrock 
93d91236feSeschrock typedef struct ses_sensor_desc {
94d91236feSeschrock 	int		sd_type;
95d91236feSeschrock 	int		sd_units;
96d91236feSeschrock 	const char	*sd_propname;
97d91236feSeschrock 	double		sd_multiplier;
98d91236feSeschrock } ses_sensor_desc_t;
99d91236feSeschrock 
100d91236feSeschrock #define	TOPO_METH_SES_MODE_VERSION	0
101d91236feSeschrock #define	TOPO_METH_SES_READING_VERSION	0
102d91236feSeschrock #define	TOPO_METH_SES_STATE_VERSION	0
103d91236feSeschrock #define	TOPO_METH_SES_PSU_VERSION	0
104d91236feSeschrock 
105d91236feSeschrock #define	TOPO_METH_SES_READING_PROP	"propname"
106d91236feSeschrock #define	TOPO_METH_SES_READING_MULT	"multiplier"
107d91236feSeschrock 
108d91236feSeschrock #define	TOPO_METH_SES_STATE_PROP	"propname"
109d91236feSeschrock 
110d91236feSeschrock #define	TOPO_METH_SES_MODE_PROP		"property-name"
111d91236feSeschrock #define	TOPO_METH_SES_MODE_ALTPROP	"alternate-property"
112d91236feSeschrock 
113d91236feSeschrock static const topo_method_t ses_indicator_methods[] = {
114d91236feSeschrock 	{ "ses_indicator_mode", TOPO_PROP_METH_DESC,
115d91236feSeschrock 	    TOPO_METH_SES_MODE_VERSION, TOPO_STABILITY_INTERNAL,
116*ab26215bSRobert Mustacchi 	    ses_indicator_mode },
117*ab26215bSRobert Mustacchi 	{ NULL }
118d91236feSeschrock };
119d91236feSeschrock 
120d91236feSeschrock static const topo_method_t ses_sensor_methods[] = {
121d91236feSeschrock 	{ "ses_sensor_reading", TOPO_PROP_METH_DESC,
122d91236feSeschrock 	    TOPO_METH_SES_READING_VERSION, TOPO_STABILITY_INTERNAL,
123d91236feSeschrock 	    ses_sensor_reading },
124d91236feSeschrock 	{ "ses_sensor_state", TOPO_PROP_METH_DESC,
125d91236feSeschrock 	    TOPO_METH_SES_STATE_VERSION, TOPO_STABILITY_INTERNAL,
126d91236feSeschrock 	    ses_sensor_state },
127d91236feSeschrock 	{ "ses_psu_state", TOPO_PROP_METH_DESC,
128d91236feSeschrock 	    TOPO_METH_SES_PSU_VERSION, TOPO_STABILITY_INTERNAL,
129d91236feSeschrock 	    ses_psu_state },
130*ab26215bSRobert Mustacchi 	{ NULL }
131d91236feSeschrock };
132d91236feSeschrock 
133d91236feSeschrock /*
134d91236feSeschrock  * Get or set an indicator.  This method is invoked with arguments indicating
135d91236feSeschrock  * the property to query to retrieve the value.  Some elements (enclosures and
136d91236feSeschrock  * devices) support a request property that is distinct from an array-detected
137d91236feSeschrock  * property.  Either of these conditions will result in the indicator being
138d91236feSeschrock  * lit, so we have to check both properties.
139d91236feSeschrock  */
140d91236feSeschrock static int
ses_indicator_mode(topo_mod_t * mod,tnode_t * tn,topo_version_t vers,nvlist_t * in,nvlist_t ** out)141d91236feSeschrock ses_indicator_mode(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
142d91236feSeschrock     nvlist_t *in, nvlist_t **out)
143d91236feSeschrock {
144d91236feSeschrock 	ses_node_t *np;
145d91236feSeschrock 	nvlist_t *args, *pargs, *props;
146d91236feSeschrock 	char *propname, *altprop;
147d91236feSeschrock 	uint32_t mode;
148d91236feSeschrock 	boolean_t current, altcurrent;
149d91236feSeschrock 	nvlist_t *nvl;
150525b85dbSEric Schrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
151d91236feSeschrock 
152d91236feSeschrock 	if (vers > TOPO_METH_SES_MODE_VERSION)
153d91236feSeschrock 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
154d91236feSeschrock 
155d91236feSeschrock 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 ||
156d91236feSeschrock 	    nvlist_lookup_string(args, TOPO_METH_SES_MODE_PROP,
157d91236feSeschrock 	    &propname) != 0) {
158d91236feSeschrock 		topo_mod_dprintf(mod, "invalid arguments to 'mode' method\n");
159d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
160d91236feSeschrock 	}
161d91236feSeschrock 
162d91236feSeschrock 	if (nvlist_lookup_string(args, TOPO_METH_SES_MODE_ALTPROP,
163d91236feSeschrock 	    &altprop) != 0)
164d91236feSeschrock 		altprop = NULL;
165d91236feSeschrock 
1660b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL) {
167d91236feSeschrock 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
168d91236feSeschrock 		    "method\n");
169d91236feSeschrock 		return (-1);
170d91236feSeschrock 	}
171d91236feSeschrock 	verify((props = ses_node_props(np)) != NULL);
172d91236feSeschrock 
173d91236feSeschrock 	if (nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0 &&
174d91236feSeschrock 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
175d91236feSeschrock 		/* set operation */
176d91236feSeschrock 		if (nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
177d91236feSeschrock 		    &mode) != 0) {
178d91236feSeschrock 			topo_mod_dprintf(mod, "invalid type for indicator "
179d91236feSeschrock 			    "mode property");
1800b32bb8bSEric Schrock 			(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1810b32bb8bSEric Schrock 			goto error;
182d91236feSeschrock 		}
183d91236feSeschrock 
184d91236feSeschrock 		if (mode != TOPO_LED_STATE_OFF && mode != TOPO_LED_STATE_ON) {
185d91236feSeschrock 			topo_mod_dprintf(mod, "invalid indicator mode %d\n",
186d91236feSeschrock 			    mode);
1870b32bb8bSEric Schrock 			(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1880b32bb8bSEric Schrock 			goto error;
189d91236feSeschrock 		}
190d91236feSeschrock 
191d91236feSeschrock 		nvl = NULL;
192d91236feSeschrock 		if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
193d91236feSeschrock 		    nvlist_add_boolean_value(nvl, propname,
194d91236feSeschrock 		    mode == TOPO_LED_STATE_ON ? B_TRUE : B_FALSE) != 0) {
195d91236feSeschrock 			nvlist_free(nvl);
1960b32bb8bSEric Schrock 			(void) topo_mod_seterrno(mod, EMOD_NOMEM);
1970b32bb8bSEric Schrock 			goto error;
198d91236feSeschrock 		}
199d91236feSeschrock 
200d91236feSeschrock 		if (ses_node_ctl(np, SES_CTL_OP_SETPROP, nvl) != 0) {
201d91236feSeschrock 			topo_mod_dprintf(mod, "failed to set indicator: %s\n",
202d91236feSeschrock 			    ses_errmsg());
203d91236feSeschrock 			nvlist_free(nvl);
2040b32bb8bSEric Schrock 			goto error;
205d91236feSeschrock 		}
206d91236feSeschrock 
207525b85dbSEric Schrock 		tp->set_snaptime = 0;
208d91236feSeschrock 		nvlist_free(nvl);
209d91236feSeschrock 	} else {
210d91236feSeschrock 		/* get operation */
211d91236feSeschrock 		if (nvlist_lookup_boolean_value(props,
212d91236feSeschrock 		    propname, &current) != 0) {
213d91236feSeschrock 			topo_mod_dprintf(mod, "failed to lookup %s in node "
214d91236feSeschrock 			    "properties\n", propname);
2150b32bb8bSEric Schrock 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
2160b32bb8bSEric Schrock 			goto error;
217d91236feSeschrock 		}
218d91236feSeschrock 
219d91236feSeschrock 		if (altprop != NULL && nvlist_lookup_boolean_value(props,
220d91236feSeschrock 		    altprop, &altcurrent) == 0)
221d91236feSeschrock 			current |= altcurrent;
222d91236feSeschrock 
223d91236feSeschrock 		mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
224d91236feSeschrock 	}
225d91236feSeschrock 
226d91236feSeschrock 	nvl = NULL;
227d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
228d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
229d91236feSeschrock 	    TOPO_LED_MODE) != 0 ||
230d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
231d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, mode) != 0) {
232d91236feSeschrock 		nvlist_free(nvl);
2330b32bb8bSEric Schrock 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
2340b32bb8bSEric Schrock 		goto error;
235d91236feSeschrock 	}
236d91236feSeschrock 
2370b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
238d91236feSeschrock 	*out = nvl;
239d91236feSeschrock 	return (0);
2400b32bb8bSEric Schrock 
2410b32bb8bSEric Schrock error:
2420b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
2430b32bb8bSEric Schrock 	return (-1);
244d91236feSeschrock }
245d91236feSeschrock 
246d91236feSeschrock /*
247d91236feSeschrock  * Read the given sensor value.  This just looks up the value in the node
248d91236feSeschrock  * properties, and multiplies by a fixed value (determined when the method is
249d91236feSeschrock  * instantiated).
250d91236feSeschrock  */
251d91236feSeschrock static int
ses_sensor_reading(topo_mod_t * mod,tnode_t * tn,topo_version_t vers,nvlist_t * in,nvlist_t ** out)252d91236feSeschrock ses_sensor_reading(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
253d91236feSeschrock     nvlist_t *in, nvlist_t **out)
254d91236feSeschrock {
255d91236feSeschrock 	ses_node_t *np;
256d91236feSeschrock 	nvlist_t *args, *props;
257d91236feSeschrock 	char *prop;
258d91236feSeschrock 	double raw, multiplier;
259d91236feSeschrock 	uint64_t current;
260d91236feSeschrock 	int64_t scurrent;
261d91236feSeschrock 	nvlist_t *nvl;
262d91236feSeschrock 
263d91236feSeschrock 	if (vers > TOPO_METH_SES_MODE_VERSION)
264d91236feSeschrock 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
265d91236feSeschrock 
266d91236feSeschrock 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 ||
267d91236feSeschrock 	    nvlist_lookup_string(args, TOPO_METH_SES_READING_PROP,
268d91236feSeschrock 	    &prop) != 0) {
269d91236feSeschrock 		topo_mod_dprintf(mod,
270d91236feSeschrock 		    "invalid arguments to 'reading' method\n");
271d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
272d91236feSeschrock 	}
273d91236feSeschrock 
274d91236feSeschrock 	if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT,
275d91236feSeschrock 	    &multiplier) != 0)
276d91236feSeschrock 		multiplier = 1;
277d91236feSeschrock 
2780b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL) {
279d91236feSeschrock 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
280d91236feSeschrock 		    "method\n");
281d91236feSeschrock 		return (-1);
282d91236feSeschrock 	}
283d91236feSeschrock 	verify((props = ses_node_props(np)) != NULL);
284d91236feSeschrock 
285d91236feSeschrock 	if (nvlist_lookup_uint64(props, prop, &current) == 0) {
286d91236feSeschrock 		raw = (double)current;
287d91236feSeschrock 	} else if (nvlist_lookup_int64(props, prop, &scurrent) == 0) {
288d91236feSeschrock 		raw = (double)scurrent;
289d91236feSeschrock 	} else {
290d91236feSeschrock 		topo_mod_dprintf(mod, "failed to lookup %s in node "
291d91236feSeschrock 		    "properties\n", prop);
2920b32bb8bSEric Schrock 		ses_node_unlock(mod, tn);
293d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
294d91236feSeschrock 	}
295d91236feSeschrock 
2960b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
2970b32bb8bSEric Schrock 
298d91236feSeschrock 	nvl = NULL;
299d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
300d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
301d91236feSeschrock 	    TOPO_SENSOR_READING) != 0 ||
302d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 ||
303d91236feSeschrock 	    nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, raw * multiplier) != 0) {
304d91236feSeschrock 		nvlist_free(nvl);
305d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
306d91236feSeschrock 	}
307d91236feSeschrock 
308d91236feSeschrock 	*out = nvl;
309d91236feSeschrock 	return (0);
310d91236feSeschrock }
311d91236feSeschrock 
312d91236feSeschrock /*
313d91236feSeschrock  * Returns the current sensor state.  This can be invoked for one of two
314d91236feSeschrock  * different types of sensors: threshold or discrete sensors.  For discrete
315d91236feSeschrock  * sensors, we expect a name of a boolean property and indicate
316d91236feSeschrock  * asserted/deasserted based on that.  For threshold sensors, we check for the
317d91236feSeschrock  * standard warning/critical properties and translate that into the appropriate
318d91236feSeschrock  * topo state.
319d91236feSeschrock  */
320d91236feSeschrock /*ARGSUSED*/
321d91236feSeschrock static int
ses_sensor_state(topo_mod_t * mod,tnode_t * tn,topo_version_t vers,nvlist_t * in,nvlist_t ** out)322d91236feSeschrock ses_sensor_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
323d91236feSeschrock     nvlist_t *in, nvlist_t **out)
324d91236feSeschrock {
325d91236feSeschrock 	nvlist_t *nvl, *args, *props;
3266efb64caSEric Schrock 	boolean_t value;
3277bced3d7SEric Schrock 	uint64_t status;
328d91236feSeschrock 	uint32_t state;
329d91236feSeschrock 	ses_node_t *np;
330d91236feSeschrock 	char *prop;
331d91236feSeschrock 
3320b32bb8bSEric Schrock 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
3330b32bb8bSEric Schrock 		topo_mod_dprintf(mod,
3340b32bb8bSEric Schrock 		    "invalid arguments to 'state' method\n");
3350b32bb8bSEric Schrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
3360b32bb8bSEric Schrock 	}
3370b32bb8bSEric Schrock 
3380b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL) {
339d91236feSeschrock 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
340d91236feSeschrock 		    "method\n");
341d91236feSeschrock 		return (-1);
342d91236feSeschrock 	}
343d91236feSeschrock 	verify((props = ses_node_props(np)) != NULL);
344d91236feSeschrock 
3457bced3d7SEric Schrock 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
3467bced3d7SEric Schrock 		status = SES_ESC_UNSUPPORTED;
3477bced3d7SEric Schrock 
348d91236feSeschrock 	state = 0;
349d91236feSeschrock 	if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP,
350d91236feSeschrock 	    &prop) == 0) {
3517bced3d7SEric Schrock 		/* discrete (fault) sensor */
3527bced3d7SEric Schrock 
3536efb64caSEric Schrock 		if (status == SES_ESC_UNRECOVERABLE)
3546efb64caSEric Schrock 			state |= TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV;
3556efb64caSEric Schrock 		else if (status == SES_ESC_CRITICAL)
3566efb64caSEric Schrock 			state |= TOPO_SENSOR_STATE_GENERIC_FAIL_CRITICAL;
3576efb64caSEric Schrock 		else if (nvlist_lookup_boolean_value(props, prop,
3586efb64caSEric Schrock 		    &value) == 0 && value)
3596efb64caSEric Schrock 			state |= TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV;
360d91236feSeschrock 		else
3616efb64caSEric Schrock 			state |= TOPO_SENSOR_STATE_GENERIC_FAIL_DEASSERTED;
362d91236feSeschrock 	} else {
363d91236feSeschrock 		/* threshold sensor */
364d91236feSeschrock 		if (nvlist_lookup_boolean_value(props,
365d91236feSeschrock 		    SES_PROP_WARN_UNDER, &value) == 0 && value)
3660b1b4412SEric Schrock 			state |= TOPO_SENSOR_STATE_THRESH_LOWER_NONCRIT;
367d91236feSeschrock 		if (nvlist_lookup_boolean_value(props,
368d91236feSeschrock 		    SES_PROP_WARN_OVER, &value) == 0 && value)
3690b1b4412SEric Schrock 			state |= TOPO_SENSOR_STATE_THRESH_UPPER_NONCRIT;
370d91236feSeschrock 		if (nvlist_lookup_boolean_value(props,
371d91236feSeschrock 		    SES_PROP_CRIT_UNDER, &value) == 0 && value)
3720b1b4412SEric Schrock 			state |= TOPO_SENSOR_STATE_THRESH_LOWER_CRIT;
373d91236feSeschrock 		if (nvlist_lookup_boolean_value(props,
374d91236feSeschrock 		    SES_PROP_CRIT_OVER, &value) == 0 && value)
3750b1b4412SEric Schrock 			state |= TOPO_SENSOR_STATE_THRESH_UPPER_CRIT;
376d91236feSeschrock 	}
377d91236feSeschrock 
3780b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
3790b32bb8bSEric Schrock 
380d91236feSeschrock 	nvl = NULL;
381d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
382d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
383d91236feSeschrock 	    TOPO_SENSOR_STATE) != 0 ||
384d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
385d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) {
386d91236feSeschrock 		nvlist_free(nvl);
387d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
388d91236feSeschrock 	}
389d91236feSeschrock 
390d91236feSeschrock 	*out = nvl;
391d91236feSeschrock 	return (0);
392d91236feSeschrock }
393d91236feSeschrock 
394d91236feSeschrock /*
395d91236feSeschrock  * Read the status of a PSU.  This is such a specialized operation that it has
396d91236feSeschrock  * its own method instead of trying to piggyback on ses_sensor_state().  We
397d91236feSeschrock  * use the following mapping to get to the standard topo power supply states:
398d91236feSeschrock  *
399d91236feSeschrock  *	acfail		-> INPUT_LOST
400d91236feSeschrock  *	dcfail		-> INPUT_LOST
401d91236feSeschrock  *	undervoltage	-> INPUT_RANGE
402d91236feSeschrock  *	overvoltage	-> INPUT_RANGE_PRES
403d91236feSeschrock  *	overcurrent	-> INPUT_RANGE_PRES
404d91236feSeschrock  *	overtemp	-> (none)
405d91236feSeschrock  *
406d91236feSeschrock  * If we ever have a need for reading overtemp, we can expand the topo
407d91236feSeschrock  * representation for power supplies, but at the moment this seems unnecessary.
408d91236feSeschrock  */
409d91236feSeschrock /*ARGSUSED*/
410d91236feSeschrock static int
ses_psu_state(topo_mod_t * mod,tnode_t * tn,topo_version_t vers,nvlist_t * in,nvlist_t ** out)411d91236feSeschrock ses_psu_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
412d91236feSeschrock     nvlist_t *in, nvlist_t **out)
413d91236feSeschrock {
414d91236feSeschrock 	nvlist_t *nvl, *props;
415d91236feSeschrock 	boolean_t value;
416d91236feSeschrock 	uint32_t state;
417d91236feSeschrock 	ses_node_t *np;
418d91236feSeschrock 
4190b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL) {
420d91236feSeschrock 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
421d91236feSeschrock 		    "method\n");
422d91236feSeschrock 		return (-1);
423d91236feSeschrock 	}
424d91236feSeschrock 	verify((props = ses_node_props(np)) != NULL);
425d91236feSeschrock 
426d91236feSeschrock 	state = 0;
427d91236feSeschrock 	if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_FAIL,
4287bced3d7SEric Schrock 	    &value) == 0 && value) ||
429d91236feSeschrock 	    (nvlist_lookup_boolean_value(props, SES_PSU_PROP_AC_FAIL,
4307bced3d7SEric Schrock 	    &value) == 0 && value))
431d91236feSeschrock 		state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST;
432d91236feSeschrock 
433d91236feSeschrock 	if (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_UNDER_VOLTAGE,
4347bced3d7SEric Schrock 	    &value) == 0 && value)
435d91236feSeschrock 		state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE;
436d91236feSeschrock 
437d91236feSeschrock 	if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_VOLTAGE,
4387bced3d7SEric Schrock 	    &value) == 0 && value) ||
439d91236feSeschrock 	    (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_CURRENT,
4407bced3d7SEric Schrock 	    &value) == 0 && value))
441d91236feSeschrock 		state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES;
442d91236feSeschrock 
4430b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
4440b32bb8bSEric Schrock 
445d91236feSeschrock 	nvl = NULL;
446d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
447d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
448d91236feSeschrock 	    TOPO_SENSOR_STATE) != 0 ||
449d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
450d91236feSeschrock 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) {
451d91236feSeschrock 		nvlist_free(nvl);
452d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
453d91236feSeschrock 	}
454d91236feSeschrock 
455d91236feSeschrock 	*out = nvl;
456d91236feSeschrock 	return (0);
457d91236feSeschrock }
458d91236feSeschrock 
459d91236feSeschrock /*
460d91236feSeschrock  * Create a facility node, either a sensor or an indicator.
461d91236feSeschrock  */
462d91236feSeschrock static tnode_t *
ses_add_fac_common(topo_mod_t * mod,tnode_t * pnode,const char * name,const char * type,uint64_t nodeid)463d91236feSeschrock ses_add_fac_common(topo_mod_t *mod, tnode_t *pnode, const char *name,
464d91236feSeschrock     const char *type, uint64_t nodeid)
465d91236feSeschrock {
466d91236feSeschrock 	tnode_t *tn;
467d91236feSeschrock 	topo_pgroup_info_t pgi;
468d91236feSeschrock 	int err;
4697bced3d7SEric Schrock 	ses_enum_target_t *stp = topo_node_getspecific(pnode);
470d91236feSeschrock 
471d91236feSeschrock 	if ((tn = topo_node_facbind(mod, pnode, name, type)) == NULL) {
472d91236feSeschrock 		topo_mod_dprintf(mod, "failed to bind facility node %s\n",
473d91236feSeschrock 		    name);
474d91236feSeschrock 		return (NULL);
475d91236feSeschrock 	}
476d91236feSeschrock 
4777bced3d7SEric Schrock 	stp->set_refcount++;
4787bced3d7SEric Schrock 	topo_node_setspecific(tn, stp);
479d91236feSeschrock 
480d91236feSeschrock 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
481d91236feSeschrock 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
482d91236feSeschrock 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
483d91236feSeschrock 	pgi.tpi_version = 1;
484d91236feSeschrock 
485d91236feSeschrock 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
486d91236feSeschrock 		topo_mod_dprintf(mod, "failed to create facility property "
487d91236feSeschrock 		    "group: %s\n", topo_strerror(err));
488d91236feSeschrock 		topo_node_unbind(tn);
489d91236feSeschrock 		return (NULL);
490d91236feSeschrock 	}
491d91236feSeschrock 
492d91236feSeschrock 	/*
493d91236feSeschrock 	 * We need the node-id property for each facility node.
494d91236feSeschrock 	 */
495d91236feSeschrock 	pgi.tpi_name = TOPO_PGROUP_SES;
496d91236feSeschrock 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
497d91236feSeschrock 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
498d91236feSeschrock 	pgi.tpi_version = TOPO_VERSION;
499d91236feSeschrock 
500d91236feSeschrock 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
501d91236feSeschrock 		topo_mod_dprintf(mod, "failed to create ses property "
502d91236feSeschrock 		    "group: %s\n", topo_strerror(err));
503d91236feSeschrock 		topo_node_unbind(tn);
504d91236feSeschrock 		return (NULL);
505d91236feSeschrock 	}
506d91236feSeschrock 
507d91236feSeschrock 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
508d91236feSeschrock 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
509d91236feSeschrock 	    nodeid, &err) != 0) {
510d91236feSeschrock 		topo_mod_dprintf(mod,
511d91236feSeschrock 		    "failed to create property %s: %s\n",
512d91236feSeschrock 		    TOPO_PROP_NODE_ID, topo_strerror(err));
513d91236feSeschrock 		topo_node_unbind(tn);
514d91236feSeschrock 		return (NULL);
515d91236feSeschrock 	}
516d91236feSeschrock 
517d91236feSeschrock 	return (tn);
518d91236feSeschrock }
519d91236feSeschrock 
520d91236feSeschrock /*
521d91236feSeschrock  * Add an indicator.  This can be represented by a single property, or by the
522d91236feSeschrock  * union of two elements when SES is capable of distinguishing between
523d91236feSeschrock  * requested failure and detected failure.
524d91236feSeschrock  */
525d91236feSeschrock static int
ses_add_indicator(topo_mod_t * mod,tnode_t * pnode,uint64_t nodeid,int type,const char * name,const char * propname,const char * altprop)526d91236feSeschrock ses_add_indicator(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
527d91236feSeschrock     int type, const char *name, const char *propname, const char *altprop)
528d91236feSeschrock {
529d91236feSeschrock 	tnode_t *tn;
530d91236feSeschrock 	int err;
531d91236feSeschrock 	nvlist_t *nvl;
532d91236feSeschrock 
533d91236feSeschrock 	/* create facility node and add methods */
534d91236feSeschrock 	if ((tn = ses_add_fac_common(mod, pnode, name,
535d91236feSeschrock 	    TOPO_FAC_TYPE_INDICATOR, nodeid)) == NULL)
536d91236feSeschrock 		return (-1);
537d91236feSeschrock 
538d91236feSeschrock 	if (topo_method_register(mod, tn, ses_indicator_methods) < 0) {
539d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register facility methods\n");
540d91236feSeschrock 		topo_node_unbind(tn);
541d91236feSeschrock 		return (-1);
542d91236feSeschrock 	}
543d91236feSeschrock 
544d91236feSeschrock 	/* set standard properties */
545d91236feSeschrock 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY,
546d91236feSeschrock 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, type, &err) != 0) {
547d91236feSeschrock 		topo_mod_dprintf(mod,
548d91236feSeschrock 		    "failed to set facility node properties: %s\n",
549d91236feSeschrock 		    topo_strerror(err));
550d91236feSeschrock 		topo_node_unbind(tn);
551d91236feSeschrock 		return (-1);
552d91236feSeschrock 	}
553d91236feSeschrock 
554d91236feSeschrock 	/* 'mode' property */
555d91236feSeschrock 	nvl = NULL;
556d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
557d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_METH_SES_MODE_PROP,
558d91236feSeschrock 	    propname) != 0 ||
559d91236feSeschrock 	    (altprop != NULL && nvlist_add_string(nvl,
560d91236feSeschrock 	    TOPO_METH_SES_MODE_ALTPROP, altprop) != 0)) {
561d91236feSeschrock 		nvlist_free(nvl);
562d91236feSeschrock 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
563d91236feSeschrock 		topo_node_unbind(tn);
564d91236feSeschrock 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
565d91236feSeschrock 	}
566d91236feSeschrock 
567d91236feSeschrock 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
568d91236feSeschrock 	    TOPO_LED_MODE, TOPO_TYPE_UINT32, "ses_indicator_mode",
569d91236feSeschrock 	    nvl, &err) != 0) {
570d91236feSeschrock 		nvlist_free(nvl);
571d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register reading method: %s\n",
572d91236feSeschrock 		    topo_strerror(err));
573d91236feSeschrock 		return (-1);
574d91236feSeschrock 	}
575d91236feSeschrock 
576d91236feSeschrock 	if (topo_prop_setmutable(tn, TOPO_PGROUP_FACILITY,
577d91236feSeschrock 	    TOPO_LED_MODE, &err) != 0) {
578d91236feSeschrock 		nvlist_free(nvl);
579d91236feSeschrock 		topo_mod_dprintf(mod, "failed to set property as mutable: %s\n",
580d91236feSeschrock 		    topo_strerror(err));
581d91236feSeschrock 		return (-1);
582d91236feSeschrock 	}
583d91236feSeschrock 
584d91236feSeschrock 	nvlist_free(nvl);
585d91236feSeschrock 	return (0);
586d91236feSeschrock }
587d91236feSeschrock 
588d91236feSeschrock static tnode_t *
ses_add_sensor_common(topo_mod_t * mod,tnode_t * pnode,uint64_t nodeid,const char * name,const char * class,int type)589d91236feSeschrock ses_add_sensor_common(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
590d91236feSeschrock     const char *name, const char *class, int type)
591d91236feSeschrock {
592d91236feSeschrock 	tnode_t *tn;
593d91236feSeschrock 	int err;
594d91236feSeschrock 
595d91236feSeschrock 	/* create facility node and add methods */
596d91236feSeschrock 	if ((tn = ses_add_fac_common(mod, pnode, name,
597d91236feSeschrock 	    TOPO_FAC_TYPE_SENSOR, nodeid)) == NULL)
598d91236feSeschrock 		return (NULL);
599d91236feSeschrock 
600d91236feSeschrock 	if (topo_method_register(mod, tn, ses_sensor_methods) < 0) {
601d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register facility methods\n");
602d91236feSeschrock 		topo_node_unbind(tn);
603d91236feSeschrock 		return (NULL);
604d91236feSeschrock 	}
605d91236feSeschrock 
606d91236feSeschrock 	/* set standard properties */
607d91236feSeschrock 	if (topo_prop_set_string(tn, TOPO_PGROUP_FACILITY,
608d91236feSeschrock 	    TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
609d91236feSeschrock 	    class, &err) != 0 ||
610d91236feSeschrock 	    topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY,
611d91236feSeschrock 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE,
612d91236feSeschrock 	    type, &err) != 0) {
613d91236feSeschrock 		topo_mod_dprintf(mod,
614d91236feSeschrock 		    "failed to set facility node properties: %s\n",
615d91236feSeschrock 		    topo_strerror(err));
616d91236feSeschrock 		topo_node_unbind(tn);
617d91236feSeschrock 		return (NULL);
618d91236feSeschrock 	}
619d91236feSeschrock 
620d91236feSeschrock 	return (tn);
621d91236feSeschrock }
622d91236feSeschrock 
623d91236feSeschrock /*
624d91236feSeschrock  * Add an analog (threshold) sensor to the enclosure.  This is used for fan
625d91236feSeschrock  * speed, voltage, current, and temperature sensors.
626d91236feSeschrock  */
627d91236feSeschrock static int
ses_add_sensor(topo_mod_t * mod,tnode_t * pnode,uint64_t nodeid,const char * name,const ses_sensor_desc_t * sdp)628d91236feSeschrock ses_add_sensor(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
629d91236feSeschrock     const char *name, const ses_sensor_desc_t *sdp)
630d91236feSeschrock {
631d91236feSeschrock 	tnode_t *tn;
632d91236feSeschrock 	int err;
633d91236feSeschrock 	nvlist_t *nvl;
634d91236feSeschrock 
635d91236feSeschrock 	if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name,
636d91236feSeschrock 	    TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL)
637d91236feSeschrock 		return (-1);
638d91236feSeschrock 
639d91236feSeschrock 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY,
640d91236feSeschrock 	    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sdp->sd_units, &err) != 0) {
641d91236feSeschrock 		topo_mod_dprintf(mod,
642d91236feSeschrock 		    "failed to set facility node properties: %s\n",
643d91236feSeschrock 		    topo_strerror(err));
644d91236feSeschrock 		topo_node_unbind(tn);
645d91236feSeschrock 		return (-1);
646d91236feSeschrock 	}
647d91236feSeschrock 
648d91236feSeschrock 	/* 'reading' property */
649d91236feSeschrock 	nvl = NULL;
650d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
651d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_METH_SES_READING_PROP,
652d91236feSeschrock 	    sdp->sd_propname) != 0 ||
653d91236feSeschrock 	    (sdp->sd_multiplier != 0 &&
654d91236feSeschrock 	    nvlist_add_double(nvl, TOPO_METH_SES_READING_MULT,
655d91236feSeschrock 	    sdp->sd_multiplier) != 0)) {
656d91236feSeschrock 		nvlist_free(nvl);
657d91236feSeschrock 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
658d91236feSeschrock 		topo_node_unbind(tn);
659d91236feSeschrock 		return (-1);
660d91236feSeschrock 	}
661d91236feSeschrock 
662d91236feSeschrock 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
663d91236feSeschrock 	    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "ses_sensor_reading",
664d91236feSeschrock 	    nvl, &err) != 0) {
665d91236feSeschrock 		nvlist_free(nvl);
666d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register reading method: %s\n",
667d91236feSeschrock 		    topo_strerror(err));
668d91236feSeschrock 		return (-1);
669d91236feSeschrock 	}
670d91236feSeschrock 
671d91236feSeschrock 	nvlist_free(nvl);
672d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) {
673d91236feSeschrock 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
674d91236feSeschrock 		topo_node_unbind(tn);
675d91236feSeschrock 		return (-1);
676d91236feSeschrock 	}
677d91236feSeschrock 
678d91236feSeschrock 	/* 'state' property */
679d91236feSeschrock 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
680d91236feSeschrock 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state",
681d91236feSeschrock 	    nvl, &err) != 0) {
682d91236feSeschrock 		nvlist_free(nvl);
683d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register state method: %s\n",
684d91236feSeschrock 		    topo_strerror(err));
685d91236feSeschrock 		return (-1);
686d91236feSeschrock 	}
687d91236feSeschrock 
688d91236feSeschrock 	nvlist_free(nvl);
689d91236feSeschrock 	return (0);
690d91236feSeschrock }
691d91236feSeschrock 
692d91236feSeschrock /*
693d91236feSeschrock  * Add a discrete sensor for simple boolean values.  This is used to indicate
694d91236feSeschrock  * externally-detected failures for fans, bays, and enclosures.
695d91236feSeschrock  */
696d91236feSeschrock static int
ses_add_discrete(topo_mod_t * mod,tnode_t * pnode,uint64_t nodeid,const char * name,const char * prop)697d91236feSeschrock ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
698d91236feSeschrock     const char *name, const char *prop)
699d91236feSeschrock {
700d91236feSeschrock 	tnode_t *tn;
701d91236feSeschrock 	int err;
702d91236feSeschrock 	nvlist_t *nvl;
703d91236feSeschrock 
704d91236feSeschrock 	if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name,
705d91236feSeschrock 	    TOPO_SENSOR_CLASS_DISCRETE,
7066efb64caSEric Schrock 	    TOPO_SENSOR_TYPE_GENERIC_FAILURE)) == NULL)
707d91236feSeschrock 		return (-1);
708d91236feSeschrock 
709d91236feSeschrock 	nvl = NULL;
710d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
711d91236feSeschrock 	    nvlist_add_string(nvl, TOPO_METH_SES_STATE_PROP, prop) != 0) {
712d91236feSeschrock 		nvlist_free(nvl);
713d91236feSeschrock 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
714d91236feSeschrock 		topo_node_unbind(tn);
715d91236feSeschrock 		return (-1);
716d91236feSeschrock 	}
717d91236feSeschrock 
718d91236feSeschrock 	/* 'state' property */
719d91236feSeschrock 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
720d91236feSeschrock 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state",
721d91236feSeschrock 	    nvl, &err) != 0) {
722d91236feSeschrock 		nvlist_free(nvl);
723d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register state method: %s\n",
724d91236feSeschrock 		    topo_strerror(err));
725d91236feSeschrock 		return (-1);
726d91236feSeschrock 	}
727d91236feSeschrock 
728d91236feSeschrock 	nvlist_free(nvl);
729d91236feSeschrock 	return (0);
730d91236feSeschrock }
731d91236feSeschrock 
732d91236feSeschrock /*ARGSUSED*/
733d91236feSeschrock static int
ses_add_psu_status(topo_mod_t * mod,tnode_t * pnode,uint64_t nodeid)734d91236feSeschrock ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid)
735d91236feSeschrock {
736d91236feSeschrock 	tnode_t *tn;
737d91236feSeschrock 	int err;
738d91236feSeschrock 	nvlist_t *nvl;
739d91236feSeschrock 
740d91236feSeschrock 	/* create facility node and add methods */
741d91236feSeschrock 	if ((tn = ses_add_sensor_common(mod, pnode, nodeid, "status",
742d91236feSeschrock 	    TOPO_SENSOR_CLASS_DISCRETE,
743d91236feSeschrock 	    TOPO_SENSOR_TYPE_POWER_SUPPLY)) == NULL)
744d91236feSeschrock 		return (-1);
745d91236feSeschrock 
746d91236feSeschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) {
747d91236feSeschrock 		nvlist_free(nvl);
748d91236feSeschrock 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
749d91236feSeschrock 		topo_node_unbind(tn);
750d91236feSeschrock 		return (-1);
751d91236feSeschrock 	}
752d91236feSeschrock 
753d91236feSeschrock 	/* 'state' property */
754d91236feSeschrock 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
755d91236feSeschrock 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_psu_state",
756d91236feSeschrock 	    nvl, &err) != 0) {
757d91236feSeschrock 		nvlist_free(nvl);
758d91236feSeschrock 		topo_mod_dprintf(mod, "failed to register state method: %s\n",
759d91236feSeschrock 		    topo_strerror(err));
760d91236feSeschrock 		return (-1);
761d91236feSeschrock 	}
762d91236feSeschrock 
763d91236feSeschrock 	nvlist_free(nvl);
764d91236feSeschrock 	return (0);
765d91236feSeschrock }
766d91236feSeschrock 
767d91236feSeschrock /*ARGSUSED*/
768d91236feSeschrock int
ses_node_enum_facility(topo_mod_t * mod,tnode_t * tn,topo_version_t vers,nvlist_t * in,nvlist_t ** out)769d91236feSeschrock ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
770d91236feSeschrock     nvlist_t *in, nvlist_t **out)
771d91236feSeschrock {
772d91236feSeschrock 	ses_node_t *np;
773d91236feSeschrock 	nvlist_t *props;
774d91236feSeschrock 	uint64_t type, nodeid;
775d91236feSeschrock 	ses_sensor_desc_t sd = { 0 };
776d91236feSeschrock 
7770b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL)
778d91236feSeschrock 		return (-1);
779d91236feSeschrock 
780d91236feSeschrock 	assert(ses_node_type(np) == SES_NODE_ELEMENT);
781d91236feSeschrock 	nodeid = ses_node_id(np);
782d91236feSeschrock 	verify((props = ses_node_props(np)) != NULL);
783d91236feSeschrock 	verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
784d91236feSeschrock 
785d91236feSeschrock 	if (type != SES_ET_DEVICE && type != SES_ET_ARRAY_DEVICE &&
7860b32bb8bSEric Schrock 	    type != SES_ET_COOLING && type != SES_ET_POWER_SUPPLY) {
7870b32bb8bSEric Schrock 		ses_node_unlock(mod, tn);
788d91236feSeschrock 		return (0);
7890b32bb8bSEric Schrock 	}
790d91236feSeschrock 
791d91236feSeschrock 	/*
792d91236feSeschrock 	 * Every element supports an 'ident' indicator.  All elements also
793d91236feSeschrock 	 * support a 'fail' indicator, but the properties used to represent
794d91236feSeschrock 	 * this condition differs between elements.
795d91236feSeschrock 	 */
796d91236feSeschrock 	if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident",
797d91236feSeschrock 	    SES_PROP_IDENT, NULL) != 0)
7980b32bb8bSEric Schrock 		goto error;
799d91236feSeschrock 
800d91236feSeschrock 	switch (type) {
801d91236feSeschrock 	case SES_ET_DEVICE:
802d91236feSeschrock 	case SES_ET_ARRAY_DEVICE:
803d91236feSeschrock 		/*
804d91236feSeschrock 		 * Disks support an additional 'ok2rm' indicator, as well as
805d91236feSeschrock 		 * externally detected 'fail' sensor.
806d91236feSeschrock 		 */
807d91236feSeschrock 		if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
808d91236feSeschrock 		    "fail", SES_DEV_PROP_FAULT_RQSTD,
809d91236feSeschrock 		    SES_DEV_PROP_FAULT_SENSED) != 0 ||
810d91236feSeschrock 		    ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_OK2RM,
811d91236feSeschrock 		    "ok2rm", SES_PROP_RMV, SES_PROP_RMV) != 0 ||
812d91236feSeschrock 		    ses_add_discrete(mod, tn, nodeid, "fault",
813d91236feSeschrock 		    SES_DEV_PROP_FAULT_SENSED) != 0)
8140b32bb8bSEric Schrock 			goto error;
815d91236feSeschrock 		break;
816d91236feSeschrock 
817d91236feSeschrock 	case SES_ET_COOLING:
818d91236feSeschrock 		/*
819d91236feSeschrock 		 * Add the fan speed sensor, and a discrete sensor for
820d91236feSeschrock 		 * detecting failure.
821d91236feSeschrock 		 */
8227bced3d7SEric Schrock 		sd.sd_type = TOPO_SENSOR_TYPE_THRESHOLD_STATE;
823d91236feSeschrock 		sd.sd_units = TOPO_SENSOR_UNITS_RPM;
824d91236feSeschrock 		sd.sd_propname = SES_COOLING_PROP_FAN_SPEED;
825d91236feSeschrock 		if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
826d91236feSeschrock 		    "fail", SES_PROP_FAIL, NULL) != 0 ||
827d91236feSeschrock 		    ses_add_sensor(mod, tn, nodeid, "speed", &sd) != 0 ||
828d91236feSeschrock 		    ses_add_discrete(mod, tn, nodeid, "fault",
829db7d7001SYuri Pankov 		    SES_PROP_FAIL) != 0)
8300b32bb8bSEric Schrock 			goto error;
831d91236feSeschrock 		break;
832d91236feSeschrock 
833d91236feSeschrock 	case SES_ET_POWER_SUPPLY:
834d91236feSeschrock 		/*
835d91236feSeschrock 		 * For power supplies, we have a number of different sensors:
836d91236feSeschrock 		 * acfail, dcfail, overtemp, undervoltate, overvoltage,
837d91236feSeschrock 		 * and overcurrent.  Rather than expose these all as individual
838d91236feSeschrock 		 * sensors, we lump them together into a 'status' sensor of
839d91236feSeschrock 		 * type TOPO_SENSOR_TYPE_POWER_SUPPLY and export the
840d91236feSeschrock 		 * appropriate status flags as defined by the libtopo standard.
841d91236feSeschrock 		 */
842d91236feSeschrock 		if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
843d91236feSeschrock 		    "fail", SES_PROP_FAIL, NULL) != 0)
8440b32bb8bSEric Schrock 			goto error;
845d91236feSeschrock 
846d91236feSeschrock 		if (ses_add_psu_status(mod, tn, nodeid) != 0)
8470b32bb8bSEric Schrock 			goto error;
848d91236feSeschrock 		break;
849d91236feSeschrock 
850d91236feSeschrock 	default:
851d91236feSeschrock 		return (0);
852d91236feSeschrock 	}
853d91236feSeschrock 
8540b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
855d91236feSeschrock 	return (0);
8560b32bb8bSEric Schrock 
8570b32bb8bSEric Schrock error:
8580b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
8590b32bb8bSEric Schrock 	return (-1);
860d91236feSeschrock }
861d91236feSeschrock 
862d91236feSeschrock /*
863d91236feSeschrock  * Add enclosure-wide sensors (temperature, voltage, and current) beneath the
864d91236feSeschrock  * given aggregate.
865d91236feSeschrock  */
866d91236feSeschrock static int
ses_add_enclosure_sensors(topo_mod_t * mod,tnode_t * tn,ses_node_t * agg,uint64_t type)867d91236feSeschrock ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg,
868d91236feSeschrock     uint64_t type)
869d91236feSeschrock {
870d91236feSeschrock 	ses_node_t *child;
871d91236feSeschrock 	const char *defaultname;
872d91236feSeschrock 	char *desc, *name;
873d91236feSeschrock 	char rawname[64];
874d91236feSeschrock 	nvlist_t *props, *aprops;
875d91236feSeschrock 	uint64_t index, nodeid;
876d91236feSeschrock 	ses_sensor_desc_t sd = { 0 };
877d91236feSeschrock 	size_t len;
878d91236feSeschrock 
879d91236feSeschrock 	switch (type) {
880d91236feSeschrock 	case SES_ET_TEMPERATURE_SENSOR:
881d91236feSeschrock 		sd.sd_type = TOPO_SENSOR_TYPE_TEMP;
882d91236feSeschrock 		sd.sd_units = TOPO_SENSOR_UNITS_DEGREES_C;
883d91236feSeschrock 		sd.sd_propname = SES_TEMP_PROP_TEMP;
884d91236feSeschrock 		defaultname = "temperature";
885d91236feSeschrock 		break;
886d91236feSeschrock 
887d91236feSeschrock 	case SES_ET_VOLTAGE_SENSOR:
888d91236feSeschrock 		sd.sd_type = TOPO_SENSOR_TYPE_VOLTAGE;
889d91236feSeschrock 		sd.sd_units = TOPO_SENSOR_UNITS_VOLTS;
890d91236feSeschrock 		sd.sd_propname = SES_VS_PROP_VOLTAGE_MV;
891d91236feSeschrock 		sd.sd_multiplier = 0.001;
892d91236feSeschrock 		defaultname = "voltage";
893d91236feSeschrock 		break;
894d91236feSeschrock 
895d91236feSeschrock 	case SES_ET_CURRENT_SENSOR:
896d91236feSeschrock 		sd.sd_type = TOPO_SENSOR_TYPE_CURRENT;
897d91236feSeschrock 		sd.sd_units = TOPO_SENSOR_UNITS_AMPS;
898d91236feSeschrock 		sd.sd_propname = SES_CS_PROP_CURRENT_MA;
899d91236feSeschrock 		sd.sd_multiplier = 0.001;
900d91236feSeschrock 		defaultname = "current";
901d91236feSeschrock 		break;
902d91236feSeschrock 
903d91236feSeschrock 	default:
904d91236feSeschrock 		return (0);
905d91236feSeschrock 	}
906d91236feSeschrock 
907d91236feSeschrock 	aprops = ses_node_props(agg);
908d91236feSeschrock 
909d91236feSeschrock 	for (child = ses_node_child(agg); child != NULL;
910d91236feSeschrock 	    child = ses_node_sibling(child)) {
911d91236feSeschrock 		/*
912d91236feSeschrock 		 * The only tricky part here is getting the name for the
913d91236feSeschrock 		 * sensor, where we follow the algorithm of the standard
914d91236feSeschrock 		 * elements.
915d91236feSeschrock 		 */
916d91236feSeschrock 		props = ses_node_props(child);
917d91236feSeschrock 		nodeid = ses_node_id(child);
918d91236feSeschrock 		if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
919d91236feSeschrock 		    &index) != 0)
920d91236feSeschrock 			continue;
921d91236feSeschrock 
922d91236feSeschrock 		if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION,
923d91236feSeschrock 		    &desc) == 0 && desc[0] != '\0') {
924d91236feSeschrock 			(void) strlcpy(rawname, desc, sizeof (rawname));
925d91236feSeschrock 		} else {
926d91236feSeschrock 			if (nvlist_lookup_string(aprops,
927d91236feSeschrock 			    SES_PROP_CLASS_DESCRIPTION, &desc) != 0 ||
928d91236feSeschrock 			    desc[0] == '\0')
929d91236feSeschrock 				desc = (char *)defaultname;
930d91236feSeschrock 
931d91236feSeschrock 			len = strlen(desc);
932d91236feSeschrock 			while (len > 0 && desc[len - 1] == ' ')
933d91236feSeschrock 				len--;
934d91236feSeschrock 
935d91236feSeschrock 			(void) snprintf(rawname, sizeof (rawname),
936d91236feSeschrock 			    "%.*s %llu", len, desc, index);
937d91236feSeschrock 		}
938d91236feSeschrock 
93900d7a6fbSRob Johnston 		if ((name = topo_mod_clean_str(mod, rawname)) == NULL)
940d91236feSeschrock 			return (-1);
941d91236feSeschrock 
942d91236feSeschrock 		if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) {
943d91236feSeschrock 			topo_mod_strfree(mod, name);
944d91236feSeschrock 			return (-1);
945d91236feSeschrock 		}
946d91236feSeschrock 
947d91236feSeschrock 		topo_mod_strfree(mod, name);
948d91236feSeschrock 	}
949d91236feSeschrock 
950d91236feSeschrock 	return (0);
951d91236feSeschrock }
952d91236feSeschrock 
953d91236feSeschrock /*ARGSUSED*/
954d91236feSeschrock int
ses_enc_enum_facility(topo_mod_t * mod,tnode_t * tn,topo_version_t vers,nvlist_t * in,nvlist_t ** out)955d91236feSeschrock ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
956d91236feSeschrock     nvlist_t *in, nvlist_t **out)
957d91236feSeschrock {
958d91236feSeschrock 	ses_node_t *np, *agg;
959d91236feSeschrock 	nvlist_t *aprops;
960d91236feSeschrock 	uint64_t type, nodeid;
961d91236feSeschrock 
9620b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL)
963d91236feSeschrock 		return (-1);
964d91236feSeschrock 
965d91236feSeschrock 	assert(ses_node_type(np) == SES_NODE_ENCLOSURE);
966d91236feSeschrock 	nodeid = ses_node_id(np);
967d91236feSeschrock 
968d91236feSeschrock 	/*
969d91236feSeschrock 	 * 'ident' and 'fail' LEDs, and 'fault' sensor.
970d91236feSeschrock 	 */
971d91236feSeschrock 	if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident",
972d91236feSeschrock 	    SES_PROP_IDENT, NULL) != 0 ||
973d91236feSeschrock 	    ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, "fail",
974d91236feSeschrock 	    SES_PROP_FAIL_REQ, SES_PROP_FAIL) != 0 ||
975d91236feSeschrock 	    ses_add_discrete(mod, tn, nodeid, "fault", SES_PROP_FAIL) != 0)
9760b32bb8bSEric Schrock 		goto error;
977d91236feSeschrock 
978d91236feSeschrock 	/*
979d91236feSeschrock 	 * Environmental sensors (temperature, voltage, current).  We have no
980d91236feSeschrock 	 * way of knowing if any of these sensors correspond to a particular
981d91236feSeschrock 	 * element, so we just attach them to the enclosure as a whole.  In the
982d91236feSeschrock 	 * future, some vendor-specific libses plugin knowledge could let us
983d91236feSeschrock 	 * make this correlation clearer.
984d91236feSeschrock 	 */
985d91236feSeschrock 	for (agg = ses_node_child(np); agg != NULL;
986d91236feSeschrock 	    agg = ses_node_sibling(agg)) {
987d91236feSeschrock 		if (ses_node_type(agg) != SES_NODE_AGGREGATE)
988d91236feSeschrock 			continue;
989d91236feSeschrock 
990d91236feSeschrock 		verify((aprops = ses_node_props(agg)) != NULL);
991d91236feSeschrock 		if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE,
992d91236feSeschrock 		    &type) != 0)
993d91236feSeschrock 			continue;
994d91236feSeschrock 
995d91236feSeschrock 		if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0)
9960b32bb8bSEric Schrock 			goto error;
997d91236feSeschrock 	}
998d91236feSeschrock 
9990b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
1000d91236feSeschrock 	return (0);
10010b32bb8bSEric Schrock 
10020b32bb8bSEric Schrock error:
10030b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
10040b32bb8bSEric Schrock 	return (-1);
1005d91236feSeschrock }
1006