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 }