xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/fac_prov_ahci/fac_prov_ahci.c (revision 8222814ef8560ee0ba222eca8ca5acffc6cd0e44)
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) 2018, Joyent, Inc.
14  */
15 
16 #include <unistd.h>
17 #include <stropts.h>
18 #include <fcntl.h>
19 #include <libnvpair.h>
20 #include <string.h>
21 #include <strings.h>
22 #include <sys/fm/protocol.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <fm/libtopo.h>
26 #include <fm/topo_mod.h>
27 #include <libgen.h>
28 
29 #include "sys/sata/adapters/ahci/ahciem.h"
30 
31 #define	TOPO_METH_AHCI_LED_MODE_VERSION	0
32 
33 /*
34  * This enum is used to more clearly demonstrate the mapping between libtopo's
35  * concept of LED types and the types represented in AHCI.
36  */
37 typedef enum {
38 	AHCI_FAC_IDENT,
39 	AHCI_FAC_FAULT
40 } ahci_fac_led_t;
41 
42 static int fac_prov_ahci_enum(topo_mod_t *, tnode_t *, const char *,
43     topo_instance_t, topo_instance_t, void *, void *);
44 
45 /*
46  * ahci facility provider methods
47  */
48 static int ahci_led_mode(topo_mod_t *, tnode_t *, topo_version_t,
49     nvlist_t *, nvlist_t **);
50 
51 static const topo_modops_t ahci_ops = { fac_prov_ahci_enum, NULL };
52 
53 static const topo_modinfo_t ahci_info =
54 	{ "ahci facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION,
55 	&ahci_ops };
56 
57 static const topo_method_t ahci_fac_methods[] = {
58 	{ "ahci_led_mode", TOPO_PROP_METH_DESC,
59 	    TOPO_METH_AHCI_LED_MODE_VERSION,
60 	    TOPO_STABILITY_INTERNAL, ahci_led_mode },
61 	{ NULL }
62 };
63 
64 int
65 _topo_init(topo_mod_t *mod, topo_version_t version)
66 {
67 	if (getenv("TOPOFACAHCIDEBUG") != NULL)
68 		topo_mod_setdebug(mod);
69 
70 	return (topo_mod_register(mod, &ahci_info, TOPO_VERSION));
71 }
72 
73 void
74 _topo_fini(topo_mod_t *mod)
75 {
76 	topo_mod_unregister(mod);
77 }
78 
79 /*
80  * Get or set the LED state for a given LED of type (facility node type, port).
81  * This function returns -1 on error.
82  */
83 static int
84 do_led_control(topo_mod_t *mod, const char *devctl, uint32_t port,
85     ahci_fac_led_t fac_type, uint32_t *ledmode, boolean_t set)
86 {
87 	int fd = -1, ret = -1;
88 
89 	if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) {
90 		topo_mod_dprintf(mod, "devctl open failed: %s",
91 		    strerror(errno));
92 		goto out;
93 	}
94 
95 	if (set) {
96 		ahci_ioc_em_set_t ahci_set;
97 		uint32_t led_status = *ledmode;
98 		uint_t op, leds;
99 
100 		switch (fac_type) {
101 		case AHCI_FAC_IDENT:
102 			leds = AHCI_EM_LED_IDENT_ENABLE;
103 			break;
104 		case AHCI_FAC_FAULT:
105 			leds = AHCI_EM_LED_FAULT_ENABLE;
106 			break;
107 		default:
108 			topo_mod_dprintf(mod, "invalid facility node type: %d",
109 			    fac_type);
110 			goto out;
111 		}
112 
113 		if (led_status) {
114 			op = AHCI_EM_IOC_SET_OP_ADD;
115 		} else {
116 			op = AHCI_EM_IOC_SET_OP_REM;
117 		}
118 
119 		bzero(&ahci_set, sizeof (ahci_set));
120 		ahci_set.aiems_port = port;
121 		ahci_set.aiems_op = op;
122 		ahci_set.aiems_leds = leds;
123 
124 		if (ioctl(fd, AHCI_EM_IOC_SET, &ahci_set) == -1) {
125 			topo_mod_dprintf(mod, "ioctl failed: %s",
126 			    strerror(errno));
127 			goto out;
128 		}
129 	} else {
130 		uint_t led_set = 0;
131 		ahci_ioc_em_get_t ahci_get;
132 
133 		bzero(&ahci_get, sizeof (ahci_get));
134 		if (ioctl(fd, AHCI_EM_IOC_GET, &ahci_get) == -1) {
135 			topo_mod_dprintf(mod, "led control ioctl failed: %s",
136 			    strerror(errno));
137 			goto out;
138 		}
139 
140 		switch (fac_type) {
141 		case AHCI_FAC_IDENT:
142 			led_set = ahci_get.aiemg_status[port] &
143 			    AHCI_EM_LED_IDENT_ENABLE;
144 			break;
145 		case AHCI_FAC_FAULT:
146 			led_set = ahci_get.aiemg_status[port] &
147 			    AHCI_EM_LED_FAULT_ENABLE;
148 			break;
149 		default:
150 			topo_mod_dprintf(mod, "invalid facility node type: %d",
151 			    fac_type);
152 			goto out;
153 		}
154 
155 		*ledmode = (led_set != 0) ?
156 		    TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
157 	}
158 	ret = 0;
159 
160 out:
161 	if (fd >= 0) {
162 		(void) close(fd);
163 	}
164 	return (ret);
165 }
166 
167 static int
168 ahci_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
169     nvlist_t *in, nvlist_t **nvout)
170 {
171 	int err, ret = 0;
172 	tnode_t *pnode = topo_node_parent(node);
173 	uint32_t type, ledmode = 0, ahci_port;
174 	nvlist_t *pargs, *nvl;
175 	char *devctl = NULL;
176 	boolean_t set;
177 	ahci_fac_led_t fac_type;
178 
179 	if (vers > TOPO_METH_AHCI_LED_MODE_VERSION) {
180 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
181 	}
182 
183 	if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
184 	    &type, &err) != 0) {
185 		topo_mod_dprintf(mod, "%s: Failed to lookup %s property "
186 		    "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err));
187 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
188 	}
189 
190 	/*
191 	 * While the AHCI specification includes bits for locate, fault, and
192 	 * activity LEDs, we generally only need to account for locate and fault
193 	 * LEDs, as activity LEDs are typically disabled in hardware.
194 	 */
195 	switch (type) {
196 	case (TOPO_LED_TYPE_SERVICE):
197 		fac_type = AHCI_FAC_FAULT;
198 		break;
199 	case (TOPO_LED_TYPE_LOCATE):
200 		fac_type = AHCI_FAC_IDENT;
201 		break;
202 	default:
203 		topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__,
204 		    type);
205 		ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
206 		return (ret);
207 	}
208 
209 	if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
210 	    TOPO_BINDING_DEVCTL, &devctl, &err) != 0 ||
211 	    topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
212 	    TOPO_BINDING_PORT, &ahci_port, &err) != 0) {
213 		topo_mod_dprintf(mod, "%s: Facility was missing ahci binding "
214 		    "properties\n", __func__);
215 		ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
216 		goto out;
217 	}
218 
219 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
220 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
221 		/*
222 		 * Set the LED mode.
223 		 */
224 		set = B_TRUE;
225 
226 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
227 		    &ledmode)) != 0) {
228 			topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair "
229 			    "(%s)\n", __func__, TOPO_PROP_VAL_VAL,
230 			    strerror(ret));
231 			ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
232 			goto out;
233 
234 		}
235 		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
236 		    ledmode ? "ON" : "OFF");
237 	} else {
238 		/*
239 		 * Get the LED mode
240 		 */
241 		set = B_FALSE;
242 		topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__);
243 	}
244 	ret = do_led_control(mod, devctl, ahci_port, fac_type, &ledmode, set);
245 
246 	if (ret == -1) {
247 		topo_mod_dprintf(mod, "%s: do_led_control failed", __func__);
248 		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
249 		goto out;
250 	}
251 
252 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
253 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
254 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
255 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
256 		topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n",
257 		    __func__);
258 		nvlist_free(nvl);
259 		ret = topo_mod_seterrno(mod, EMOD_NOMEM);
260 		goto out;
261 	}
262 	*nvout = nvl;
263 
264 out:
265 	topo_mod_strfree(mod, devctl);
266 	return (ret);
267 }
268 
269 static int
270 fac_prov_ahci_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
271     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
272 {
273 
274 	if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) {
275 		if (topo_method_register(mod, rnode, ahci_fac_methods) != 0) {
276 			topo_mod_dprintf(mod, "%s: topo_method_register() "
277 			    "failed: %s", __func__, topo_mod_errmsg(mod));
278 			return (-1);
279 		}
280 		return (0);
281 	}
282 
283 	topo_mod_dprintf(mod, "%s: unexpected node flags 0x%x", __func__,
284 	    topo_node_flags(rnode));
285 	return (-1);
286 }
287