/*
 * Copyright (C) 2008 Intel Corporation.
 * All rights reserved
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
 */

/*******************************************************************************
 * Theory of operation                                                         *
 *                                                                             *
 * DCMI is the Data Center Management Interface which is a subset of IPMI v2.0.*
 * DCMI incorporates the ability to locate a system with DCMI functionality,   *
 * its available temperature sensors, and power limiting control.              *
 *                                                                             *
 * All of the available DCMI commands are contained in a struct with a numeric *
 * value and a string.  When the user specifies a command the string is        *
 * compared to one of several structs and is then given a numeric value based  *
 * on the matched string.  A case statement is used to select the desired      *
 * action from the user.  If an invalid string is entered, or a string that is *
 * not a command option is entered, the available commands are printed to the  *
 * screen.  This allows the strings to be changed quickly with the DCMI spec.  *
 *                                                                             *
 * Each called function usually executes whichever command was requested to    *
 * keep the main() from being overly complicated.                              *
 *                                                                             *
 * This code conforms to the 1.0 DCMI Specification                           *
 *  released by Hari Ramachandran of the Intel Corporation                     *
 *                                                                             *
 ******************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <netdb.h>

#include <ipmitool/ipmi_dcmi.h>
#include <ipmitool/helper.h>
#include <ipmitool/ipmi.h>
#include <ipmitool/log.h>
#include <ipmitool/ipmi_intf.h>
#include <ipmitool/ipmi_strings.h>
#include <ipmitool/ipmi_mc.h>
#include <ipmitool/ipmi_entity.h>
#include <ipmitool/ipmi_constants.h>
#include <ipmitool/ipmi_sensor.h>

#include "../src/plugins/lanplus/lanplus.h"

#define IPMI_LAN_PORT       0x26f

extern int verbose;

/*******************************************************************************
 * The structs below are the DCMI command option strings.  They are printed    *
 * when the user does not issue enough options or the wrong ones.  The reason  *
 * that the DMCI command strings are in a struct is so that when the           *
 * specification changes, the strings can be changed quickly with out having   *
 * to change a lot of the code in the main().                                  *
 ******************************************************************************/

/* Main set of DCMI commands */
const struct dcmi_cmd dcmi_cmd_vals[] = {
    { 0x00, "discover", "           Used to discover supported DCMI capabilities" },
    { 0x01, "power", "              Pltform power limit command options"         },
    { 0x02, "sensors", "            Prints the available DCMI sensors"            },
    { 0x03, "asset_tag", "          Prints the platforms asset tag"               },
    { 0x04, "set_asset_tag", "      Sets the platforms asset tag"                 },
    { 0x05, "get_mc_id_string", "   Get management controller id string"          },
    { 0x06, "set_mc_id_string", "   Set management controller id string"          },
    { 0x07, "thermalpolicy", "      Thermal policy get/set"                       },
    { 0x08, "get_temp_reading", "   Get Temperature Readings"                     },
    { 0x09, "get_conf_param", "     Get DCMI Config Parameters"                   },
    { 0x0A, "set_conf_param", "     Set DCMI Config Parameters"                   },
    { 0x0B, "oob_discover", "       Ping/Pong Message for DCMI Discovery"         },
    { 0xFF, NULL, NULL                                                        }
};

/* get capabilites */
const struct dcmi_cmd dcmi_capable_vals[] = {
    { 0x01, "platform", "            Lists the system capabilities" },
    { 0x02, "mandatory_attributes", "Lists SEL, identification and"
            "temperature attributes"                                    },
    { 0x03, "optional_attributes", " Lists power capabilities"      },
    { 0x04, "managebility access", " Lists OOB channel information" },
    { 0xFF, NULL, NULL                                              }
};

/* platform capabilities                                                       
 * Since they are actually in two bytes, we need three structs to make this human
 * readable... */
const struct dcmi_cmd dcmi_mandatory_platform_capabilities[] = {
    { 0x01, "Identification support available", "" },
    { 0x02, "SEL logging available", ""            },
    { 0x03, "Chassis power available", ""          },
    { 0x04, "Temperature monitor available", ""  },
    { 0xFF, NULL, NULL                   }
};

/* optional capabilities */
const struct dcmi_cmd dcmi_optional_platform_capabilities[] = {
    { 0x01, "Power management available", ""       },   
    { 0xFF, NULL, NULL                   }
};

/* access capabilties */
const struct dcmi_cmd dcmi_management_access_capabilities[] = {
    { 0x01, "In-band KCS channel available", ""               },
    { 0x02, "Out-of-band serial TMODE available", ""          },
    { 0x03, "Out-of-band secondary LAN channel available", "" },
    { 0x04, "Out-of-band primary LAN channel available", ""   },
    { 0x05, "SOL enabled", ""                       },
    { 0x06, "VLAN capable", ""      },
    { 0xFF, NULL, NULL                              }
 };

/* identification capabilities */
const struct dcmi_cmd dcmi_id_capabilities_vals[] = {
     { 0x01, "GUID", ""          },
     { 0x02, "DHCP hostname", "" },
     { 0x03, "Asset tag", ""    },
     { 0xFF, NULL, NULL          }
};

/* Configuration parameters*/
const struct dcmi_cmd dcmi_conf_param_vals[] = {
     { 0x01, "activate_dhcp",   "\tActivate DHCP"},
     { 0x02, "dhcp_config",     "\tDHCP Configuration" },
     { 0x03, "init",            "\t\tInitial timeout interval"  },
     { 0x04, "timeout",         "\t\tServer contact timeout interval"  },
     { 0x05, "retry",           "\t\tServer contact retry interval"  },
     { 0xFF, NULL, NULL          }
};


/* temperature monitoring capabilities */
const struct dcmi_cmd dcmi_temp_monitoring_vals[] = {
     { 0x01, "inlet", "    Inlet air temperature sensors"  },
     { 0x02, "cpu", "      CPU temperature sensors"        },
     { 0x03, "baseboard", "Baseboard temperature sensors"  },
     { 0xff, NULL, NULL                                    }
};

/* These are not comands.  These are the DCMI temp sensors and their numbers
 * If new sensors are added, they need to be added to this list with their
 * sensor number */
const struct dcmi_cmd dcmi_discvry_snsr_vals[] = {
    { 0x40, "Inlet", "    Inlet air temperature sensors"  },
    { 0x41, "CPU", "      CPU temperature sensors"        },
    { 0x42, "Baseboard", "Baseboard temperature sensors"  },
    { 0xff, NULL, NULL                                    }
};

/* Temperature Readings */
const struct dcmi_cmd dcmi_temp_read_vals[] = {
    { 0x40, "Inlet",        "Inlet air temperature(40h)         " },
    { 0x41, "CPU",          "CPU temperature sensors(41h)       " },
    { 0x42, "Baseboard",    "Baseboard temperature sensors(42h) " },
    { 0xff, NULL, NULL                                    }
};

/* power management/control commands */
const struct dcmi_cmd dcmi_pwrmgmt_vals[] = {
    { 0x00, "reading", "   Get power related readings from the system" },
    { 0x01, "get_limit", " Get the configured power limits"            },
    { 0x02, "set_limit", " Set a power limit option"                   },
    { 0x03, "activate", "  Activate the set power limit"               },
    { 0x04, "deactivate", "Deactivate the set power limit"             },
    { 0xFF, NULL, NULL                                                 }
};

/* set power limit commands */
const struct dcmi_cmd dcmi_pwrmgmt_set_usage_vals[] = {
    { 0x00, "action", "    <sel_logging | power_off>"                      },
    { 0x01, "limit", "     <number in Watts>"           },
    { 0x02, "correction", "<number in milliseconds>" },
    { 0x03, "sample", "    <number in seconds>"            },
    { 0xFF, NULL, NULL                                                  }
};

/* power management/set action commands */
const struct dcmi_cmd dcmi_pwrmgmt_action_vals[] = {
    { 0x00, "No Action", "" },
    { 0x01, "Hard Power Off & Log Event to SEL", "" },
    { 0x11, "Log Event to SEL", ""  },
    { 0xFF, NULL, NULL      }
};

/* thermal policy action commands */
const struct dcmi_cmd dcmi_thermalpolicy_vals[] = {
        { 0x00, "get", "Get thermal policy"  },
        { 0x01, "set", "Set thermal policy"  },
        { 0xFF, NULL, NULL      }
};

/* thermal policy action commands */
const struct dcmi_cmd dcmi_confparameters_vals[] = {
        { 0x00, "get", "Get configuration parameters"  },
        { 0x01, "set", "Set configuration parameters"  },
        { 0xFF, NULL, NULL      }
};

/* entityIDs used in thermap policy */
const struct dcmi_cmd dcmi_thermalpolicy_set_parameters_vals[] = {
        { 0x00, "volatile", "   Current Power Cycle"        },
        { 0x01, "nonvolatile", "Set across power cycles"        },
        { 0x01, "poweroff", "   Hard Power Off system"          },
        { 0x00, "nopoweroff", " No 'Hard Power Off' action"         },
        { 0x01, "sel", "        Log event to SEL"   },
        { 0x00, "nosel", "      No 'Log event to SEL' action"   },
        { 0x00, "disabled", "   Disabled"   },
        { 0x00, NULL,    NULL                   }
};


/* DCMI command specific completion code results per 1.0 spec
 * 80h - parameter not supported.
 * 81h - attempt to set the ‘set in progress’ value (in parameter #0) when not
 *       in the ‘set complete’ state. (This completion code provides a way to
 *       recognize that another party has already ‘claimed’ the parameters)
 * 82h - attempt to write read-only parameter
 * 82h - set not supported on selected channel (e.g. channel is session-less.)
 * 83h - access mode not supported
 * 84h – Power Limit out of range
 * 85h – Correction Time out of range
 * 89h – Statistics Reporting Period out of range
 */
const struct valstr dcmi_ccode_vals[] = {
    { 0x80, "Parameter not supported"                                },
    { 0x81, "Something else has already claimed these parameters"    },
    { 0x82, "Not supported or failed to write a read-only parameter" },
    { 0x83, "Access mode is not supported"                           },
    { 0x84, "Power/Thermal limit out of range"                       },
    { 0x85, "Correction/Exception time out of range"                 },
    { 0x89, "Sample/Statistics Reporting period out of range"                             },
    { 0x8A, "Power limit already active"                             },
   { 0xFF, NULL                                                     }
};

/* End strings */

/*******************************************************************************
 * This was taken from print_valstr() from helper.c.  It serves the same       *
 * purpose but with out the extra formatting.  This function simply prints     *
 * the dcmi_cmd struct provided.  verthorz specifies to print vertically or    *
 * horizontally.  If the string is printed horizontally then a | will be       *
 * printed between each instance of vs[i].str until it is NULL                 *
 *                                                                             *
 * @vs:         value string list to print                                     *
 * @title:      name of this value string list                                 *
 * @loglevel:   what log level to print, -1 for stdout                         *
 * @verthorz:   printed vertically or horizontally, 0 or 1                     *
 ******************************************************************************/
void print_strs(const struct dcmi_cmd * vs, const char * title, int loglevel,
    int verthorz) {
    int i;

    if (vs == NULL)
        return;

    if (title != NULL) {
        if (loglevel < 0)
            printf("\n%s\n", title);
        else
            lprintf(loglevel, "\n%s", title);
    }
    for (i = 0; vs[i].str != NULL; i++) {
        if (loglevel < 0) {
            if (vs[i].val < 256)
                if (verthorz == 0)
                    printf("    %s    %s\n", vs[i].str, vs[i].desc);
                else
                    printf("%s", vs[i].str);
            else if (verthorz == 0)
                printf("    %s    %s\n", vs[i].str, vs[i].desc);
            else
                printf("%s", vs[i].str);
        } else {
            if (vs[i].val < 256)
                lprintf(loglevel, "    %s    %s", vs[i].str, vs[i].desc);
            else
                lprintf(loglevel, "    %s    %s", vs[i].str, vs[i].desc);
        }
        /* Check to see if this is NOT the last element in vs.str if true
         * print the | else don't print anything. */
        if ((verthorz == 1) && (vs[i+1].str != NULL))
            printf(" | ");
    }
    if (verthorz == 0)
        if (loglevel < 0)
            printf("\n");
        else
            lprintf(loglevel, "");
}

/*******************************************************************************
 * This was taken from str2val() from helper.c.  It serves the same            *
 * purpose but with the addition of a desc field from the structure.           *
 * This function converts the str from the dcmi_cmd struct provided to the     *
 * value associated to the compared string in the struct.                      *
 *                                                                             *
 * @str:        string to compare against                                      *
 * @vs:         dcmi_cmd structure                                             *
 ******************************************************************************/
uint16_t str2val2(const char *str, const struct dcmi_cmd *vs)
{
    int i;

    if (vs == NULL || str == NULL)
        return 0;
    
    for (i = 0; vs[i].str != NULL; i++) {
        if (strncasecmp(vs[i].str, str, __maxlen(str, vs[i].str)) == 0)
            return vs[i].val;
    }

    return vs[i].val;
}

/*******************************************************************************
 * This was taken from val2str() from helper.c.  It serves the same            *
 * purpose but with the addition of a desc field from the structure.           *
 * This function converts the val and returns a string from the dcmi_cmd       *
 * struct provided in the struct.                                              *
 *                                                                             *
 * @val:        value to compare against                                       *
 * @vs:         dcmi_cmd structure                                             *
 ******************************************************************************/
const char * val2str2(uint16_t val, const struct dcmi_cmd *vs)
{
    static char un_str[32];
    int i;

    if (vs == NULL)
        return NULL;
    
    for (i = 0; vs[i].str != NULL; i++) {
        if (vs[i].val == val)
            return vs[i].str;
    }

    memset(un_str, 0, 32);
    snprintf(un_str, 32, "Unknown (0x%x)", val);

    return un_str;
}

/*******************************************************************************
 * check the DCMI response   from the BMC                                      *
 * @rsp:       Response data structure                                      *
 *                                                                             *
 ******************************************************************************/
static int chk_rsp(struct ipmi_rs * rsp) {
    
    /* if the response from the intf is NULL then the BMC is experiencing
     * some issue and cannot complete the command */
    if (rsp == NULL) {
        lprintf(LOG_ERR, "\n    Unable to get DCMI information");
        return 1;
    }
    
    /* if the completion code is greater than zero there was an error.  We'll
     * use val2str from helper.c to print the error from either the DCMI
     * completion code struct or the generic IPMI completion_code_vals struct
     */
    if ((rsp->ccode >= 0x80) && (rsp->ccode <= 0x8F)) {
        lprintf(LOG_ERR, "\n    DCMI request failed because: %s (%x)",
            val2str(rsp->ccode, dcmi_ccode_vals), rsp->data[0]);
        return 1;
    } else if (rsp->ccode > 0) {
        lprintf(LOG_ERR, "\n    DCMI request failed because: %s (%x)",
            val2str(rsp->ccode, completion_code_vals), rsp->data[0]);
        return 1;
    }

    /* check to make sure this is a DCMI firmware */
    if(rsp->data[0] != IPMI_DCMI) {
        printf("\n    A valid DCMI command was not returned! (%x)", rsp->data[0]);
        return 1;
    }
    
    return 0;
}
/*******************************************************************************
 *  Get capabilities ipmi response                                             *
 *                                                                             *
 * This function returns the available capabilities of the platform.           *
 * The reason it returns in the rsp struct is so that it can be used for other *
 * purposes.                                                                   *
 *                                                                             *
 * returns ipmi response structure                                             *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @selector: Parameter selector
 ******************************************************************************/
struct ipmi_rs * 
ipmi_dcmi_getcapabilities(struct ipmi_intf * intf, uint8_t selector) {
    struct ipmi_rq req; /* request data to send to the BMC */
    uint8_t msg_data[2]; /* 'raw' data to be sent to the BMC */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = selector;

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.0 spec */
    req.msg.cmd = IPMI_DCMI_COMPAT; /* 0x01 per 1.0 spec */
    req.msg.data = msg_data; /* 0xDC 0x01 or the msg_data above */
    req.msg.data_len = 2; /* How many times does req.msg.data need to read */

    return intf->sendrecv(intf, &req);
}
/* end capabilities struct */

/*******************************************************************************
/* Displays capabilities from structure
* returns void                                *
*                                                                             *
* @cmd:        dcmi_cmd structure                                             *
* @data_val:  holds value of what to display                                              *
******************************************************************************/
void display_capabilities_attributes(
        const struct dcmi_cmd *cmd, uint8_t data_val)
{
    uint8_t i;
    for (i = 0x01; cmd[i-1].val != 0xFF; i++) {
        if (data_val & (1<<(i-1))) {
            printf("        %s\n", val2str2(i, cmd));
        }
    }
}
static int
ipmi_dcmi_prnt_oobDiscover(struct ipmi_intf * intf)
{
    struct ipmi_rs *rsp;
    int rc;

    if (intf->opened == 0 && intf->open != NULL) {
        if (intf->open(intf) < 0)
            return (int) NULL;
    }
    
    #ifndef IPMI_INTF_LANPLUS
    printf("Function oobDicover unavailable when lanplus (IPMI v2) is not enabled\n");
    return -1;

    #else

    struct ipmi_session *s;

    if (intf == NULL || intf->session == NULL)
        return -1;
    s = intf->session;

    if (s->port == 0)
        s->port = IPMI_LAN_PORT;
    if (s->privlvl == 0)
        s->privlvl = IPMI_SESSION_PRIV_ADMIN;
    if (s->timeout == 0)
        s->timeout = IPMI_LAN_TIMEOUT;
    if (s->retry == 0)
        s->retry = IPMI_LAN_RETRY;

    if (s->hostname == NULL || strlen((const char *)s->hostname) == 0) {
        lprintf(LOG_ERR, "No hostname specified!");
        return -1;
    }

    intf->abort = 1;
    intf->session->sol_data.sequence_number = 1;
    
    /* open port to BMC */
    memset(&s->addr, 0, sizeof(struct sockaddr_in));
    s->addr.sin_family = AF_INET;
    s->addr.sin_port = htons(s->port);

    rc = inet_pton(AF_INET, (const char *)s->hostname, &s->addr.sin_addr);
    if (rc <= 0) {
        struct hostent *host = gethostbyname((const char *)s->hostname);
        if (host == NULL) {
            lprintf(LOG_ERR, "Address lookup for %s failed",
                s->hostname);
            return -1;
        }
        s->addr.sin_family = host->h_addrtype;
        memcpy(&s->addr.sin_addr, host->h_addr, host->h_length);
    }

    lprintf(LOG_DEBUG, "IPMI LAN host %s port %d",
        s->hostname, ntohs(s->addr.sin_port));

    intf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (intf->fd < 0) {
        lperror(LOG_ERR, "Socket failed");
        return -1;
    }

    /* connect to UDP socket so we get async errors */
    rc = connect(intf->fd, (struct sockaddr *)&s->addr,
             sizeof(struct sockaddr_in));
    if (rc < 0) {
        lperror(LOG_ERR, "Connect failed");
        intf->close(intf);
        return -1;
    }

    intf->opened = 1;

    //Lets ping/pong
    return ipmiv2_lan_ping(intf);
    #endif
}
/*******************************************************************************
 * This is the get DCMI Capabilities function to see what the BMC supports.    *
 *                                                                             *
 * returns 0 with out error -1 with any errors                                 *
 *                                                                             *
 * @intf:      ipmi interface handler                                          *
 * @selector:  selection parameter                                             *
 ******************************************************************************/
static int
ipmi_dcmi_prnt_getcapabilities(struct ipmi_intf * intf, uint8_t selector) {
    
    uint8_t i;
    uint8_t bit_shifter = 0;
    struct ipmi_rs * rsp;
    rsp = ipmi_dcmi_getcapabilities(intf, selector);

    if(chk_rsp(rsp))
        return -1;

    /* if there were no errors, the command worked! */
    
    struct capabilities cape;
    memcpy(&cape, rsp->data, sizeof (cape));
    
    /* check to make sure that this is a 1.0 and 1.1 command */
    if ((cape.conformance != IPMI_DCMI_CONFORM) && (cape.conformance != IPMI_DCMI_1_1_CONFORM)) {
        printf("ERROR!  This command is not available on this platform\n");
        return -1;
    }
    
    /* check to make sure that this is a rev .01 or .02 */
    if (cape.revision != 0x01 && cape.revision != 0x02) {
        printf("ERROR!  This command is not compatible with this version\n");
        return -1;
    }
    
    /* 0x01 - platform capabilities
     * 0x02 - Manageability Access Capabilities
     * 0x03 - SEL Capability
     * 0x04 - Identification Capability
     * 0x05 - LAN Out-Of-Band Capability
     * 0x06 - Serial Out-Of-Band TMODE Capability
     */
    switch (selector) {
        case 0x01:
            printf("    Supported DCMI capabilities:\n");
            /* loop through each of the entries in the first byte from the
             * struct */
            printf("\n         Mandatory platform capabilties\n");
            display_capabilities_attributes(
                dcmi_mandatory_platform_capabilities, cape.data_byte1);
            /* loop through each of the entries in the second byte from the
             * struct */           
            printf("\n         Optional platform capabilties\n");
            display_capabilities_attributes(
                dcmi_optional_platform_capabilities, cape.data_byte2);
            /* loop through each of the entries in the third byte from the
             * struct */           
            printf("\n         Managebility access capabilties\n");
            display_capabilities_attributes(
                dcmi_management_access_capabilities, cape.data_byte3);
            break;
        case 0x02:
            printf("\n    Mandatory platform attributes:\n");
            /* byte 1 & 2 data */
            printf("\n         SEL Attributes: ");
            printf("\n               SEL automatic rollover is ");
            /* mask the 2nd byte of the data response with 10000000b or 0x80   *
             * because of the endian-ness the 15th bit is in the second byte  */
            if ((cape.data_byte2 & 0x80))
                printf("enabled");
            else
                printf("not present");
            
            /* since the number of SEL entries is split across the two data
             * bytes we will need to bit shift and append them together again */
            /* cast cape.data_byte1 as 16 bits */
            uint16_t sel_entries = (uint16_t)cape.data_byte1;
            /* or sel_entries with byte 2 and shift it 8 places  */
            sel_entries |= (uint16_t)cape.data_byte2 << 8;
            printf("\n               %d SEL entries\n", sel_entries & 0xFFF);
             /* byte 3 data */
            printf("\n         Identification Attributes: \n");
            display_capabilities_attributes(
                 dcmi_id_capabilities_vals, cape.data_byte3);
             /* byte 4 data */
            printf("\n         Temperature Monitoring Attributes: \n");
             display_capabilities_attributes(
                  dcmi_temp_monitoring_vals, cape.data_byte4);      
            break;
        case 0x03:
            printf("\n    Optional Platform Attributes: \n");
            /* Power Management */
            printf("\n         Power Management:\n");
            if (cape.data_byte1 == 0x40)
            {
                printf("                Slave address of device: 20h (BMC)\n" );
            }
            else
            {
                printf("                Slave address of device: %xh (8bits)"
                "(Satellite/External controller)\n", cape.data_byte1);
            }
            // Controller channel number (4-7) bits
            if ((cape.data_byte2>>4) == 0x00)
            {
                printf("                Channel number is 0h (Primary BMC)\n");
            }
            else
            {
                printf("                Channel number is %xh \n",(cape.data_byte2>>4));                
            }   
            // Device revision (0-3)
            printf("                    Device revision is %d \n", cape.data_byte2 &0xf);
            break;
        case 0x04:
            /* LAN */
            printf("\n    Manageability Access Attributes: \n");
            if (cape.data_byte1 == 0xFF)
                printf("         Primary LAN channel is not available for OOB\n");
            else
                printf("         Primary LAN channel number: %d is available\n",
                    cape.data_byte1);
            
            if (cape.data_byte2 == 0xFF)
                printf("         Secondary LAN channel is not available for OOB\n");
            else
                printf("         Secondary LAN channel number: %d is available\n",
                    cape.data_byte2);
            /* serial */
            if (cape.data_byte3 == 0xFF)
                printf("         No serial channel is available\n");

            else
                printf("         Serial channel number: %d is available\n",
                    cape.data_byte3);
            break;
        default:
            return -1;
    }

    return 0;
    /* return intf->sendrecv(intf, &req); */
}

/*******************************************************************************
 * This is the get asset tag command.  This checks the length of the asset tag *
 * with the first read, then reads n number of bytes thereafter to get the     *
 * complete asset tag.                                                         *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @offset: where to start reading the asset tag                               *
 * @length: how much to read                                                   *
 *                                                                             *
 * returns ipmi_rs structure                                                   *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_getassettag(
    struct ipmi_intf * intf, uint8_t offset, uint8_t length) {

    struct ipmi_rq req; /* request data to send to the BMC */
    uint8_t msg_data[3]; /* 'raw' data to be sent to the BMC */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = offset; /* offset 0 */
    msg_data[2] = length; /* read one byte */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */
    req.msg.cmd = IPMI_DCMI_GETASSET; /* 0x01 per 1.1 spec */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = 3; /* How many times does req.msg.data need to read */

    return intf->sendrecv(intf, &req);
}

/*******************************************************************************
 * This is the get asset tag command.  The function first checks to see if the *
 * platform is capable of getting the asset tag by calling the getcapabilities *
 * function and checking the response.  Then it checks the length of the asset *
 * tag with the first read, then x number of reads thereafter to get the asset *
 * complete asset tag then print it.                                           *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 *                                                                             *
 * returns 0 if no failure, -1 with a failure                                  *
 ******************************************************************************/
static int ipmi_dcmi_prnt_getassettag(struct ipmi_intf * intf) {
    uint8_t data_byte2;
    struct ipmi_rs * rsp; /* ipmi response */
    uint8_t taglength = 0;
    uint8_t getlength = 0;
    uint8_t offset = 0;
    uint8_t i;

    /* now let's get the asset tag length */
    rsp = ipmi_dcmi_getassettag(intf, 0, 0);

    if(chk_rsp(rsp))
        return -1;

    taglength = rsp->data[1];

    printf("\n Asset tag: ");
    while (taglength) {
        getlength = taglength/DCMI_MAX_BYTE_SIZE ? DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE;
        rsp = ipmi_dcmi_getassettag(intf, offset, getlength);

        //macro has no effect here where can generate sig segv if rsp occurs with null
        if (rsp != NULL)
            GOOD_ASSET_TAG_CCODE(rsp->ccode);
        if(chk_rsp(rsp))
            return -1;

        for (i=0; i<getlength; i++) {
            printf("%c", rsp->data[i+2]);
        }
        offset += getlength;
        taglength -= getlength;
    }

    printf("\n");   
    return 0;
}

/*******************************************************************************
 * This is the set asset tag command.  This checks the length of the asset tag *
 * with the first read, then reads n number of bytes thereafter to set the     *
 * complete asset tag.                                                         *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @offset: offset to write                                                    *
 * @length: number of bytes to write (16 bytes maximum)                        *
 * @data:   data to write                                                      *
 *                                                                             *
 * returns ipmi_rs structure                                                   *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_setassettag(
    struct ipmi_intf * intf, uint8_t offset, uint8_t length, uint8_t *data) {

    struct ipmi_rq req; /* request data to send to the BMC */
    uint8_t msg_data[3+length]; /* 'raw' data to be sent to the BMC */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = offset; /* offset 0 */
    msg_data[2] = length; /* read one byte */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */
    req.msg.cmd = IPMI_DCMI_SETASSET; /* 0x08 per 1.1 spec */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = length +3; /* How many times does req.msg.data need to read */
    memcpy(req.msg.data + 3, data, length);

    return intf->sendrecv(intf, &req);
}

static int ipmi_dcmi_prnt_setassettag(struct ipmi_intf * intf, uint8_t * data) {
    uint8_t data_byte2;
    struct ipmi_rs * rsp; /* ipmi response */
    uint8_t tmpData[DCMI_MAX_BYTE_SIZE];
    uint8_t taglength = 0;
    uint8_t getlength = 0;
    uint8_t offset = 0;
    uint8_t i;

    /* now let's get the asset tag length */
    taglength = strlen(data);

    if (taglength > 64){
        printf("\nValue to long\n");
        return -1; 
    }

    printf("\n Set Asset Tag: ");
    while (taglength) {
        getlength = taglength/DCMI_MAX_BYTE_SIZE ? DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE;
        memcpy(tmpData, data + offset, getlength);
        rsp = ipmi_dcmi_setassettag(intf, offset, getlength, tmpData);

        if(chk_rsp(rsp))
            return -1;

        for (i=0; i<getlength; i++) {
            printf("%c", tmpData[i]);
        }
        offset += getlength;
        taglength -= getlength;
    }

    printf("\n");   
    return 0;
}

/*******************************************************************************
 * Management Controller Identifier String is provided in order to accommodate *
 * the requirement for the management controllers to identify themselves.      *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @offset: offset to read                                                     *
 * @length: number of bytes to read (16 bytes maximum)                         *
 *                                                                             *
 * returns ipmi_rs structure                                                   *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_getmngctrlids(
    struct ipmi_intf * intf, uint8_t offset, uint8_t length) {

    struct ipmi_rq req; /* request data to send to the BMC */
    uint8_t msg_data[3]; /* 'raw' data to be sent to the BMC */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = offset; /* offset 0 */
    msg_data[2] = length; /* read one byte */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */
    req.msg.cmd = IPMI_DCMI_GETMNGCTRLIDS; /* 0x09 per 1.1 spec */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = 3; /* How many times does req.msg.data need to read */
    return intf->sendrecv(intf, &req);
}

static int ipmi_dcmi_prnt_getmngctrlids(struct ipmi_intf * intf) {
    uint8_t data_byte2;
    struct ipmi_rs * rsp; /* ipmi response */
    uint8_t taglength = 0;
    uint8_t getlength = 0;
    uint8_t offset = 0;
    uint8_t i;

    /* now let's get the asset tag length */
    rsp = ipmi_dcmi_getmngctrlids(intf, 0, 1);

    if(chk_rsp(rsp))
        return -1;

    taglength = rsp->data[1];

    printf("\n Get Management Controller Identifier String: ");
    while (taglength) {
        getlength = taglength/DCMI_MAX_BYTE_SIZE ? DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE;
        rsp = ipmi_dcmi_getmngctrlids(intf, offset, getlength);

        if(chk_rsp(rsp))
            return -1;
 
        for (i=0; i<getlength; i++) {
            printf("%c", rsp->data[i+2]);
        }
        offset += getlength;
        taglength -= getlength;
    }

    printf("\n");
    return 0;
}

/*******************************************************************************
 * Management Controller Identifier String is provided in order to accommodate *
 * the requirement for the management controllers to identify themselves.      *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @offset: offset to write                                                    *
 * @length: number of bytes to write (16 bytes maximum)                        *
 * @data:   data to write                                                      *
 *                                                                             *
 * returns ipmi_rs structure                                                   *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_setmngctrlids(
    struct ipmi_intf * intf, uint8_t offset, uint8_t length, uint8_t *data) {

    struct ipmi_rq req; /* request data to send to the BMC */
    uint8_t msg_data[3+length]; /* 'raw' data to be sent to the BMC */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = offset; /* offset 0 */
    msg_data[2] = length; /* read one byte */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP; /* 0x2C per 1.1 spec */
    req.msg.cmd = IPMI_DCMI_SETMNGCTRLIDS; /* 0x0A per 1.1 spec */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = 3 + length; /* How many times does req.msg.data need to read */
    memcpy(req.msg.data + 3, data, length);

    return intf->sendrecv(intf, &req);
}

/*******************************************************************************
 * Set Asset Tag command provides ability for the management console to set the*
 * asset tag as appropriate. Management controller is not responsible for the  *
 * data format used for the Asset Tag once modified by IPDC.                   *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 *                                                                             *
 * returns 0 if no failure, -1 with a failure                                  *
 ******************************************************************************/
static int ipmi_dcmi_prnt_setmngctrlids(struct ipmi_intf * intf, uint8_t * data) {

    uint8_t data_byte2;
    struct ipmi_rs * rsp; /* ipmi response */
    uint8_t tmpData[DCMI_MAX_BYTE_SIZE];
    uint8_t taglength = 0;
    uint8_t getlength = 0;
    uint8_t offset = 0;
    uint8_t i;

    data += '\0';
    taglength = strlen(data) +1;

    if (taglength > 64){
        printf("\nValue to long\n");
        return -1; 
    }

    printf("\n Set Management Controller Identifier String Command: ");
    while (taglength) {
        getlength = taglength/DCMI_MAX_BYTE_SIZE ? DCMI_MAX_BYTE_SIZE : taglength%DCMI_MAX_BYTE_SIZE;
        memcpy(tmpData, data + offset, getlength);
        rsp = ipmi_dcmi_setmngctrlids(intf, offset, getlength, tmpData);

        if(strncmp(intf->name, "lanplus", 7)) //because after call "Set mc id string" RMCP+ will go down we have no "rsp"
        {
            if(chk_rsp(rsp))
                return -1;
        }

        for (i=0; i<getlength; i++) {
            printf("%c", tmpData[i]);
        }
        offset += getlength;
        taglength -= getlength;
    }

    printf("\n");
    return 0;
}

/*******************************************************************************
 * Issues a discovery command to see what sensors are available on the target. *
 * system.  
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @isnsr:  entity ID                                                          *
 * @offset:   offset (Entity instace start)                                                   *
 *                                                                             *
 * returns ipmi_rs structure                                                   *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_discvry_snsr( 
    struct ipmi_intf * intf, uint8_t isnsr, uint8_t offset) {
    
    struct ipmi_rq req; /* ipmi request struct */
    uint8_t msg_data[5]; /* number of request data bytes */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = 0x01; /* Senser Type = Temp (01h) */
    msg_data[2] = isnsr; /* Sensor Number */
    msg_data[3] = 0x00; /* Entity Instance, set to read all instances */
    msg_data[4] = offset; /* Entity instace start */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_GETSNSR;
    req.msg.data = msg_data; /* Contents above */
    req.msg.data_len = 5; /* how many times does req.msg.data need to read */

    return intf->sendrecv(intf, &req);
}

static int ipmi_print_sensor_info(struct ipmi_intf *intf, uint16_t rec_id);

/*******************************************************************************
 * DCMI sensor discovery                                                       *
 * Uses the dcmi_discvry_snsr_vals struct to print its string and              *
 * uses the numeric values to request the sensor sdr record id.                *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @isnsr:  entity ID                                                          *
 * @ient:   sensor entity id                                                   *
 *                                                                             *
 ******************************************************************************/
static int ipmi_dcmi_prnt_discvry_snsr(struct ipmi_intf * intf, uint8_t isnsr) {
  int i = 0;
    struct ipmi_rs * rsp; /* ipmi response */
  uint8_t records = 0;
  int8_t instances = 0;
    uint8_t offset = 0;
    uint16_t record_id = 0;
    uint8_t id_buff[16]; /* enough for 8 record IDs */
    rsp = ipmi_dcmi_discvry_snsr(intf, isnsr, 0); 

  if(chk_rsp(rsp))
      return -1;
     
    instances = rsp->data[1];
    printf("\n%s: %d temperature sensor%s found:\n",
        val2str2(isnsr, dcmi_discvry_snsr_vals), 
        instances, 
        (instances > 1) ? "s" : "");
    while(instances > 0)
    {
        ipmi_dcmi_discvry_snsr(intf, isnsr, offset);
        
        if(chk_rsp(rsp))
            return -1;
            
        records = rsp->data[2];
        
        /* cache the data since it may be destroyed by subsequent ipmi_xxx calls */
        memcpy(id_buff, &rsp->data[3], 16);     
        
        for (i=0; i<records; i++) {
         /* Record ID is in little endian format */
            record_id = (id_buff[2*i + 1] << 8) + id_buff[2*i];
            printf("Record ID 0x%04x: ", record_id);
            ipmi_print_sensor_info(intf, record_id);
        }
        
        offset += 8;
        instances -= records;
    }
    
  return 0;
}
/* end sensor discovery */

/*******************************************************************************
 *  Power Management get power reading                                         *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 ******************************************************************************/
static int ipmi_dcmi_pwr_rd(struct ipmi_intf * intf) {
    struct ipmi_rs * rsp;
    struct ipmi_rq req;
    uint8_t msg_data[4]; /* number of request data bytes */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = 0x01; /* Mode Power Status */
    msg_data[2] = 0x00; /* reserved */
    msg_data[3] = 0x00; /* reserved */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_GETRED; /* Get power reading */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = 4; /* how many times does req.msg.data need to read */

    rsp = intf->sendrecv(intf, &req);

    if(chk_rsp(rsp))
        return -1;
    
    /* rsp->data[0] is equal to response data byte 2 in spec */

    /* printf("Group Extension Identification: %02x\n", rsp->data[0]); */
    struct power_reading val;
    memcpy(&val, rsp->data, sizeof (val));
    printf("\n");
    printf("    Instantaneous power reading:              %8d Watts\n",
        val.curr_pwr);
    printf("    Minimum during sampling period:           %8d Watts\n",
        val.min_sample);
    printf("    Maximum during sampling period:           %8d Watts\n",
        val.max_sample);
    printf("    Average power reading over sample period: %8d Watts\n",
        val.avg_pwr);
    printf("    IPMI timestamp:                           %s",
        ctime((time_t *)&val.time_stamp));
    printf("    Sampling period:                          %08d Milliseconds\n",
        val.sample);
    printf("    Power reading state is:                   ");
    /* mask the rsp->data so that we only care about bit 6 */
    if((val.state & 0x40) == 0x40) {
        printf("activated");
    } else {
        printf("deactivated");
    }
    printf("\n\n");
    return 0;
}
/* end Power Management get reading */


/*******************************************************************************
 * This is the get thermalpolicy command.                                      *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 *                                                                             *                                                  *
 ******************************************************************************/
int ipmi_dcmi_getthermalpolicy(struct ipmi_intf * intf, uint8_t entityID, uint8_t entityInstance)
{
    struct ipmi_rs * rsp;
    struct ipmi_rq req;
    uint8_t msg_data[3]; /* number of request data bytes */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = entityID; /* Inlet Temperature DCMI ID*/
    msg_data[2] = entityInstance; /* Entity Instance */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_GETTERMALLIMIT; /* Get thermal policy reading */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = 3; /* how many times does req.msg.data need to read */

    rsp = intf->sendrecv(intf, &req);

    if(chk_rsp(rsp))
        return -1;

    /* rsp->data[0] is equal to response data byte 2 in spec */

    struct thermal_limit val;
    memcpy(&val, rsp->data, sizeof (val));

    printf("\n");

    printf("    Persistance flag is:                      %s\n", ((val.exceptionActions & 0x80) ? "set" : "notset"));
    printf("    Exception Actions, taken if the Temperature Limit exceeded:\n");
    printf("        Hard Power Off system and log event:  %s\n", ((val.exceptionActions & 0x40) ? "active":"inactive"));
    printf("        Log event to SEL only:                %s\n", ((val.exceptionActions & 0x20) ? "active":"inactive"));
    printf("    Temperature Limit                         %d degrees\n", val.tempLimit);
    printf("    Exception Time                            %d seconds\n", val.exceptionTime);

    printf("\n\n");

    return 0;
}

/*******************************************************************************
 * This is the set thermalpolicy command.                                      *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 *                                                                             *                                                  *
 ******************************************************************************/
int ipmi_dcmi_setthermalpolicy(struct ipmi_intf * intf,
                               uint8_t entityID,
                               uint8_t entityInst,
                               uint8_t persistanceFlag,
                               uint8_t actionHardPowerOff,
                               uint8_t actionLogToSEL,
                               uint8_t tempLimit,
                               uint8_t samplingTimeLSB,
                               uint8_t samplingTimeMSB)
{
    struct ipmi_rs * rsp;
    struct ipmi_rq req;
    uint8_t msg_data[7]; /* number of request data bytes */

    msg_data[0] = IPMI_DCMI;    /* Group Extension Identification */
    msg_data[1] = entityID;     /* Inlet Temperature DCMI ID*/
    msg_data[2] = entityInst;   /* Entity Instance */

    msg_data[3] = ( ((persistanceFlag   ?1:0)<<7) |
                    ((actionHardPowerOff?1:0)<<6) |
                    ((actionLogToSEL    ?1:0)<<5)
                  );            /* persistance and actions or disabled if no actions */

    msg_data[4] = tempLimit;

    msg_data[5] = samplingTimeLSB;
    msg_data[6] = samplingTimeMSB;

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_SETTERMALLIMIT; /* Get thermal policy reading */
    req.msg.data = msg_data; /* msg_data above */
    req.msg.data_len = 7; /* how many times does req.msg.data need to read */

    rsp = intf->sendrecv(intf, &req);

    if(chk_rsp(rsp))
        return -1;

    /* rsp->data[0] is equal to response data byte 2 in spec */

    printf("\nThermal policy %d for %0Xh entity successfully set.\n\n", entityInst, entityID);
    return 0;
}


/******************************************************************************
 * This is Get Temperature Readings Command
 *
 * returns ipmi response structure
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_get_temp_readings(struct ipmi_intf * intf,
                                                uint8_t entityID,
                                                uint8_t entityInst,
                                                uint8_t entityInstStart)
{
    struct ipmi_rq req;
    uint8_t msg_data[5];            /* number of request data bytes */

    msg_data[0] = IPMI_DCMI;        /* Group Extension Identification */
    msg_data[1] = 0x01;             /* Sensor type */
    msg_data[2] = entityID;         /* Entity Instance */
    msg_data[3] = entityInst;
    msg_data[4] = entityInstStart;

    memset(&req, 0, sizeof(req));
    req.msg.netfn       = IPMI_NETFN_DCGRP;
    req.msg.cmd         = IPMI_DCMI_GETTEMPRED;     /* Get thermal policy reading */
    req.msg.data        = msg_data;                 /* msg_data above */
    req.msg.data_len    = 5;                        /* how many times does req.msg.data need to read */

    return intf->sendrecv(intf, &req);
}

static int ipmi_dcmi_prnt_get_temp_readings(struct ipmi_intf * intf)
{
     struct ipmi_rs * rsp;
     int i,j, tota_inst, get_inst, offset = 0;

    //Print sensor description
    printf("\n\tEntity ID\t\t\tEntity Instance\t   Temp. Readings");

    for (i = 0; dcmi_temp_read_vals[i].str != NULL; i++) {
        /* get all of the information about this sensor */
        rsp = ipmi_dcmi_get_temp_readings(intf, dcmi_temp_read_vals[i].val, 0, 0);
        if(chk_rsp(rsp))
            continue;
        //Total number of available instances for the Entity ID
        offset = 0;
        tota_inst   = rsp->data[1];

        while (tota_inst > 0){
            get_inst = ((tota_inst/DCMI_MAX_BYTE_TEMP_READ_SIZE) ? DCMI_MAX_BYTE_TEMP_READ_SIZE : (tota_inst%DCMI_MAX_BYTE_TEMP_READ_SIZE));
            rsp = ipmi_dcmi_get_temp_readings(intf, dcmi_temp_read_vals[i].val, offset, 0);

            if(chk_rsp(rsp))
                continue;

            //Number of sets of Temperature Data in this response (Max 8 per response)
            for (j=0; j < rsp->data[2]*2; j=j+2)
            {
                //Print Instance temperature info
                printf("\n%s",dcmi_temp_read_vals[i].desc);
                printf("\t\t%i\t\t%c%i C", rsp->data[j+4], ((rsp->data[j+3]) >> 7) ? '-' : '+', (rsp->data[j+3] & 127));
            }
            offset += get_inst;
            tota_inst -= get_inst;
        }
    }
    return 0;
}

/******************************************************************************
 * This is Get DCMI Config Parameters Command
 *
 * returns ipmi response structure
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_getconfparam(struct ipmi_intf * intf, int param_selector)
{
    struct ipmi_rq req;
    uint8_t msg_data[3]; /* number of request data bytes */
    
    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = param_selector; /* Parameter selector */
    msg_data[2] = 0x00; /*Set Selector. Selects a given set of parameters under a given Parameter selector value. 00h if parameter doesn't use a Set Selector. */

    memset(&req, 0, sizeof(req));
    req.msg.netfn       = IPMI_NETFN_DCGRP;
    req.msg.cmd         = IPMI_DCMI_GETCONFPARAM; /* Get DCMI Config Parameters */
    req.msg.data        = msg_data; /* Contents above */
    req.msg.data_len    = 3; /* how many times does req.msg.data need to read */

    return intf->sendrecv(intf, &req);
}

static int ipmi_dcmi_prnt_getconfparam(struct ipmi_intf * intf)
{
    struct ipmi_rs * rsp;
    const int dcmi_conf_params = 5;
    int param_selector;
    uint16_t tmp_value = 0;
    
    for (param_selector=2 ; param_selector <= dcmi_conf_params; param_selector++)//We are not interested in parameter 1 which always will return 0
    {
        rsp = ipmi_dcmi_getconfparam(intf, param_selector);
        if(chk_rsp(rsp))
            return -1;

        //Time to print what we have got
        switch(param_selector)
        {
        case 2:
            tmp_value = (rsp->data[4])& 1;
            printf("\n\tDHCP Discovery method\t: ");
            printf("\n\t\tManagement Controller ID String is %s", tmp_value ? "enabled" : "disabled");
            printf("\n\t\tVendor class identifier DCMI IANA and Vendor class-specific Informationa are %s", ((rsp->data[4])& 2) ? "enabled" : "disabled" );
            break;
        case 3:
            printf("\n\tInitial timeout interval\t: %i seconds", rsp->data[4]);
            break;
        case 4:
            printf("\n\tServer contact timeout interval\t: %i seconds", rsp->data[4] + (rsp->data[5]<<8));
            break;
        case 5:
            printf("\n\tServer contact retry interval\t: %i seconds", rsp->data[4] + (rsp->data[5] << 8));
            break;
        default:
            printf("\n\tConfiguration Parameter not supported.");
        }
    }
    return 0;
}


/******************************************************************************
 * This is Set DCMI Config Parameters Command
 *
 * returns ipmi response structure
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_setconfparam(struct ipmi_intf * intf, uint8_t param_selector, uint16_t value)
{
    struct ipmi_rq req;
    uint8_t msg_data[5];            /* number of request data bytes */
    
    msg_data[0] = IPMI_DCMI;        /* Group Extension Identification */
    msg_data[1] = param_selector;   /* Parameter selector */
    msg_data[2] = 0x00;             /* Set Selector (use 00h for parameters that only have one set). */
    
    if (param_selector > 3)         /* One bite more */
    {
        msg_data[3] = value & 0xFF;
        msg_data[4] = value >> 8;
    }else
        msg_data[3] = value;
    
    memset(&req, 0, sizeof(req));
    req.msg.netfn       = IPMI_NETFN_DCGRP;
    req.msg.cmd         = IPMI_DCMI_SETCONFPARAM; /* Set DCMI Config Parameters */
    req.msg.data        = msg_data; /* Contents above */
    if (param_selector > 3)         /* One bite more */
        req.msg.data_len = 5;   /* how many times does req.msg.data need to read */
    else
        req.msg.data_len = 4;   /* how many times does req.msg.data need to read */
    
    return intf->sendrecv(intf, &req);
}


/******************************************************************************
 *  Power Management get limit ipmi response
 *
 * This function returns the currently set power management settings as an
 * ipmi response structure.  The reason it returns in the rsp struct is so
 * that it can be used in the set limit [slimit()] function to populate
 * un-changed or un-edited values.
 *
 * returns ipmi response structure
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 ******************************************************************************/
struct ipmi_rs * ipmi_dcmi_pwr_glimit(struct ipmi_intf * intf)
{
    struct ipmi_rq req;
    uint8_t msg_data[3]; /* number of request data bytes */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = 0x00; /* reserved */
    msg_data[2] = 0x00; /* reserved */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_GETLMT; /* Get power limit */
    req.msg.data = msg_data; /* Contents above */
    req.msg.data_len = 3; /* how many times does req.msg.data need to read */

    return intf->sendrecv(intf, &req);
}
/* end Power Management get limit response */

/*******************************************************************************
 *  Power Management print the get limit command
 *
 * This function calls the get limit function that returns an ipmi response.
 *
 * returns 0 else 1 with error
 * @intf:   ipmi interface handler                                             *
 ******************************************************************************/
static int ipmi_dcmi_pwr_prnt_glimit(struct ipmi_intf * intf) {

    struct ipmi_rs * rsp;
    uint8_t realCc = 0xff;

    rsp = ipmi_dcmi_pwr_glimit(intf);

    /* rsp can be a null so check response before any operation on it to avoid sig segv */
    if (rsp != NULL)
    {
        realCc = rsp->ccode;
        GOOD_PWR_GLIMIT_CCODE(rsp->ccode);
    }

    if(chk_rsp(rsp))
        return -1;
    
    /* rsp->data[0] is equal to response data byte 2 in spec */
    /* printf("Group Extension Identification: %02x\n", rsp->data[0]); */

    struct power_limit val;
    memcpy(&val, rsp->data, sizeof (val));
       
    printf("\n    Current Limit State: %s\n",
        (realCc == 0) ? "Power Limit Active" : "No Active Power Limit" );
    printf("    Exception actions:   %s\n",
        val2str2(val.action, dcmi_pwrmgmt_action_vals));
    printf("    Power Limit:         %i Watts\n", val.limit);
    printf("    Correction time:     %i milliseconds\n", val.correction);
    printf("    Sampling period:     %i seconds\n", val.sample);
    printf("\n");
    return 0;
}
/* end print get limit */

/*******************************************************************************
 *  Power Management set limit
 *
 * Undocumented bounds:
 * Power limit: 0 - 0xFFFF
 * Correction period 5750ms to 28751ms or 0x1676 to 0x704F
 * sample period: 3 sec to 65 sec and 69+
 *
 * @intf:    ipmi interface handler                                            *
 * @option:  Power option to change                                            *
 * @value:   Value of the desired change                                       *
 ******************************************************************************/
static int ipmi_dcmi_pwr_slimit(struct ipmi_intf * intf, const char * option,
    const char * value) {
    struct ipmi_rs * rsp; /* ipmi response */
    struct ipmi_rq req; /* ipmi request (to send) */
    uint8_t msg_data[15]; /* number of request data bytes */
    uint32_t lvalue = strtol(value, NULL, 10);
    int i;
 
    
    rsp = ipmi_dcmi_pwr_glimit(intf); /* get the power limit settings */

    #if 0
    {
        unsigned char counter = 0;
        printf("DATA (%d): ", rsp->data_len);
        for(counter = 0; counter < rsp->data_len; counter ++) 
        {
            printf("%02X ", rsp->data[counter]);
        }
        printf("\n");
    }
    #endif

    /* rsp can be a null so check response before any operation on it to avoid sig segv */
    if (rsp != NULL)
        GOOD_PWR_GLIMIT_CCODE(rsp->ccode);

    if(chk_rsp(rsp))
        return -1;
    
    struct power_limit val;
    memcpy(&val, rsp->data, sizeof (val));
    
    /* same as above; sets the values of the val struct
    * DCMI group ID *
    * val.grp_id = rsp->data[0];
    * exception action *
    * val.action = rsp->data[3]; * 
    * 
    * power limit in Watts *
    * store 16 bits of the rsp from the 4th entity *
    * val.limit = *(uint16_t*)(&rsp->data[4]);
    * correction period in mS *
    * store 32 bits of the rsp from the 6th entity *
    * val.correction = *(uint32_t*)(&rsp->data[6]);
    * store 16 bits of the rsp from the 12th entity *
    * sample period in seconds *
    * val.sample = *(uint16_t*)(&rsp->data[12]);
    */

    switch (str2val2(option, dcmi_pwrmgmt_set_usage_vals)) {
    case 0x00:
        /* action */
        switch (str2val2(value, dcmi_pwrmgmt_action_vals)) {
        case 0x01:
            /* power_off */
            val.action = 1;
            break;
        case 0x11:
            /* sel_logging*/
            val.action = 0x11;
            break;
        case 0xFF:
            /* error - not a string we knew what to do with */
            return -1;
        }
        break;
    case 0x01:
        /* limit */
        val.limit = *(uint16_t*)(&lvalue);
        break;
    case 0x02:
        /* correction */
        val.correction = *(uint32_t*)(&lvalue);
        break;
    case 0x03:
        /* sample */
        val.sample = *(uint16_t*)(&lvalue);
        break;
    case 0xff:
        /* no valid options */
        return -1;
    }

    msg_data[0] = val.grp_id; /* Group Extension Identification */

    msg_data[1] = 0x00; /* reserved */
    msg_data[2] = 0x00; /* reserved */
    msg_data[3] = 0x00; /* reserved */

    msg_data[4] = val.action; /* exception action; 0x00 disables it */

    /* fill msg_data[5] with the first 16 bits of val.limit */
    *(uint16_t*)(&msg_data[5]) = val.limit;
    // msg_data[5] = 0xFF;
    // msg_data[6] = 0xFF;


    /* fill msg_data[7] with the first 32 bits of val.correction */
    *(uint32_t*)(&msg_data[7]) = val.correction;
    // msg_data[7] = 0x76;
    // msg_data[8] = 0x16;
    // msg_data[9] = 0x00;
    // msg_data[10] = 0x00;


    msg_data[11] = 0x00; /* reserved */
    msg_data[12] = 0x00; /* reserved */

    /* fill msg_data[7] with the first 16 bits of val.sample */
    *(uint16_t*)(&msg_data[13]) = val.sample;
    // msg_data[13] = 0x03;


    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_SETLMT; /* Set power limit */
    req.msg.data = msg_data; /* Contents above */
    req.msg.data_len = 15; /* how many times does req.msg.data need to read */

    rsp = intf->sendrecv(intf, &req);

    if(chk_rsp(rsp))
        return -1;
    
    return 0;
}
/* end Power Management set limit */

/*******************************************************************************
 *  Power Management activate deactivate
 *
 * @intf:    ipmi interface handler                                            *
 * @option:  uint8_t - 0 to deactivate or 1 to activate                        *
 ******************************************************************************/
static int ipmi_dcmi_pwr_actdeact(struct ipmi_intf * intf, uint8_t option) {
    struct ipmi_rs * rsp;
    struct ipmi_rq req;
    uint8_t msg_data[4]; /* number of request data bytes */

    msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
    msg_data[1] = option; /* 0 = Deactivate 1 = Activate */
    msg_data[2] = 0x00; /* reserved */
    msg_data[3] = 0x00; /* reserved */

    memset(&req, 0, sizeof(req));
    req.msg.netfn = IPMI_NETFN_DCGRP;
    req.msg.cmd = IPMI_DCMI_PWRACT; /* Act-deactivate power limit */
    req.msg.data = msg_data; /* Contents above */
    req.msg.data_len = 4; /* how mant times does req.msg.data need to read */

    rsp = intf->sendrecv(intf, &req);

    if(chk_rsp(rsp))
        return -1;
    
    printf("\n    Power limit successfully ");
    if (option == 0x00) {
        printf("deactivated");
    } else {
        printf("activated");
    }
    printf("\n");
    
    return 0;
}
/* end power management activate/deactivate */


/*******************************************************************************
 *  main
 *
 * @intf:   dcmi interface handler
 * @argc:   argument count
 * @argv:   argument vector
 ******************************************************************************/
int ipmi_dcmi_main(struct ipmi_intf * intf, int argc, char **argv) {
    int rc = 0; /* Return code used when calling some of the dcmi commands */
    uint8_t ctl = 0;
    int i, ii, instances;
    struct ipmi_rs *rsp;
   
    if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) {
        print_strs(
            dcmi_cmd_vals, "Data Center Management Interface commands", -1, 0);
        return -1;
    }

    /* start the cmd requested */
    switch (str2val2(argv[0], dcmi_cmd_vals)) {
    case 0x00:
        /* discover capabilities*/
        
        for (i = 1; dcmi_capable_vals[i-1].str != NULL; i++) {
            if(ipmi_dcmi_prnt_getcapabilities(intf, i) < 0) {
                printf("Error discovering %s capabilities!\n",
                    val2str2(i, dcmi_capable_vals));
                return -1;
            }   
        }
        break;
        /*  end discover */

    case 0x01:
        /* power */

        argv++;
        if (argv[0] == NULL) {
            print_strs(dcmi_pwrmgmt_vals, "power <command>", -1, 0);
            return -1;
        }
        /* power management */
        switch (str2val2(argv[0], dcmi_pwrmgmt_vals)) {
        case 0x00:
            /* get reading */
            rc = ipmi_dcmi_pwr_rd(intf);
            break;
        case 0x01:
            /* get limit */
            /* because the get limit function is also used to populate
             * unchanged values for the set limit command it returns an ipmi
             * response structure
             */
            rc = ipmi_dcmi_pwr_prnt_glimit(intf);
            break;
        case 0x02:
            /* set limit */
            if (argc < 4) {
                print_strs(dcmi_pwrmgmt_set_usage_vals,
                    "set_limit <parameter> <value>", -1, 0);
                return -1;
            }
            if ( argc == 10) //Let`s initialize dcmi power parameters
            {
                struct ipmi_rq req;
                uint8_t data[256];

                memset(data, 0, sizeof(data));
                memset(&req, 0, sizeof(req));

                req.msg.netfn = IPMI_NETFN_DCGRP;
                req.msg.lun = 0x00;
                req.msg.cmd = IPMI_DCMI_SETLMT; /* Set power limit */
                req.msg.data = data; /* Contents above */
                req.msg.data_len =  15;

                data[0] = IPMI_DCMI; /* Group Extension Identification */
                data[1] = 0x0;  /* reserved */
                data[2] = 0x0;  /* reserved */
                data[3] = 0x0;  /* reserved */

                /* action */
                switch (str2val2(argv[2], dcmi_pwrmgmt_action_vals)) {
                case 0x01:
                    /* power_off */
                    data[4] = 0x01;
                    break;
                case 0x11:
                    /* sel_logging*/
                    data[4] = 0x11;
                    break;
                case 0xFF:
                    /* error - not a string we knew what to do with */
                    return -1;
                }
                *(uint16_t*)(&data[5]) = strtol(argv[4], NULL, 10);/* limit */
                *(uint32_t*)(&data[7]) = strtol(argv[6], NULL, 10);/* correction */
                data[11] = 0x00;  /* reserved */
                data[12] = 0x00;  /* reserved */
                *(uint16_t*)(&data[13]) = strtol(argv[8], NULL, 10); /* sample */

                rsp = intf->sendrecv(intf, &req);
                if(chk_rsp(rsp))
                    return -1;
            }else{
                /* loop through each parameter and value until we have neither */
                while ((argv[1] != NULL) && (argv[2] != NULL)) {
                    rc = ipmi_dcmi_pwr_slimit(intf, argv[1], argv[2]);
                    /* catch any error that the set limit function returned */
                    if (rc > 0) {
                        print_strs(dcmi_pwrmgmt_set_usage_vals,
                            "set_limit <parameter> <value>", -1, 0);
                        return -1;
                    }
                    /* the first argument is the command and the second is the 
                     * value.  Move argv two places; what is now 3 will be 1 */
                    argv+=2;
                }
            }
            rc = ipmi_dcmi_pwr_prnt_glimit(intf);
            break;
        case 0x03:
            /* activate */
            rc = ipmi_dcmi_pwr_actdeact(intf, 1);
            break;
        case 0x04:
            /* deactivate */
            rc = ipmi_dcmi_pwr_actdeact(intf, 0);
            break;
        default:
            /* no valid options */
            print_strs(dcmi_pwrmgmt_vals, "power <command>", -1, 0);
            break;
        }
        /* power mgmt end */
        break;
        /* end power command */
    case 0x02:
        /* sensor print */
        /* Look for each item in the dcmi_discvry_snsr_vals struct
         * and if it exists, print the sdr record id(s) for it.
         * Use the val from each one as the sensor number. */
        for (i = 0; dcmi_discvry_snsr_vals[i].str != NULL; i++) {
            /* get all of the information about this sensor */
            rc = ipmi_dcmi_prnt_discvry_snsr(
                intf, dcmi_discvry_snsr_vals[i].val);
        }
        break;
        /* end sensor print */
    case 0x03:
        /* asset tag */
        if(ipmi_dcmi_prnt_getassettag(intf) < 0) {
            printf("Error getting asset tag!\n");
            return -1;
        }   
        break;
        /* end asset tag */
    case 0x04:
        /* set asset tag */
        {
            if (argc == 1 ) {
                print_strs(
                    dcmi_cmd_vals, "Data Center Management Interface commands", -1, 0);
            return -1;
            }
            if(ipmi_dcmi_prnt_setassettag(intf, argv[1]) < 0) {
                printf("\nError setting asset tag!\n");
                return -1;
            }   
            break;
        }
        /* end set asset tag */
    case 0x05:
        /* get management controller identifier string */
        if(ipmi_dcmi_prnt_getmngctrlids(intf) < 0) {
            printf("Error getting management controller identifier string!\n");
            return -1;
        }   
        break;
        /* end get management controller identifier string */

    case 0x06:
        /* set management controller identifier string */
        {
            if (argc == 1 ) {
                print_strs(
                    dcmi_cmd_vals, "Data Center Management Interface commands", -1, 0);
            return -1;
            }
            if(ipmi_dcmi_prnt_setmngctrlids(intf, argv[1]) < 0) {
                printf("Error setting management controller identifier string!\n");
                return -1;
            }   
            break;
        }
        /* end set management controller identifier string */

    case 0x07:
    {
        uint8_t entityID, entityInst;
        uint8_t persistanceFlag;
        uint8_t actionHardPowerOff;
        uint8_t actionLogToSEL;
        uint8_t tempLimit;
        uint16_t samplingTime;
        uint8_t samplingTimeLSB;
        uint8_t samplingTimeMSB;

        /* Thermal policy get/set */
        /* dcmitool dcmi thermalpolicy get */

        switch (str2val2(argv[1], dcmi_thermalpolicy_vals)) {
        case 0x00:
            if (argc < 4)
            {
                printf("Get <entityID> <instanceID>\n");
                return -1;
            }

            entityID = (uint8_t)strtol(argv[2], NULL,0);
            entityInst  = (uint8_t) strtol(argv[3], NULL, 0);
            rc = ipmi_dcmi_getthermalpolicy(intf,  entityID, entityInst);
            break;

        case 0x01:
            if (argc < 4)
            {
                printf("Set <entityID> <instanceID>\n");
                return -1;
            }
            else if (argc < 9)
            {
                print_strs(dcmi_thermalpolicy_set_parameters_vals,
                        "Set thermalpolicy instance parameters: "
                        "<volatile/nonvolatile/disabled> "
                        "<poweroff/nopoweroff/disabled> "
                        "<sel/nosel/disabled> <templimitByte> <exceptionTime>", -1, 0);
                return -1;
            }

            entityID = (uint8_t) strtol(argv[2],NULL, 0);
            entityInst  = (uint8_t) strtol(argv[3], NULL, 0);

            persistanceFlag = (uint8_t) str2val2(argv[4], dcmi_thermalpolicy_set_parameters_vals);
            actionHardPowerOff = (uint8_t) str2val2(argv[5], dcmi_thermalpolicy_set_parameters_vals);
            actionLogToSEL = (uint8_t) str2val2(argv[6], dcmi_thermalpolicy_set_parameters_vals);
            tempLimit = (uint8_t) strtol(argv[7], NULL, 0);
            samplingTime = (uint16_t) strtol(argv[8], NULL, 0);
            samplingTimeLSB =  (samplingTime & 0xFF);
            samplingTimeMSB = ((samplingTime & 0xFF00) >> 8);

            rc = ipmi_dcmi_setthermalpolicy(intf,
                                            entityID,
                                            entityInst,
                                            persistanceFlag,
                                            actionHardPowerOff,
                                            actionLogToSEL,
                                            tempLimit,
                                            samplingTimeLSB,
                                            samplingTimeMSB);

            break;

        default:
            print_strs(dcmi_thermalpolicy_vals, "thermalpolicy <command>", -1, 0);
            return -1;
        }
        break;
    }
    case 0x08:
        if(ipmi_dcmi_prnt_get_temp_readings(intf) < 0 )
        {
            printf("Error get temperature readings!\n");
        };
        break;
    case 0x09:
        if(ipmi_dcmi_prnt_getconfparam(intf) < 0 )
        {
            printf("Error Get DCMI Configuration Parameters!\n");
        };
        break;
    case 0x0A:
    {
        switch (argc) {
        case 2:
            if(strncmp(argv[1], "activate_dhcp", 13) !=0){
                print_strs( dcmi_conf_param_vals, "DCMI Configuration Parameters", -1, 0);
                return -1;
            }
            break;
        default:
            if (argc != 3 || strncmp(argv[1], "help", 4) == 0)
            {
                print_strs( dcmi_conf_param_vals, "DCMI Configuration Parameters", -1, 0);
                return -1;
            }
        }

        if (strncmp(argv[1], "activate_dhcp", 13) == 0)
            rsp = ipmi_dcmi_setconfparam(intf, 1, 1);
        else {
            rsp = ipmi_dcmi_setconfparam(intf, str2val2(argv[1], dcmi_conf_param_vals), (uint16_t) strtol(argv[2], NULL, 0));
        }
        if(chk_rsp(rsp))
        {
            printf("Error Set DCMI Configuration Parameters!\n");
        }
        break;
    }
    case 0x0B:
    {
        if (intf->session == NULL){
            printf("\nOOB discovery is available only via RMCP interface.\n");
            return -1;
        }
        if(ipmi_dcmi_prnt_oobDiscover(intf) < 0)
        {
            printf("\nOOB discovering capabilities failed.\n");
            return -1;
        }
        break;
    }
    default:
        /* couldn't detect what the user entered */
        print_strs(
            dcmi_cmd_vals, "Data Center Management Interface commands", -1, 0);
        return -1;
        break;
    }
    printf("\n");
    return 0;
}

/*******************************************************************************
 * Display DCMI sensor information                                             *
 * Uses the ipmi_sdr_get_next_header to read SDR header and compare to the     *
 * target Record ID. Then either ipmi_sensor_print_full or                     *
 * ipmi_sensor_print_compact is called to print the data                       *
 *                                                                             *
 * @intf:   ipmi interface handler                                             *
 * @rec_id: target Record ID                                                   *
 *                                                                             *
 ******************************************************************************/
static int ipmi_print_sensor_info(struct ipmi_intf *intf, uint16_t rec_id)
{
    struct sdr_get_rs *header;
    struct ipmi_sdr_iterator *itr;
    int rc = 0;
    int r = 0;
    uint8_t *rec = NULL;
        
    itr = ipmi_sdr_start(intf, 0);
    if (itr == NULL) {
        lprintf(LOG_ERR, "Unable to open SDR for reading");
        return -1;
    }

    while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) {        
        if(header->id == rec_id)
            break;
    }

    if(header != NULL) {
    /* yes, we found the SDR for this record ID, now get full record */
        rec = ipmi_sdr_get_record(intf, header, itr);
        if (rec == NULL) {
            lprintf(LOG_DEBUG, "rec == NULL");
            rc = -1;
        }

        if((header->type == SDR_RECORD_TYPE_FULL_SENSOR) ||
           (header->type == SDR_RECORD_TYPE_COMPACT_SENSOR)) {
            r = ipmi_sensor_print_fc(intf,
	    	(struct sdr_record_common_sensor *)rec, header->type);
        }
        else
            rc = -1;
    }
    else {
        rc = -1;    /* record id not found */
    }   

    free(rec);
    ipmi_sdr_end(intf, itr);

    return rc;
}
