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