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