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) 2012, 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
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
70 _topo_fini(topo_mod_t *mod)
71 {
72 topo_mod_unregister(mod);
73 }
74
75 static int
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 lc.Command = set ? MPTSAS_LEDCTL_FLAG_SET : MPTSAS_LEDCTL_FLAG_GET;
83 lc.Enclosure = enclosure;
84 lc.Slot = slot;
85 lc.Led = led;
86 lc.LedStatus = *ledmode;
87
88 fd = open(devctl, O_RDWR);
89 if ((fd = open(devctl, O_RDWR)) == -1) {
90 topo_mod_dprintf(mod, "devctl open failed: %s",
91 strerror(errno));
92 return (-1);
93 }
94
95 if (ioctl(fd, MPTIOCTL_LED_CONTROL, &lc) == -1) {
96 topo_mod_dprintf(mod, "led control ioctl failed: %s",
97 strerror(errno));
98 (void) close(fd);
99 return (-1);
100 }
101
102 *ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
103
104 (void) close(fd);
105 return (0);
106 }
107
108 static int
109 mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
110 nvlist_t *in, nvlist_t **out)
111 {
112 int err, ret = 0;
113 tnode_t *pnode = topo_node_parent(node);
114 uint32_t type, ledmode = 0;
115 nvlist_t *pargs, *nvl;
116 char *driver = NULL, *devctl = NULL;
117 uint32_t enclosure, slot;
118 uint8_t mptsas_led;
119 boolean_t set;
120
121 if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION)
122 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
123
124 if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
125 TOPO_BINDING_DRIVER, &driver, &err) != 0 ||
126 strcmp("mpt_sas", driver) != 0) {
127 topo_mod_dprintf(mod, "%s: Facility driver was not mpt_sas",
128 __func__);
129 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
130 goto out;
131 }
132 if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
133 &type, &err) != 0) {
134 topo_mod_dprintf(mod, "%s: Failed to lookup %s property "
135 "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err));
136 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
137 }
138 switch (type) {
139 case (TOPO_LED_TYPE_SERVICE):
140 mptsas_led = MPTSAS_LEDCTL_LED_FAIL;
141 break;
142 case (TOPO_LED_TYPE_LOCATE):
143 mptsas_led = MPTSAS_LEDCTL_LED_IDENT;
144 break;
145 case (TOPO_LED_TYPE_OK2RM):
146 mptsas_led = MPTSAS_LEDCTL_LED_OK2RM;
147 break;
148 default:
149 topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__,
150 type);
151 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
152 }
153 if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
154 TOPO_BINDING_DEVCTL, &devctl, &err) != 0 ||
155 topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
156 TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 ||
157 topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
158 TOPO_BINDING_SLOT, &slot, &err) != 0) {
159 topo_mod_dprintf(mod, "%s: Facility was missing mpt_sas binding"
160 " properties\n", __func__);
161 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
162 goto out;
163 }
164
165 if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
166 nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
167 /*
168 * Set the LED mode
169 */
170 set = B_TRUE;
171 if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
172 &ledmode)) != 0) {
173 topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair "
174 "(%s)\n", __func__, TOPO_PROP_VAL_VAL,
175 strerror(ret));
176 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
177 goto out;
178 }
179 topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
180 ledmode ? "ON" : "OFF");
181 } else {
182 /*
183 * Get the LED mode
184 */
185 set = B_FALSE;
186 topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__);
187 }
188
189 if (do_led_control(mod, devctl, enclosure, slot, mptsas_led, &ledmode,
190 set) != 0) {
191 topo_mod_dprintf(mod, "%s: do_led_control failed", __func__);
192 ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
193 goto out;
194 }
195
196 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
197 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
198 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
199 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
200 topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n",
201 __func__);
202 nvlist_free(nvl);
203 ret = topo_mod_seterrno(mod, EMOD_NOMEM);
204 goto out;
205 }
206 *out = nvl;
207
208 out:
209 if (driver != NULL)
210 topo_mod_strfree(mod, driver);
211 if (devctl != NULL)
212 topo_mod_strfree(mod, devctl);
213 return (ret);
214 }
215
216 /*ARGSUSED*/
217 static int
218 fac_prov_mptsas_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
219 topo_instance_t min, topo_instance_t max, void *arg, void *unused)
220 {
221 if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) {
222 if (topo_method_register(mod, rnode, mptsas_fac_methods) != 0) {
223 topo_mod_dprintf(mod, "%s: topo_method_register() "
224 "failed: %s", __func__, topo_mod_errmsg(mod));
225 return (-1);
226 }
227 return (0);
228 }
229
230 topo_mod_dprintf(mod, "%s: unexpected node flags %x", __func__,
231 topo_node_flags(rnode));
232 return (-1);
233 }