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