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