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