1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
14  */
15 
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <strings.h>
22 #include <limits.h>
23 #include <alloca.h>
24 #include <errno.h>
25 #include <libnvpair.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/param.h>
29 #include <sys/fm/protocol.h>
30 #include <fm/libtopo.h>
31 #include <fm/topo_mod.h>
32 
33 #include "sys/scsi/adapters/mpt_sas/mptsas_ioctl.h"
34 
35 #define	TOPO_METH_MPTSAS_LED_MODE_VERSION	0
36 
37 static int fac_prov_mptsas_enum(topo_mod_t *, tnode_t *, const char *,
38     topo_instance_t, topo_instance_t, void *, void *);
39 
40 /*
41  * mpt_sas facility provider methods
42  */
43 static int mptsas_led_mode(topo_mod_t *, tnode_t *, topo_version_t,
44     nvlist_t *, nvlist_t **);
45 
46 const topo_modops_t mptsas_ops = { fac_prov_mptsas_enum, NULL };
47 
48 const topo_modinfo_t mptsas_info =
49 	{ "mpt_sas facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION,
50 	&mptsas_ops };
51 
52 static const topo_method_t mptsas_fac_methods[] = {
53 	{ "mptsas_led_mode", TOPO_PROP_METH_DESC,
54 	    TOPO_METH_MPTSAS_LED_MODE_VERSION,
55 	    TOPO_STABILITY_INTERNAL, mptsas_led_mode },
56 	{ NULL }
57 };
58 
59 /*ARGSUSED*/
60 int
_topo_init(topo_mod_t * mod,topo_version_t version)61 _topo_init(topo_mod_t *mod, topo_version_t version)
62 {
63 	if (getenv("TOPOFACMPTSASDEBUG") != NULL)
64 		topo_mod_setdebug(mod);
65 
66 	return (topo_mod_register(mod, &mptsas_info, TOPO_VERSION));
67 }
68 
69 void
_topo_fini(topo_mod_t * mod)70 _topo_fini(topo_mod_t *mod)
71 {
72 	topo_mod_unregister(mod);
73 }
74 
75 static int
do_led_control(topo_mod_t * mod,char * devctl,uint16_t enclosure,uint16_t slot,uint8_t led,uint32_t * ledmode,boolean_t set)76 do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure,
77     uint16_t slot, uint8_t led, uint32_t *ledmode, boolean_t set)
78 {
79 	int fd;
80 	mptsas_led_control_t lc;
81 
82 	bzero(&lc, sizeof (lc));
83 
84 	lc.Command = set ? MPTSAS_LEDCTL_FLAG_SET : MPTSAS_LEDCTL_FLAG_GET;
85 	lc.Enclosure = enclosure;
86 	lc.Slot = slot;
87 	lc.Led = led;
88 	lc.LedStatus = *ledmode;
89 
90 	if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) {
91 		topo_mod_dprintf(mod, "devctl open failed: %s",
92 		    strerror(errno));
93 		return (-1);
94 	}
95 
96 	if (ioctl(fd, MPTIOCTL_LED_CONTROL, &lc) == -1) {
97 		if (errno == ENOENT) {
98 			/*
99 			 * If there is not presently a target attached for
100 			 * a particular enclosure/slot pair then the driver
101 			 * does not track LED status for this bay.  Assume
102 			 * all LEDs are off.
103 			 */
104 			lc.LedStatus = 0;
105 		} else {
106 			topo_mod_dprintf(mod, "led control ioctl failed: %s",
107 			    strerror(errno));
108 			(void) close(fd);
109 			return (-1);
110 		}
111 	}
112 
113 	*ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
114 
115 	(void) close(fd);
116 	return (0);
117 }
118 
119 static int
mptsas_led_mode(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** nvout)120 mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
121     nvlist_t *in, nvlist_t **nvout)
122 {
123 	int err, ret = 0;
124 	tnode_t *pnode = topo_node_parent(node);
125 	uint32_t type, ledmode = 0;
126 	nvlist_t *pargs, *nvl;
127 	char *driver = NULL, *devctl = NULL;
128 	uint32_t enclosure, slot;
129 	uint8_t mptsas_led;
130 	boolean_t set;
131 
132 	if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION)
133 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
134 
135 	if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
136 	    TOPO_BINDING_DRIVER, &driver, &err) != 0 ||
137 	    strcmp("mpt_sas", driver) != 0) {
138 		topo_mod_dprintf(mod, "%s: Facility driver was not mpt_sas",
139 		    __func__);
140 		ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
141 		goto out;
142 	}
143 	if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
144 	    &type, &err) != 0) {
145 		topo_mod_dprintf(mod, "%s: Failed to lookup %s property "
146 		    "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err));
147 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
148 	}
149 	switch (type) {
150 	case (TOPO_LED_TYPE_SERVICE):
151 		mptsas_led = MPTSAS_LEDCTL_LED_FAIL;
152 		break;
153 	case (TOPO_LED_TYPE_LOCATE):
154 		mptsas_led = MPTSAS_LEDCTL_LED_IDENT;
155 		break;
156 	case (TOPO_LED_TYPE_OK2RM):
157 		mptsas_led = MPTSAS_LEDCTL_LED_OK2RM;
158 		break;
159 	default:
160 		topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__,
161 		    type);
162 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
163 	}
164 	if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
165 	    TOPO_BINDING_DEVCTL, &devctl, &err) != 0 ||
166 	    topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
167 	    TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 ||
168 	    topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
169 	    TOPO_BINDING_SLOT, &slot, &err) != 0) {
170 		topo_mod_dprintf(mod, "%s: Facility was missing mpt_sas binding"
171 		    " properties\n", __func__);
172 		ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
173 		goto out;
174 	}
175 
176 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
177 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
178 		/*
179 		 * Set the LED mode
180 		 */
181 		set = B_TRUE;
182 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
183 		    &ledmode)) != 0) {
184 			topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair "
185 			    "(%s)\n", __func__, TOPO_PROP_VAL_VAL,
186 			    strerror(ret));
187 			ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
188 			goto out;
189 		}
190 		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
191 		    ledmode ? "ON" : "OFF");
192 	} else {
193 		/*
194 		 * Get the LED mode
195 		 */
196 		set = B_FALSE;
197 		topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__);
198 	}
199 
200 	if (do_led_control(mod, devctl, enclosure, slot, mptsas_led, &ledmode,
201 	    set) != 0) {
202 		topo_mod_dprintf(mod, "%s: do_led_control failed", __func__);
203 		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
204 		goto out;
205 	}
206 
207 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
208 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
209 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
210 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
211 		topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n",
212 		    __func__);
213 		nvlist_free(nvl);
214 		ret = topo_mod_seterrno(mod, EMOD_NOMEM);
215 		goto out;
216 	}
217 	*nvout = nvl;
218 
219 out:
220 	if (driver != NULL)
221 		topo_mod_strfree(mod, driver);
222 	if (devctl != NULL)
223 		topo_mod_strfree(mod, devctl);
224 	return (ret);
225 }
226 
227 /*ARGSUSED*/
228 static int
fac_prov_mptsas_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * unused)229 fac_prov_mptsas_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
230     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
231 {
232 	if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) {
233 		if (topo_method_register(mod, rnode, mptsas_fac_methods) != 0) {
234 			topo_mod_dprintf(mod, "%s: topo_method_register() "
235 			    "failed: %s", __func__, topo_mod_errmsg(mod));
236 			return (-1);
237 		}
238 		return (0);
239 	}
240 
241 	topo_mod_dprintf(mod, "%s: unexpected node flags %x", __func__,
242 	    topo_node_flags(rnode));
243 	return (-1);
244 }
245