xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/ses/ses_facility.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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