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