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 2017 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 #include <libnvpair.h>
  17 #include <scsi/libses.h>
  18 #include <scsi/libses_plugin.h>
  19 #include <scsi/plugins/ses/framework/ses2_impl.h>
  20 
  21 /*
  22  * This is a plugin for Dell's MD3060e JBOD.  It updates libses'
  23  * ses-description field to indicate drawer and slot numbers,
  24  * and overrides the bay number if the invalid bit is set for the
  25  * AES descriptor.
  26  */
  27 
  28 /*
  29  * Override bay number if the invalid bit is set for the AES descriptor.
  30  * This is modeled after the LENOVO-D1224J12ESM3P plugin.
  31  */
  32 static int
  33 dell_fix_bay(ses_plugin_t *sp, ses_node_t *np)
  34 {
  35         ses2_aes_descr_eip_impl_t *dep;
  36         ses2_aes_descr_sas0_eip_impl_t *s0ep;
  37         size_t len;
  38         int nverr;
  39         nvlist_t *props = ses_node_props(np);
  40 
  41         /*
  42          * The spec conveniently defines the bay number as part of the
  43          * additional element status descriptor.  However, the AES descriptor
  44          * is technically only valid if the device is inserted.
  45          * Thankfully, the Dell enclosure defines this value even if
  46          * the invalid bit is set, so we override bay value, even for empty
  47          * bays.
  48          */
  49         if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
  50             SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
  51                 return (0);
  52 
  53         if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS ||
  54             !dep->sadei_eip || !dep->sadei_invalid)
  55                 return (0);
  56 
  57         s0ep = (ses2_aes_descr_sas0_eip_impl_t *)dep->sadei_protocol_specific;
  58 
  59         SES_NV_ADD(uint64, nverr, props, SES_PROP_BAY_NUMBER,
  60             s0ep->sadsi_bay_number);
  61 
  62         return (0);
  63 }
  64 
  65 /*
  66  * This updates libses ses-description field for Dell's MD3060e JBOD.
  67  * This JBOD is special because it has a physical label attached to it
  68  * which splits the slot numbering into 5 drawers, each having slots 0-11.
  69  * The description and slot numbering we get from the JBOD has slots
  70  * numbered 1-60. We map these 1-60 description "SLOT # " strings
  71  * into "Drawer X, Slot Y. ( Global SLOT # )" strings.
  72  */
  73 
  74 /*ARGSUSED*/
  75 static int
  76 dell_parse_node(ses_plugin_t *sp, ses_node_t *np)
  77 {
  78         uint64_t type, bay;
  79         int nverr, rc;
  80         nvlist_t *props;
  81         char *descr, buf[SES2_MIN_DIAGPAGE_ALLOC];
  82         int drawer, drawer_slot;
  83 
  84         if (ses_node_type(np) != SES_NODE_ELEMENT)
  85                 return (0);
  86 
  87         props = ses_node_props(np);
  88         VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
  89         if (type != SES_ET_ARRAY_DEVICE && type != SES_ET_DEVICE)
  90                 return (0);
  91 
  92         if ((rc = dell_fix_bay(sp, np)) != 0) 
  93                 return (rc);
  94 
  95         /* bay will range 1-60 */
  96         if (nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, &bay) != 0)
  97                 return (0);
  98 
  99         /* description strings will have something like "SLOT ## " */
 100         if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &descr) != 0)
 101                 return (0);
 102 
 103         /*
 104          * there are 12 slots per drawer; we want drawer numering to
 105          * start with 1 and drawer slot numbering to start at 0
 106          */
 107         drawer = ((bay - 1) / 12) + 1;
 108         drawer_slot = (bay - (drawer - 1) * 12) - 1;
 109 
 110         /* modify the descrition to include drawer and a slot within drawer */
 111         buf[SES2_MIN_DIAGPAGE_ALLOC - 1] = '\0';
 112         if (snprintf(buf, SES2_MIN_DIAGPAGE_ALLOC - 1,
 113             "Drawer %d, Slot %d. ( Global %s)", drawer, drawer_slot, descr) < 0)
 114                 return (0);
 115 
 116         /* replace the ses-description field with the string we created above */
 117         SES_NV_ADD(string, nverr, props, SES_PROP_DESCRIPTION, buf);
 118 
 119         return (0);
 120 }
 121 
 122 int
 123 _ses_init(ses_plugin_t *sp)
 124 {
 125         ses_plugin_config_t config = {
 126                 .spc_node_parse = dell_parse_node
 127         };
 128 
 129         return (ses_plugin_register(sp, LIBSES_PLUGIN_VERSION, &config) != 0);
 130 }