/*
 * 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
 */
#define _BSD_SOURCE

#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;
extern int csv_output;
static int ipmi_print_sensor_info(struct ipmi_intf *intf, uint16_t rec_id);

/*******************************************************************************
 * 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", "              Platform power limit command options"         },
	{ 0x02, "sensors", "            Prints the available DCMI sensors"            },
	{ 0x03, "asset_tag", "          Prints the platform's asset tag"              },
	{ 0x04, "set_asset_tag", "      Sets the platform's 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", "    <no_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/get action commands */
const struct dcmi_cmd dcmi_pwrmgmt_get_action_vals[] = {
	{ 0x00, "No Action", ""},
	{ 0x01, "Hard Power Off & Log Event to SEL", ""},

	{ 0x02, "OEM reserved (02h)", ""},
	{ 0x03, "OEM reserved (03h)", ""},
	{ 0x04, "OEM reserved (04h)", ""},
	{ 0x05, "OEM reserved (05h)", ""},
	{ 0x06, "OEM reserved (06h)", ""},
	{ 0x07, "OEM reserved (07h)", ""},
	{ 0x08, "OEM reserved (08h)", ""},
	{ 0x09, "OEM reserved (09h)", ""},
	{ 0x0a, "OEM reserved (0ah)", ""},
	{ 0x0b, "OEM reserved (0bh)", ""},
	{ 0x0c, "OEM reserved (0ch)", ""},
	{ 0x0d, "OEM reserved (0dh)", ""},
	{ 0x0e, "OEM reserved (0eh)", ""},
	{ 0x0f, "OEM reserved (0fh)", ""},
	{ 0x10, "OEM reserved (10h)", ""},

	{ 0x11, "Log Event to SEL", ""},
	{ 0xFF, NULL, NULL      }
};

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

	{ 0x02, "oem_02", "OEM reserved (02h)"},
	{ 0x03, "oem_03", "OEM reserved (03h)"},
	{ 0x04, "oem_04", "OEM reserved (04h)"},
	{ 0x05, "oem_05", "OEM reserved (05h)"},
	{ 0x06, "oem_06", "OEM reserved (06h)"},
	{ 0x07, "oem_07", "OEM reserved (07h)"},
	{ 0x08, "oem_08", "OEM reserved (08h)"},
	{ 0x09, "oem_09", "OEM reserved (09h)"},
	{ 0x0a, "oem_0a", "OEM reserved (0ah)"},
	{ 0x0b, "oem_0b", "OEM reserved (0bh)"},
	{ 0x0c, "oem_0c", "OEM reserved (0ch)"},
	{ 0x0d, "oem_0d", "OEM reserved (0dh)"},
	{ 0x0e, "oem_0e", "OEM reserved (0eh)"},
	{ 0x0f, "oem_0f", "OEM reserved (0fh)"},
	{ 0x10, "oem_10", "OEM reserved (10h)"},

	{ 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 }
};

/*
 * Start of Node Manager Operations
 */

const struct dcmi_cmd dcmi_sampling_vals[] = {
	{ 0x05, "5_sec", "" },
	{ 0x0f, "15_sec", "" },
	{ 0x1E, "30_sec", "" },
	{ 0x41, "1_min", "" },
	{ 0x43, "3_min", "" },
	{ 0x47, "7_min", "" },
	{ 0x4F, "15_min", "" },
	{ 0x5E, "30_min", "" },
	{ 0x81, "1_hour", ""},
	{ 0x00, NULL, NULL },
};

/* Primary Node Manager commands */
const struct dcmi_cmd nm_cmd_vals[] = {
	{ 0x00, "discover", "Discover Node Manager " },
	{ 0x01, "capability", "Get Node Manager Capabilities" },
	{ 0x02, "control", "Enable/Disable Policy Control" },
	{ 0x03, "policy", "Add/Remove Policies" },
	{ 0x04, "statistics", "Get Statistics" },
	{ 0x05, "power", "Set Power Draw Range" },
	{ 0x06, "suspend", "Set/Get Policy suspend periods" },
	{ 0x07, "reset", "Reset Statistics" },
	{ 0x08, "alert", "Set/Get/Clear Alert destination" },
	{ 0x09, "threshold", "Set/Get Alert Thresholds" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_ctl_cmds[] = {
	{ 0x01, "enable", " <control scope>" },
	{ 0x00, "disable", "<control scope>"},
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_ctl_domain[] = {
	{ 0x00, "global", "" },
	{ 0x02, "per_domain", "<platform|CPU|Memory> (default is platform)" },
	{ 0x04, "per_policy", "<0-7>" },
	{ 0xFF, NULL, NULL },
};

/* Node Manager Domain codes */
const struct dcmi_cmd nm_domain_vals[] = {
	{ 0x00, "platform", ""  },
	{ 0x01, "CPU", ""  },
	{ 0x02, "Memory", "" },
	{ 0x03, "protection", "" },
	{ 0x04, "I/O", "" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_version_vals[] = {
	{ 0x01, "1.0", "" },
	{ 0x02, "1.5", "" },
	{ 0x03, "2.0", "" },
	{ 0x04, "2.5", "" },
	{ 0x05, "3.0", "" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_capability_opts[] = {
	{ 0x01, "domain", "<platform|CPU|Memory> (default is platform)" },
	{ 0x02, "inlet", "Inlet temp trigger" },
	{ 0x03, "missing", "Missing Power reading trigger" },
	{ 0x04, "reset", "Time after Host reset trigger" },
	{ 0x05, "boot", "Boot time policy" },
	{ 0xFF, NULL, NULL },
};

const struct  dcmi_cmd nm_policy_type_vals[] = {
	{ 0x00, "No trigger, use Power Limit", "" },
	{ 0x01, "Inlet temp trigger", "" },
	{ 0x02, "Missing Power reading trigger", "" },
	{ 0x03, "Time after Host reset trigger", "" },
	{ 0x04, "number of cores to disable at boot time", "" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_stats_opts[] = {
	{ 0x01, "domain", "<platform|CPU|Memory> (default is platform)" },
	{ 0x02, "policy_id", "<0-7>" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_stats_mode[] = {
	{ 0x01, "power", "global power" },
	{ 0x02, "temps", "inlet temperature" },
	{ 0x11, "policy_power", "per policy power" },
	{ 0x12, "policy_temps", "per policy inlet temp" },
	{ 0x13, "policy_throt", "per policy throttling stats" },
	{ 0x1B, "requests", "unhandled requests" },
	{ 0x1C, "response", "response time" },
	{ 0x1D, "cpu_throttling", "CPU throttling" },
	{ 0x1E, "mem_throttling", "memory throttling" },
	{ 0x1F, "comm_fail", "host communication failures" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_policy_action[] = {
	{ 0x00, "get", "nm policy get policy_id <0-7> [domain <platform|CPU|Memory>]" },
	{ 0x04, "add", "nm policy add policy_id <0-7> [domain <platform|CPU|Memory>] correction auto|soft|hard power <watts>|inlet <temp> trig_lim <param> stats <seconds> enable|disable" },
	{ 0x05, "remove", "nm policy remove policy_id <0-7> [domain <platform|CPU|Memory>]" },
	{ 0x06, "limiting", "nm policy limiting [domain <platform|CPU|Memory>]" },
	{ 0xFF, NULL, NULL },
};
const struct dcmi_cmd nm_policy_options[] = {
	{ 0x01, "enable", "" },
	{ 0x02, "disable", "" },
	{ 0x03, "domain", "" },
	{ 0x04, "inlet", "inlet air temp full limiting (SCRAM)"},
	{ 0x06, "correction", "auto, soft, hard" },
	{ 0x08, "power", "power limit in watts" },
	{ 0x09, "trig_lim", "time to send alert" },
	{ 0x0A, "stats", "moving window averaging time" },
	{ 0x0B, "policy_id", "policy number" },
	{ 0x0C, "volatile", "save policy in volatiel memory" },
	{ 0x0D, "cores_off", "at boot time, disable N cores" },
	{ 0xFF, NULL, NULL },
};

/* if "trigger" command used from nm_policy_options */
const struct dcmi_cmd nm_trigger[] = {
	{ 0x00, "none", "" },
	{ 0x01, "temp", "" },
	{ 0x02, "reset", "" },
	{ 0x03, "boot", "" },
	{ 0xFF, NULL, NULL },
};

/* if "correction" used from nm_policy_options */
const struct dcmi_cmd nm_correction[] = {
	{ 0x00, "auto", "" },
	{ 0x01, "soft", "" },
	{ 0x02, "hard", "" },
	{ 0xFF, NULL, NULL },
};

/* returned codes from get policy */
const struct dcmi_cmd nm_correction_vals[] = {
	{ 0x00, "no T-state use", "" },
	{ 0x01, "no T-state use", "" },
	{ 0x02, "use T-states", "" },
	{ 0xFF, NULL, NULL },
};

/* if "exception" used from nm_policy_options */
const struct dcmi_cmd nm_exception[] = {
	{ 0x00, "none", "" },
	{ 0x01, "alert", "" },
	{ 0x02, "shutdown", "" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_reset_mode[] = {
	{ 0x00, "global", "" },
	{ 0x01, "per_policy", "" },
	{ 0x1B, "requests", "" },
	{ 0x1C, "response", "" },
	{ 0x1D, "throttling", "" },
	{ 0x1E, "memory", "", },
	{ 0x1F, "comm", "" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_power_range[] = {
	{ 0x01, "domain", "domain <platform|CPU|Memory> (default is platform)" },
	{ 0x02, "min", " min <integer value>" },
	{ 0x03, "max", "max <integer value>" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_alert_opts[] = {
	{ 0x01, "set", "nm alert set chan <chan> dest <dest> string <string>" },
	{ 0x02, "get", "nm alert get" },
	{ 0x03, "clear", "nm alert clear dest <dest>" },
};

const struct dcmi_cmd nm_set_alert_param[] = {
	{ 0x01, "chan", "chan <channel>" },
	{ 0x02, "dest", "dest <destination>" },
	{ 0x03, "string", "string <string>" },
};

const struct dcmi_cmd nm_thresh_cmds[] = {
	{ 0x01, "set", "nm thresh set [domain <platform|CPU|Memory>] policy_id <policy> thresh_array" },
	{ 0x02, "get", "nm thresh get [domain <platform|CPU|Memory>] policy_id <policy>" },
};

const struct dcmi_cmd nm_thresh_param[] = {
	{ 0x01, "domain", "<platform|CPU|Memory> (default is platform)" },
	{ 0x02, "policy_id", "<0-7>" },
	{ 0xFF, NULL, NULL },
};

const struct dcmi_cmd nm_suspend_cmds[] = {
	{ 0x01, "set", "nm suspend set [domain <platform|CPU|Memory]> policy_id <policy> <start> <stop> <pattern>" },
	{ 0x02, "get", "nm suspend get [domain <platform|CPU|Memory]> policy_id <policy>" },
};

const struct valstr nm_ccode_vals[] = {
	{ 0x80, "Policy ID Invalid"},
	{ 0x81, "Domain ID Invalid"},
	{ 0x82, "Unknown policy trigger type"},
	{ 0x84, "Power Limit out of range"},
	{ 0x85, "Correction Time out of range"},
	{ 0x86, "Policy Trigger value out of range"},
	{ 0x88, "Invalid Mode"},
	{ 0x89, "Statistics Reporting Period out of range"},
	{ 0x8B, "Invalid value for Aggressive CPU correction field"},
	{ 0xA1, "No policy is currently limiting for the specified domain ID"},
	{ 0xC4, "No space available"},
	{ 0xD4, "Insufficient privledge level due wrong responder LUN"},
	{ 0xD5, "Policy exists and param unchangeable while enabled"},
	{ 0xD6, "Command subfunction disabled or unavailable"},
	{ 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, sizeof (un_str));
	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->ccode);
		return 1;
	} else if (rsp->ccode > 0) {
		lprintf(LOG_ERR, "\n    DCMI request failed because: %s (%x)",
				val2str(rsp->ccode, completion_code_vals), rsp->ccode);
		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;
}

/* check the Node Manager response from the BMC
 * @rsp:       Response data structure
 */
static int
chk_nm_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    No response to NM request");
		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 <= 0xD6)) {
		lprintf(LOG_ERR, "\n    NM request failed because: %s (%x)",
		val2str(rsp->ccode, nm_ccode_vals), rsp->ccode);
		return 1;
	} else if (rsp->ccode > 0) {
		lprintf(LOG_ERR, "\n    NM request failed because: %s (%x)",
			val2str(rsp->ccode, completion_code_vals), rsp->ccode);
		return 1;
	}
	/* check to make sure this is a DCMI firmware */
	if(rsp->data[0] != 0x57) {
		printf("\n    A valid NM 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)
{
# ifndef IPMI_INTF_LANPLUS
	lprintf(LOG_ERR,
			"DCMI Discovery is available only when LANplus(IPMI v2.0) is enabled.");
	return (-1);
# else
	struct ipmi_session_params *p;

	if (intf->opened == 0 && intf->open != NULL) {
		if (intf->open(intf) < 0)
			return (-1);
	}
	if (intf == NULL || intf->session == NULL)
		return -1;

	p = &intf->ssn_params;

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

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

	intf->abort = 1;
	intf->session->sol_data.sequence_number = 1;

	if (ipmi_intf_socket_connect (intf)  == -1) {
		lprintf(LOG_ERR, "Could not open socket!");
		return -1;
	}

	if (intf->fd < 0) {
		lperror(LOG_ERR, "Connect to %s failed",
			p->hostname);
		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)
{
	struct capabilities cape;
	struct ipmi_rs * rsp;
	uint8_t reply[16];
	rsp = ipmi_dcmi_getcapabilities(intf, selector);
	int j;

	if(chk_rsp(rsp))
		return -1;

	/* if there were no errors, the command worked! */
	memcpy(&cape, rsp->data, sizeof (cape));
	memcpy(&reply, rsp->data, sizeof (reply));
	/* check to make sure that this is a 1.0/1.1/1.5 command */
	if ((cape.conformance != IPMI_DCMI_CONFORM)
			&& (cape.conformance != IPMI_DCMI_1_1_CONFORM)
			&& (cape.conformance != IPMI_DCMI_1_5_CONFORM)) {
		lprintf(LOG_ERR,
				"ERROR!  This command is not available on this platform");
		return -1;
	}
	/* check to make sure that this is a rev .01 or .02 */
	if (cape.revision != 0x01 && cape.revision != 0x02) {
		lprintf(LOG_ERR,
				"ERROR!  This command is not compatible with this version");
		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;
	case 0x05:
		/* Node Manager */
		printf("\n    Node Manager Get DCMI Capability Info: \n");
		printf("         DCMI Specification %d.%d\n", reply[1], reply[2]);
		printf("         Rolling average time period options: %d\n", reply[4]);
		printf("         Sample time options: ");
		for (j = 1; dcmi_sampling_vals[j-1].str != NULL; j++)
			printf(" %s ", val2str2(reply[4+j],dcmi_sampling_vals));
		printf("\n");
		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)
{
	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 */
	/* How many times does req.msg.data need to read */
	req.msg.data_len = length + 3;
	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)
{
	struct ipmi_rs * rsp; /* ipmi response */
	uint8_t tmpData[DCMI_MAX_BYTE_SIZE];
	int32_t taglength = 0;
	uint8_t getlength = 0;
	uint8_t offset = 0;
	uint8_t i;

	/* now let's get the asset tag length */
	taglength = strlen((char *)data);
	if (taglength > 64){
		lprintf(LOG_ERR, "\nValue is too long.");
		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 */
	/* How many times does req.msg.data need to read */
	req.msg.data_len = 3;
	return intf->sendrecv(intf, &req);
}

static int
ipmi_dcmi_prnt_getmngctrlids(struct ipmi_intf * intf)
{
	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 */
	/* How many times does req.msg.data need to read */
	req.msg.data_len = 3 + length;
	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)
{
	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((char *)data) +1;

	if (taglength > 64) {
		lprintf(LOG_ERR, "\nValue is too long.");
		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);
		/* because after call "Set mc id string" RMCP+ will go down
		 * we have no "rsp"
		 */
		if (strncmp(intf->name, "lanplus", 7)) {
			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);
}

/* 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], sizeof (id_buff));
		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, uint8_t sample_time)
{
	struct ipmi_rs * rsp;
	struct ipmi_rq req;
	struct power_reading val;
	struct tm tm_t;
	time_t t;
	uint8_t msg_data[4]; /* number of request data bytes */
	memset(&tm_t, 0, sizeof(tm_t));
	memset(&t, 0, sizeof(t));

	msg_data[0] = IPMI_DCMI; /* Group Extension Identification */
	if (sample_time) {
		msg_data[1] = 0x02; /* Enhanced Power Statistics */
		msg_data[2] = sample_time;
	} else {
		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]); */
	memcpy(&val, rsp->data, sizeof (val));
	t = val.time_stamp;
	gmtime_r(&t, &tm_t);
	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",
			asctime(&tm_t));
	printf("    Sampling period:                          ");
	if (sample_time)
		printf("%s \n", val2str2(val.sample,dcmi_sampling_vals));
	else
		printf("%08u Seconds.\n", val.sample/1000);
	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;
	struct thermal_limit val;
	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 */
	memcpy(&val, rsp->data, sizeof (val));
	printf("\n");
	printf("    Persistence 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 */
	/* persistance and actions or disabled if no actions */
	msg_data[3] = (((persistanceFlag ? 1 : 0) << 7) |
			((actionHardPowerOff? 1 : 0) << 6) |
			((actionLogToSEL ? 1 : 0) << 5));
	msg_data[4] = tempLimit;
	msg_data[5] = samplingTimeLSB;
	msg_data[6] = samplingTimeMSB;

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

	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 */
	/* how many times does req.msg.data need to read */
	req.msg.data_len = 5;
	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 */
	/* Set Selector. Selects a given set of parameters under a given Parameter
	 * selector value. 00h if parameter doesn't use a Set Selector.
	 */
	msg_data[2] = 0x00;

	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 */
	/* how many times does req.msg.data need to read */
	req.msg.data_len = 3;
	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;
	/* We are not interested in parameter 1 which always will return 0 */
	for (param_selector = 2 ; param_selector <= dcmi_conf_params;
			param_selector++) {
		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 */
	/* Set Selector (use 00h for parameters that only have one set). */
	msg_data[2] = 0x00;

	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 */
		/* how many times does req.msg.data need to read */
		req.msg.data_len = 5;
	} else {
		/* how many times does req.msg.data need to read */
		req.msg.data_len = 4;
	}
	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 */
	/* how many times does req.msg.data need to read */
	req.msg.data_len = 3;

	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;
	struct power_limit val;
	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]); */
	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_get_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) */
	struct power_limit val;
	uint8_t msg_data[15]; /* number of request data bytes */
	uint32_t lvalue = 0;

	rsp = ipmi_dcmi_pwr_glimit(intf); /* get the power limit settings */
	/* 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;
	}
	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]);
	 */
	lprintf(LOG_INFO,
			"DCMI IN  Limit=%d Correction=%d Action=%d Sample=%d\n",
			val.limit, val.correction, val.action, val.sample);
	switch (str2val2(option, dcmi_pwrmgmt_set_usage_vals)) {
	case 0x00:
		/* action */
		switch (str2val2(value, dcmi_pwrmgmt_action_vals)) {
		case 0x00:
			/* no_action */
			val.action = 0;
			break;
		case 0x01:
			/* power_off */
			val.action = 1;
			break;
		case 0x02: 
			/* OEM reserved action */
			val.action = 0x02;
			break;
		case 0x03: 
			/* OEM reserved action */
			val.action = 0x03;
			break;
		case 0x04:
			/* OEM reserved action */
			val.action = 0x04;
			break;
		case 0x05:
			/* OEM reserved action */
			val.action = 0x05;
			break;
		case 0x06:
			/* OEM reserved action */
			val.action = 0x06;
			break;
		case 0x07:
			/* OEM reserved action */
			val.action = 0x07;
			break;
		case 0x08:
			/* OEM reserved action */
			val.action = 0x08;
			break;
		case 0x09:
			/* OEM reserved action */
			val.action = 0x09;
			break;
		case 0x0a:
			/* OEM reserved action */
			val.action = 0x0a;
			break;
		case 0x0b:
			/* OEM reserved action */
			val.action = 0x0b;
			break;
		case 0x0c:
			/* OEM reserved action */
			val.action = 0x0c;
			break;
		case 0x0d:
			/* OEM reserved action */
			val.action = 0x0d;
			break;
		case 0x0e:
			/* OEM reserved action */
			val.action = 0x0e;
			break;
		case 0x0f:
			/* OEM reserved action */
			val.action = 0x0f;
			break;
		case 0x10:
			/* OEM reserved action */
			val.action = 0x10;
			break;
		case 0x11:
			/* sel_logging*/
			val.action = 0x11;
			break;
		case 0xFF:
			/* error - not a string we knew what to do with */
			lprintf(LOG_ERR, "Given %s '%s' is invalid.",
					option, value);
			return -1;
		}
		break;
	case 0x01:
		/* limit */
		if (str2uint(value, &lvalue) != 0) {
			lprintf(LOG_ERR, "Given %s '%s' is invalid.",
					option, value);
			return (-1);
		}
		val.limit = *(uint16_t*)(&lvalue);
		break;
	case 0x02:
		/* correction */
		if (str2uint(value, &lvalue) != 0) {
			lprintf(LOG_ERR, "Given %s '%s' is invalid.",
					option, value);
			return (-1);
		}
		val.correction = *(uint32_t*)(&lvalue);
		break;
	case 0x03:
		/* sample */
		if (str2uint(value, &lvalue) != 0) {
			lprintf(LOG_ERR, "Given %s '%s' is invalid.",
					option, value);
			return (-1);
		}
		val.sample = *(uint16_t*)(&lvalue);
		break;
	case 0xff:
		/* no valid options */
		return -1;
	}
	lprintf(LOG_INFO, "DCMI OUT Limit=%d Correction=%d Action=%d Sample=%d\n", val.limit, val.correction, val.action, val.sample);

	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[13] 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 */
	/* how many times does req.msg.data need to read */
	req.msg.data_len = 15;

	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 */

/* Node Manager discover */
static int
_ipmi_nm_discover(struct ipmi_intf * intf, struct nm_discover *disc)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[3]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_VERSION;
	req.msg.data = msg_data;
	req.msg.data_len = 3;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	memcpy(disc, rsp->data, sizeof (struct nm_discover));
	return 0;
}
/* Get NM capabilities
 *
 * This function returns the available capabilities of the platform.
 *
 * returns success/failure
 *
 * @intf:   ipmi interface handler
 * @caps:   fills in capability struct
 */
static int
_ipmi_nm_getcapabilities(struct ipmi_intf * intf, uint8_t domain, uint8_t trigger, struct nm_capability *caps)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[5]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = domain;
	msg_data[4] = trigger; /* power control policy or trigger */
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_CAP;
	req.msg.data = msg_data;
	req.msg.data_len = 5;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	memcpy(caps, rsp->data, sizeof (struct nm_capability));
	return 0;
}

static int
_ipmi_nm_get_policy(struct ipmi_intf * intf, uint8_t domain, uint8_t policy_id, struct nm_get_policy *policy)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[5]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = domain;
	msg_data[4] = policy_id;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_POLICY;
	req.msg.data = msg_data;
	req.msg.data_len = 5;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	memcpy(policy, rsp->data, sizeof (struct nm_get_policy));
	return 0;
}
static int
_ipmi_nm_set_policy(struct ipmi_intf * intf, struct nm_policy *policy)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;

	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_SET_POLICY;
	req.msg.data = (uint8_t *)policy;
	req.msg.data_len = sizeof(struct nm_policy);
	policy->intel_id[0] = 0x57; policy->intel_id[1] =1; policy->intel_id[2] =0;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

static int
_ipmi_nm_policy_limiting(struct ipmi_intf * intf, uint8_t domain)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[4]; /* 'raw' data to be sent to the BMC */

	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_LIMITING;
	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = domain;
	req.msg.data = msg_data;
	req.msg.data_len = 4;
	rsp = intf->sendrecv(intf, &req);
	/* check for special case error of no policy is limiting */
	if (rsp && (rsp->ccode == 0xA1))
		return 0x80;
	else if (chk_nm_rsp(rsp))
		return -1;
	return rsp->data[0];
}

static int
_ipmi_nm_control(struct ipmi_intf * intf, uint8_t scope, uint8_t domain, uint8_t policy_id)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[6]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = scope;
	msg_data[4] = domain;
	msg_data[5] = policy_id;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_POLICY_CTL;
	req.msg.data = msg_data;
	req.msg.data_len = 6;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

/* Get NM statistics
 *
 * This function returns the statistics
 *
 * returns success/failure
 *
 * @intf:   ipmi interface handler
 * @selector: Parameter selector
 */
static int
_ipmi_nm_statistics(struct ipmi_intf * intf, uint8_t mode, uint8_t domain, uint8_t policy_id, struct nm_statistics *caps)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[6]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = mode;
	msg_data[4] = domain;
	msg_data[5] = policy_id;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_STATS;
	req.msg.data = msg_data;
	req.msg.data_len = 6;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	memcpy(caps, rsp->data, sizeof (struct nm_statistics));
	return 0;
}

static int
_ipmi_nm_reset_stats(struct ipmi_intf * intf, uint8_t mode, uint8_t domain, uint8_t policy_id)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[6]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = mode;
	msg_data[4] = domain;
	msg_data[5] = policy_id;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_RESET_STATS;
	req.msg.data = msg_data;
	req.msg.data_len = 6;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

static int
_nm_set_range(struct ipmi_intf * intf, uint8_t domain, uint16_t minimum, uint16_t maximum)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[8]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = domain;
	msg_data[4] = minimum & 0xFF;
	msg_data[5] = minimum >> 8;
	msg_data[6] = maximum & 0xFF;
	msg_data[7] = maximum >> 8;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_SET_POWER;
	req.msg.data = msg_data;
	req.msg.data_len = 8;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

static int
_ipmi_nm_get_alert(struct ipmi_intf * intf, struct nm_set_alert *alert)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[3]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_ALERT_DS;
	req.msg.data = msg_data;
	req.msg.data_len = 3;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	memcpy(alert, rsp->data, sizeof (struct nm_set_alert));
	return 0;
}

static int
_ipmi_nm_set_alert(struct ipmi_intf * intf, struct nm_set_alert *alert)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[6]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = alert->chan;
	msg_data[4] = alert->dest;
	msg_data[5] = alert->string;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_SET_ALERT_DS;
	req.msg.data = msg_data;
	req.msg.data_len = 6;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

/*
 *
 * get alert threshold values.
 *
 * the list pointer is assumed to point to an array of 16 short integers.
 * This array is filled in for valid thresholds returned.
 */
static int
_ipmi_nm_get_thresh(struct ipmi_intf * intf, uint8_t domain, uint8_t policy_id, uint16_t *list)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[5]; /* 'raw' data to be sent to the BMC */

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = domain;
	msg_data[4] = policy_id;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_ALERT_TH;
	req.msg.data = msg_data;
	req.msg.data_len = 5;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	if (rsp->data[3] > 0)
		*list++ = (rsp->data[5] << 8) | rsp->data[4];
	if (rsp->data[3] > 1)
		*list++ = (rsp->data[7] << 8) | rsp->data[6];
	if (rsp->data[3] > 2)
		*list   = (rsp->data[9] << 8) | rsp->data[8];
	return 0;
}

static int
_ipmi_nm_set_thresh(struct ipmi_intf * intf, struct nm_thresh * thresh)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[IPMI_NM_SET_THRESH_LEN]; /* 'raw' data to be sent to the BMC */

	memset(&msg_data, 0, sizeof(msg_data));
	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = thresh->domain;
	msg_data[4] = thresh->policy_id;
	msg_data[5] = thresh->count;
	if (thresh->count > 0) {
		msg_data[7] = thresh->thresholds[0] >> 8;
		msg_data[6] = thresh->thresholds[0] & 0xFF;
	}
	if (thresh->count > 1) {
		msg_data[9] = thresh->thresholds[1] >> 8;
		msg_data[8] = thresh->thresholds[1] & 0xFF;
	}
	if (thresh->count > 2) {
		msg_data[11] = thresh->thresholds[2] >> 8;
		msg_data[10] = thresh->thresholds[2] & 0xFF;
	}
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_SET_ALERT_TH;
	req.msg.data = msg_data;
	req.msg.data_len = 6 + (thresh->count * 2);
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

/*
 *
 * get suspend periods
 *
 */
static int
_ipmi_nm_get_suspend(struct ipmi_intf * intf, uint8_t domain, uint8_t policy_id, int *count, struct nm_period *periods)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[5]; /* 'raw' data to be sent to the BMC */
	int i;

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = domain;
	msg_data[4] = policy_id;
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_GET_SUSPEND;
	req.msg.data = msg_data;
	req.msg.data_len = 5;
	rsp = intf->sendrecv(intf, &req);
	if (chk_nm_rsp(rsp)) {
		return -1;
	}
	*count = rsp->data[3];
	for (i = 0; i < rsp->data[3]; i += 3, periods++) {
		periods->start = rsp->data[4+i];
		periods->stop  = rsp->data[5+i];
		periods->repeat = rsp->data[6+i];
	}
	return 0;
}

static int
_ipmi_nm_set_suspend(struct ipmi_intf * intf, struct nm_suspend *suspend)
{
	struct ipmi_rq req; /* request data to send to the BMC */
	struct ipmi_rs *rsp;
	uint8_t msg_data[21]; /* 6 control bytes + 5 suspend periods, 3 bytes per period */
	struct nm_period *periods;
	int i;

	msg_data[0] = 0x57;
	msg_data[1] = 1;
	msg_data[2] = 0;
	msg_data[3] = suspend->domain;
        msg_data[4] = suspend->policy_id;
	msg_data[5] = suspend->count;
	for (i = 0, periods = &suspend->period[0]; i < (suspend->count*3); i += 3, periods++) {
		msg_data[6+i] = periods->start;
		msg_data[7+i] = periods->stop;
		msg_data[8+i] = periods->repeat;
	}
	memset(&req, 0, sizeof(req));
	req.msg.data_len = 6 + (suspend->count*3);
	req.msg.netfn = IPMI_NETFN_OEM;
	req.msg.cmd = IPMI_NM_SET_SUSPEND;
	req.msg.data = msg_data;
        rsp = intf->sendrecv(intf, &req);
        if (chk_nm_rsp(rsp)) {
		return -1;
	}
	return 0;
}

static int
ipmi_nm_getcapabilities(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t option;
	uint8_t domain = 0;  /* default domain of platform */
	uint8_t trigger = 0; /* default power policy (no trigger) */
	struct nm_capability caps;

        while (--argc > 0) {
		argv++;
		if (argv[0] == NULL) break;
		if ((option = str2val2(argv[0], nm_capability_opts)) == 0xFF) {
			print_strs(nm_capability_opts, "Capability commands", LOG_ERR, 0);
			return -1;
		}
		switch (option) {
		case 0x01:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			break;
		case 0x02:  /* Inlet */
			trigger = 1;
			break;
		case 0x03:  /* Missing power reading */
			trigger = 2;
			break;
		case 0x04:  /* Time after host reset */
			trigger = 3;
			break;
		case 0x05:  /* Boot time policy */
			trigger = 4;
			break;
		default:
			break;
		}
		argc--;
		argv++;
	}
	trigger |= 0x10;
	memset(&caps, 0, sizeof(caps));
	if (_ipmi_nm_getcapabilities(intf, domain, trigger, &caps))
		return -1;
	if (csv_output) {
		printf("%d,%u,%u,%u,%u,%u,%u,%s\n",
			caps.max_settings, caps.max_value,caps.min_value,
			caps.min_corr/1000, caps.max_corr/1000,
			caps.min_stats, caps.max_stats,
			val2str2(caps.scope&0xF, nm_domain_vals));
		return 0;
	}
	printf("    power policies:\t\t%d\n", caps.max_settings);
	switch (trigger&0xF) {
	case 0:  /* power */
		printf("    max_power\t\t%7u Watts\n    min_power\t\t%7u Watts\n",
				caps.max_value, caps.min_value);
		break;
	case 1:  /* Inlet */
		printf("    max_temp\t\t%7u C\n    min_temp\t\t%7u C\n",
				caps.max_value, caps.min_value);
		break;
	case 2: /* Missing reading time */
	case 3: /* Time after host reset */
		printf("    max_time\t\t%7u Secs\n    min_time\t\t%7u Secs\n",
				caps.max_value/10, caps.min_value/10);
		break;
	case 4: /* boot time policy does not use these values */
	default:
		break;
	}
	printf("    min_corr\t\t%7u secs\n    max_corr\t\t%7u secs\n",
				caps.min_corr/1000, caps.max_corr/1000);
	printf("    min_stats\t\t%7u secs\n    max_stats\t\t%7u secs\n",
				caps.min_stats, caps.max_stats);
	printf("    domain scope:\t%s\n", val2str2(caps.scope&0xF, nm_domain_vals));
	return 0;
}

static int
ipmi_nm_get_policy(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t option;
	uint8_t domain = 0; /* default domain of platform */
	uint8_t policy_id = -1;
	struct nm_get_policy policy;

	memset(&policy, 0, sizeof(policy));

	while (--argc) {
		argv++;
		if (argv[0] == NULL) break;
		if ((option = str2val2(argv[0], nm_policy_options)) == 0xFF) {
			print_strs(nm_policy_options, "Get Policy commands", LOG_ERR, 0);
			return -1;
		}
		switch (option) {
		case 0x03:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			policy.domain |= domain & 0xF;
			break;
		case 0x0B:   /* policy id */
			if (str2uchar(argv[1], &policy_id) < 0) {
				lprintf(LOG_ERR,"    Policy ID must be a positive integer 0-7.\n");
				return -1;
			}
			break;
		default:
			printf("    Unknown command 0x%x, skipping.\n", option);
			break;
		}
		argc--;
		argv++;
	}
        if (policy_id == 0xFF) {
		print_strs(nm_stats_opts, "Missing policy_id parameter:", LOG_ERR, 0);
                return -1;
        }
	if (_ipmi_nm_get_policy(intf, policy.domain, policy_id, &policy))
		return -1;
	if (csv_output) {
		printf("%s,0x%x,%s,%s,%s,%u,%u,%u,%u,%s\n",
			val2str2(policy.domain&0xF, nm_domain_vals),
			policy.domain,
			(policy.policy_type & 0x10) ? "power" : "nopower ",
			val2str2(policy.policy_type & 0xF, nm_policy_type_vals),
			val2str2(policy.policy_exception, nm_exception),
			policy.policy_limits,
			policy.corr_time,
			policy.trigger_limit,
			policy.stats_period,
			policy.policy_type & 0x80 ? "volatile" : "non-volatile");
		return 0;
	}
	printf("    Power domain:                             %s\n",
		val2str2(policy.domain&0xF, nm_domain_vals));
	printf("    Policy is %s %s%s%s\n",
		policy.domain&0x10 ? "enabled" : "not enabled",
		policy.domain&0x20 ? "per Domain " : "",
		policy.domain&0x40 ? "Globally " : "",
		policy.domain&0x80 ? "via DCMI api " : "");
	printf("    Policy is %sa power control type.\n", (policy.policy_type & 0x10) ? "" : "not ");
	printf("    Policy Trigger Type:                      %s\n",
		val2str2(policy.policy_type & 0xF, nm_policy_type_vals));
	printf("    Correction Aggressiveness:                %s\n",
		val2str2((policy.policy_type>> 5) & 0x3, nm_correction_vals));
	printf("    Policy Exception Actions:                 %s\n",
		val2str2(policy.policy_exception, nm_exception));
	printf("    Power Limit:                              %u Watts\n",
		policy.policy_limits);
	printf("    Correction Time Limit:                    %u milliseconds\n",
		policy.corr_time);
	printf("    Trigger Limit:                            %u units\n",
		policy.trigger_limit);
	printf("    Statistics Reporting Period:              %u seconds\n",
		policy.stats_period);
	printf("    Policy retention:                         %s\n",
		policy.policy_type & 0x80 ? "volatile" : "non-volatile");
	if ( (policy_id == 0) && ((policy.domain & 0xf) == 0x3) )
		printf("    HW Prot Power domain:                     %s\n",
			policy.policy_type & 0x80 ? "Secondary" : "Primary");
	return 0;
}

static int
ipmi_nm_policy(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t action;
	uint8_t option;
	uint8_t correction;
	uint8_t domain = 0; /* default domain of platform */
	uint8_t policy_id = -1;
	uint16_t power, period, inlet;
	uint16_t cores;
	uint32_t limit;
	struct nm_policy policy;

	argv++;
	argc--;
	if ((argv[0] == NULL) ||
			((action = str2val2(argv[0], nm_policy_action)) == 0xFF)) {
		print_strs(nm_policy_action, "Policy commands", LOG_ERR, 0);
		return -1;
	}
	if (action == 0) /* get */
		return (ipmi_nm_get_policy(intf, argc, argv));
	memset(&policy, 0, sizeof(policy));
	/*
	 * nm policy add [domain <param>] enable|disable  policy_id <param> correction <opt> power <watts> limit <param> period <param>
	 * nm policy remove [domain <param>]  policy_id <param>
	 * nm policy limiting {domain <param>]
	 */
	while (--argc > 0) {
		argv++;
		if (argv[0] == NULL) break;
		if ((option = str2val2(argv[0], nm_policy_options)) == 0xFF) {
			print_strs(nm_policy_options, "Policy options", LOG_ERR, 0);
			return -1;
		}
		switch (option) {
		case 0x01:   /* policy enable */
			policy.domain |= IPMI_NM_POLICY_ENABLE;
			break;
		case 0x02:   /* policy disable */
			break;   /* value is initialized to zero already */
		case 0x03:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			policy.domain |= domain & 0xF;
			break;
		case 0x04:   /* inlet */
			if (str2ushort(argv[1], &inlet) < 0) {
				printf("Inlet Temp value must be 20-45.\n");
				return -1;
			}
			policy.policy_type |= 1;
			policy.policy_limits = 0;
			policy.trigger_limit = inlet;
			break;
		case 0x06:   /* get correction action */
			if (action == 0x5) break;   /* skip if this is a remove */
			if ((correction = str2val2(argv[1], nm_correction)) == 0xFF) {
				print_strs(nm_correction, "Correction Actions", LOG_ERR, 0);
				return -1;
			}
			policy.policy_type |= (correction << 5);
			break;
		case 0x07:   /* not implemented */
			break;
		case 0x08:   /* power */
			if (str2ushort(argv[1], &power) < 0) {
				printf("Power limit value must be 0-500.\n");
				return -1;
			}
			policy.policy_limits = power;
			break;
		case 0x09:   /* trigger limit */
			if (str2uint(argv[1], &limit) < 0) {
				printf("Trigger Limit value must be positive integer.\n");
				return -1;
			}
			policy.corr_time = limit;
			break;
		case 0x0A:   /* statistics period */
			if (str2ushort(argv[1], &period) < 0) {
				printf("Statistics Reporting Period must be positive integer.\n");
				return -1;
			}
			policy.stats_period = period;
			break;
		case 0x0B:   /* policy ID */
			if (str2uchar(argv[1], &policy_id) < 0) {
				printf("Policy ID must be a positive integer 0-7.\n");
				return -1;
			}
			policy.policy_id = policy_id;
			break;
		case 0x0C:   /* volatile */
			policy.policy_type |= 0x80;
			break;
		case 0x0D:   /* cores_off, number of cores to disable at boot time */
			policy.policy_type |= 4;
			if (str2ushort(argv[1], &cores) < 0) {
				printf("number of cores disabled must be 1-127.\n");
				return -1;
			}
			if ((cores < 1) || (cores > 127)) {
				printf("number of cores disabled must be 1-127.\n");
				return -1;
			}
			policy.policy_type |= 4;
			policy.policy_limits = cores << 1;
			break;
		default:
			break;
		}
		argc--;
		argv++;
	}
	if (action == 0x06) { /* limiting */
		if ((limit = _ipmi_nm_policy_limiting(intf, domain) == -1))
			return -1;
		printf("limit %x\n", limit);
		return 0;
	}
	if (policy_id == 0xFF) {
		print_strs(nm_stats_opts, "Missing policy_id parameter:", LOG_ERR, 0);
		return -1;
	}
	if (action == 0x04)   /* add */
		policy.policy_type |= 0x10;
	if (_ipmi_nm_set_policy(intf, &policy))
		return -1;
	return 0;
}
/* end policy */

static int
ipmi_nm_control(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t action;
	uint8_t scope = 0;   /* default control scope of global */
	uint8_t domain = 0;  /* default domain of platform */
	uint8_t policy_id = -1;

	argv++;
	argc--;
	/* nm_ctl_cmds returns 0 for disable, 1 for enable */
	if ((argv[0] == NULL) ||
			((action = str2val2(argv[0], nm_ctl_cmds)) == 0xFF)) {
		print_strs(nm_ctl_cmds, "Control parameters:", LOG_ERR, 0);
		print_strs(nm_ctl_domain, "control Scope (required):", LOG_ERR, 0);
		return -1;
	}
	argv++;
	while (--argc) {
		/* nm_ctl_domain returns correct bit field except for action */
		if ((argv[0] == NULL) ||
				((scope = str2val2(argv[0], nm_ctl_domain)) == 0xFF)) {
			print_strs(nm_ctl_domain, "Control Scope (required):", LOG_ERR, 0);
			return -1;
		}
		argv++;
		if (argv[0] == NULL) break;
		if (scope == 0x02) { /* domain */
			if ((domain = str2val2(argv[0], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
		} else if (scope == 0x04) { /* per_policy */

			if (str2uchar(argv[0], &policy_id) < 0) {
				lprintf(LOG_ERR,"Policy ID must be a positive integer.\n");
				return -1;
			}
			break;
		}
		argc--;
		argv++;
	}
        if ((scope == 0x04) && (policy_id == 0xFF)) {
		print_strs(nm_stats_opts, "Missing policy_id parameter:", LOG_ERR, 0);
		return -1;
	}
	if (_ipmi_nm_control(intf, scope|(action&1), domain, policy_id) < 0 )
		return -1;
	return 0;
}

static int
ipmi_nm_get_statistics(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t mode = 0;
	uint8_t option;
	uint8_t domain = 0;   /* default domain of platform */
	uint8_t policy_id = -1;
	int     policy_mode = 0;
	int     cut;
	char   *units = "";
	char    datebuf[27];
	struct nm_statistics stats;
	struct tm tm_t;
	time_t t;

	argv++;
	if ((argv[0] == NULL) ||
			((mode = str2val2(argv[0], nm_stats_mode)) == 0xFF)) {
		print_strs(nm_stats_mode, "Statistics commands", LOG_ERR, 0);
		return -1;
	}
	while (--argc) {
		argv++;
		if (argv[0] == NULL) break;
		if ((option = str2val2(argv[0], nm_stats_opts)) == 0xFF) {
				print_strs(nm_stats_opts, "Control Scope options", LOG_ERR, 0);
			return -1;
		}
		switch (option) {
		case 0x01:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			break;
		case 0x02:   /* policy ID */
			if (str2uchar(argv[1], &policy_id) < 0) {
				lprintf(LOG_ERR,"Policy ID must be a positive integer.\n");
				return -1;
			}
			break;
		default:
			break;
		}
		argc--;
		argv++;
	}

	switch (mode) {
	case 0x01:
		units = "Watts";
		break;
	case 0x02:
		units = "Celsius";
		break;
	case 0x03:
		units = "%";
		break;
	case 0x11:
	case 0x12:
	case 0x13:
		policy_mode = 1;
		units = (mode == 0x11) ? "Watts" : (mode == 0x12) ? "Celsius" : " %";
		if (policy_id == 0xFF) {
			print_strs(nm_stats_opts, "Missing policy_id parameter:", LOG_ERR, 0);
			return -1;
		}
		break;
	default:
		break;
	}
	if (_ipmi_nm_statistics(intf, mode, domain, policy_id, &stats))
		return -1;
	t = stats.time_stamp;
	gmtime_r(&t, &tm_t);
	sprintf(datebuf, "%s", asctime(&tm_t));
	cut = strlen(datebuf) -1;
	datebuf[cut] = 0;
	if (csv_output) {
		printf("%s,%s,%s,%s,%s,%d,%d,%d,%d,%s,%d\n",
			val2str2(stats.id_state & 0xF, nm_domain_vals),
			((stats.id_state >> 4) & 1) ? (policy_mode ? "Policy Enabled" : "Globally Enabled") : "Disabled" ,
			((stats.id_state >> 5) & 1) ? "active" : "suspended",
			((stats.id_state >> 6) & 1) ? "in progress" : "suspended",
			((stats.id_state >> 7) & 1) ? "triggered" : "not triggered",
			stats.curr_value,
			stats.min_value,
			stats.max_value,
			stats.ave_value,
			datebuf,
			stats.stat_period);
		return 0;
	}
	printf("    Power domain:                             %s\n",
		val2str2(stats.id_state & 0xF, nm_domain_vals));
	printf("    Policy/Global Admin state                 %s\n",
		((stats.id_state >> 4) & 1) ? (policy_mode ? "Policy Enabled" : "Globally Enabled") : "Disabled" );
	printf("    Policy/Global Operational state           %s\n",
		((stats.id_state >> 5) & 1) ? "active" : "suspended");
	printf("    Policy/Global Measurement state           %s\n",
		((stats.id_state >> 6) & 1) ? "in progress" : "suspended");
	printf("    Policy Activation state                   %s\n",
		((stats.id_state >> 7) & 1) ? "triggered" : "not triggered");
	printf("    Instantaneous reading:                    %8d %s\n",
		stats.curr_value, units);
	printf("    Minimum during sampling period:           %8d %s\n",
		stats.min_value, units);
	printf("    Maximum during sampling period:           %8d %s\n",
		stats.max_value, units);
	printf("    Average reading over sample period:       %8d %s\n",
		stats.ave_value, units);
	printf("    IPMI timestamp:                           %s\n",
		datebuf);
	printf("    Sampling period:                          %08d Seconds.\n", stats.stat_period);
	printf("\n");
	return 0;
}

static int
ipmi_nm_reset_statistics(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t mode;
	uint8_t option;
	uint8_t domain = 0;   /* default domain of platform */
	uint8_t policy_id = -1;

	argv++;
	if ((argv[0] == NULL) ||
			((mode = str2val2(argv[0], nm_reset_mode)) == 0xFF)) {
		print_strs(nm_reset_mode, "Reset Statistics Modes:", LOG_ERR, 0);
		return -1;
	}
	while (--argc) {
		argv++;
		if (argv[0] == NULL) break;
		if ((option = str2val2(argv[0], nm_stats_opts)) == 0xFF) {
			print_strs(nm_stats_opts, "Reset Scope options", LOG_ERR, 0);
			return -1;
		}
		switch (option) {
		case 0x01:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			break;
		case 0x02:   /* policy ID */
			if (str2uchar(argv[1], &policy_id) < 0) {
				lprintf(LOG_ERR,"Policy ID must be a positive integer.\n");
				return -1;
			}
			break;
		default:
			break;
		}
		argc--;
		argv++;
	}
	if (mode && (policy_id == 0xFF)) {
		print_strs(nm_stats_opts, "Missing policy_id parameter:", LOG_ERR, 0);
		return -1;
	}
	if (_ipmi_nm_reset_stats(intf, mode, domain, policy_id) < 0)
		return -1;
	return 0;
}

static int
ipmi_nm_set_range(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t domain = 0;
	uint8_t param;
	uint16_t minimum = -1;
	uint16_t maximum = -1;

	while (--argc) {
		argv++;
		if (argv[0] == NULL) break;
		if ((param = str2val2(argv[0], nm_power_range)) == 0xFF) {
			print_strs(nm_power_range, "power range parameters:", LOG_ERR, 0);
			return -1;
		}
		switch (param) {
		case 0x01:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			break;
		case 0x02: /* min */
			if (str2ushort(argv[1], &minimum) < 0) {
				lprintf(LOG_ERR,"Power minimum must be a positive integer.\n");
				return -1;
			}
			break;
		case 0x03: /* max */
			if (str2ushort(argv[1], &maximum) < 0) {
				lprintf(LOG_ERR,"Power maximum must be a positive integer.\n");
				return -1;
			}
			break;
		default:
			break;
		}
		argc--;
		argv++;
	}
	if ((minimum == 0xFFFF) || (maximum == 0xFFFF)) {
		lprintf(LOG_ERR,"Missing parameters: nm power range min <minimum> max <maximum>.\n");
		return -1;
	}
	if (_nm_set_range(intf, domain, minimum, maximum) < 0)
		return -1;
	return 0;
}

static int
ipmi_nm_get_alert(struct ipmi_intf * intf)
{
	struct nm_set_alert alert;

	memset(&alert, 0, sizeof(alert));
	if (_ipmi_nm_get_alert(intf, &alert))
		return -1;
	if (csv_output) {
		printf("%d,%s,0x%x,%s,0x%x\n",
				alert.chan&0xF,
				(alert.chan >> 7) ? "not registered" : "registered",
				alert.dest,
				(alert.string >> 7) ? "yes" : "no",
				alert.string & 0x7F);
		return 0;
	}
	printf("    Alert Chan:                                  %d\n",
		alert.chan&0xF);
	printf("    Alert Receiver:                              %s\n",
		(alert.chan >> 7) ? "not registered" : "registered");
	printf("    Alert Lan Destination:                       0x%x\n",
		alert.dest);
	printf("    Use Alert String:                            %s\n",
		(alert.string >> 7) ? "yes" : "no");
	printf("    Alert String Selector:                       0x%x\n",
		alert.string & 0x7F);
	return 0;
}

static int
ipmi_nm_alert(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t param;
	uint8_t action;
	uint8_t chan = -1;
	uint8_t dest = -1;
	uint8_t string = -1;
	struct nm_set_alert alert;

	argv++;
	argc--;
	if ((argv[0] == NULL) ||
			((action = str2val2(argv[0], nm_alert_opts)) == 0xFF)) {
		print_strs(nm_alert_opts, "Alert commands", LOG_ERR, 0);
		return -1;
	}
	if (action == 0x02) /* get */
		return (ipmi_nm_get_alert(intf));
	/* set */
	memset(&alert, 0, sizeof(alert));
	while (--argc) {
		argv++;
		if (argv[0] == NULL) break;
		if ((param = str2val2(argv[0], nm_set_alert_param)) == 0xFF) {
			print_strs(nm_set_alert_param, "Set alert Parameters:", LOG_ERR, 0);
			return -1;
		}
		switch (param) {
		case 0x01: /* channnel */
			if (str2uchar(argv[1], &chan) < 0) {
				lprintf(LOG_ERR,"Alert Lan chan must be a positive integer.\n");
				return -1;
			}
			if (action == 0x03)  /* Clear */
				chan |= 0x80;   /* deactivate alert reciever */
			break;
		case 0x02:  /* dest */
			if (str2uchar(argv[1], &dest) < 0) {
				lprintf(LOG_ERR,"Alert Destination must be a positive integer.\n");
				return -1;
			}
			break;
		case 0x03:  /* string number */
			if (str2uchar(argv[1], &string) < 0) {
				lprintf(LOG_ERR,"Alert String # must be a positive integer.\n");
				return -1;
			}
			string |= 0x80;   /* set string select flag */
			break;
		}
		argc--;
		argv++;
	}
	if ((chan == 0xFF) || (dest == 0xFF)) {
		print_strs(nm_set_alert_param, "Must set alert chan and dest params.", LOG_ERR, 0);
		return -1;
	}
	if (string == 0xFF) string = 0;
	alert.chan = chan;
	alert.dest = dest;
	alert.string = string;
	if (_ipmi_nm_set_alert(intf, &alert))
		return -1;
	return 0;
}

static int
ipmi_nm_get_thresh(struct ipmi_intf *intf, uint8_t domain, uint8_t policy_id)
{
	uint16_t list[3];

	memset(list, 0, sizeof(list));
	if (_ipmi_nm_get_thresh(intf, domain, policy_id, &list[0]))
		return -1;

	printf("    Alert Threshold domain:                   %s\n",
			val2str2(domain, nm_domain_vals));
	printf("    Alert Threshold Policy ID:                %d\n",
			policy_id);
	printf("    Alert Threshold 1:                        %d\n",
			list[0]);
	printf("    Alert Threshold 2:                        %d\n",
			list[1]);
	printf("    Alert Threshold 3:                        %d\n",
			list[2]);
	return 0;
}

static int
ipmi_nm_thresh(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t option;
	uint8_t action;
	uint8_t domain = 0;   /* default domain of platform */
	uint8_t policy_id = -1;
	struct nm_thresh thresh;
	int i = 0;

	argv++;
	argc--;
	/* set or get */
	if ((argv[0] == NULL) || (argc < 3) ||
			((action = str2val2(argv[0], nm_thresh_cmds)) == 0xFF)) {
		print_strs(nm_thresh_cmds, "Theshold commands", LOG_ERR, 0);
		return -1;
	}
	memset(&thresh, 0, sizeof(thresh));
	while (--argc) {
		argv++;
		if (argv[0] == NULL) break;
		option = str2val2(argv[0], nm_thresh_param);
		switch (option) {
		case 0x01:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			argc--;
			argv++;
			break;
		case 0x02:   /* policy ID */
			if (str2uchar(argv[1], &policy_id) < 0) {
				lprintf(LOG_ERR,"Policy ID must be a positive integer.\n");
				return -1;
			}
			argc--;
			argv++;
			break;
		case 0xFF:
			if (i > 2) {
				lprintf(LOG_ERR,"Set Threshold requires 1, 2, or 3 threshold integer values.\n");
				return -1;
			}
			if (str2ushort(argv[0], &thresh.thresholds[i++]) < 0) {
				lprintf(LOG_ERR,"threshold value %d count must be a positive integer.\n", i);
				return -1;
			}
		default:
			break;
		}
	}
	if (policy_id == 0xFF) {
		print_strs(nm_stats_opts, "Missing policy_id parameter:", LOG_ERR, 0);
		return -1;
	}
	if (action == 0x02) /* get */
		return (ipmi_nm_get_thresh(intf, domain, policy_id));
	thresh.domain = domain;
	thresh.policy_id = policy_id;
	thresh.count = i;
	if (_ipmi_nm_set_thresh(intf, &thresh) < 0)
		return -1;
	return 0;
}

static inline int
click2hour(int click)
{
	if ((click*6) < 60) return 0;
	return ((click*6)/60);
}

static inline int
click2min(int click)
{
	if (!click) return 0;
	if ((click*6) < 60) return click*6;
	return (click*6)%60;
}

static int
ipmi_nm_get_suspend(struct ipmi_intf *intf, uint8_t domain, uint8_t policy_id)
{
	struct nm_period periods[5];
	int i;
	int j;
	int count = 0;
	const char *days[7] = {"M", "Tu", "W", "Th", "F", "Sa", "Su"};

	memset(periods, 0, sizeof(periods));
	if (_ipmi_nm_get_suspend(intf, domain, policy_id, &count, &periods[0]))
		return -1;

	printf("    Suspend Policy domain:                    %s\n",
			val2str2(domain, nm_domain_vals));
	printf("    Suspend Policy Policy ID:                 %d\n",
			policy_id);
	if (!count) {
		printf("    No suspend Periods.\n");
		return 0;
	}
	for (i = 0; i < count; i++) {
		printf("    Suspend Period %d:                         %02d:%02d to %02d:%02d",
			i, click2hour(periods[i].start), click2min(periods[i].start),
			click2hour(periods[i].stop), click2min(periods[i].stop));
		if (periods[i].repeat) printf(", ");
		for (j = 0; j < 7; j++)
			printf("%s",  (periods[i].repeat >> j)&1 ? days[j] : "");
		printf("\n");
	}
	return 0;
}

static int
ipmi_nm_suspend(struct ipmi_intf * intf, int argc, char **argv)
{
	uint8_t option;
	uint8_t action;
	uint8_t domain = 0;   /* default domain of platform */
	uint8_t policy_id = -1;
	uint8_t count = 0;
	struct nm_suspend suspend;
	int i;

	argv++;
	argc--;
	/* set or get */
	if ((argv[0] == NULL) || (argc < 3) ||
			((action = str2val2(argv[0], nm_suspend_cmds)) == 0xFF)) {
		print_strs(nm_suspend_cmds, "Suspend commands", LOG_ERR, 0);
		return -1;
	}
	memset(&suspend, 0, sizeof(suspend));
	while (--argc > 0) {
		argv++;
		if (argv[0] == NULL) break;
		option = str2val2(argv[0], nm_thresh_param);
		switch (option) {
		case 0x01:   /* get domain scope */
			if ((domain = str2val2(argv[1], nm_domain_vals)) == 0xFF) {
				print_strs(nm_domain_vals, "Domain Scope:", LOG_ERR, 0);
				return -1;
			}
			argc--;
			argv++;
			break;
		case 0x02:   /* policy ID */
			if (str2uchar(argv[1], &policy_id) < 0) {
				lprintf(LOG_ERR,"Policy ID must be a positive integer.\n");
				return -1;
			}
			argc--;
			argv++;
			break;
                case 0xFF:   /* process periods */
			for (i = 0; count < IPMI_NM_SUSPEND_PERIOD_MAX; i += 3, count++) {
				if (argc < 3) {
					lprintf(LOG_ERR,"Error: suspend period requires a start, stop, and repeat values.\n");
					return -1;
				}
				if (str2uchar(argv[i+0], &suspend.period[count].start) < 0) {
					lprintf(LOG_ERR,"suspend start value %d must be 0-239.\n", count);
					return -1;
				}
				if (str2uchar(argv[i+1], &suspend.period[count].stop) < 0) {
					lprintf(LOG_ERR,"suspend stop value %d  must be 0-239.\n", count);
					return -1;
				}
				if (str2uchar(argv[i+2], &suspend.period[count].repeat) < 0) {
					lprintf(LOG_ERR,"suspend repeat value %d unable to convert.\n", count);
					return -1;
				}
				argc -= 3;
				if (argc <= 0)
					break;
			}
			if (argc <= 0)
				break;
			break;
                default:
                        break;
                }
        }
	if (action == 0x02) /* get */
		return (ipmi_nm_get_suspend(intf, domain, policy_id));

	suspend.domain = domain;
	suspend.policy_id = policy_id;
	if (_ipmi_nm_set_suspend(intf, &suspend) < 0)
		return -1;
	return 0;
}
/* end nm */

static int
ipmi_dcmi_set_limit(struct ipmi_intf * intf, int argc, char **argv)
{
	int rc = 0;

	if ( argc == 10) {
		/* Let`s initialize dcmi power parameters */
		struct ipmi_rq req;
		uint8_t data[256];
		uint16_t sample = 0;
		uint16_t limit = 0;
		uint32_t correction = 0;
		struct ipmi_rs *rsp;

		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 0x00:
			/* no_action */
			data[4] = 0x00;
			break;
		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 */
			lprintf(LOG_ERR, "Given Action '%s' is invalid.",
				argv[2]);
			return -1;
		}
		/* limit */
		if (str2ushort(argv[4], &limit) != 0) {
			lprintf(LOG_ERR,
				"Given Limit '%s' is invalid.",
				argv[4]);
			return (-1);
		}
		data[5] = limit >> 0;
		data[6] = limit >> 8;
		/* correction */
		if (str2uint(argv[6], &correction) != 0) {
			lprintf(LOG_ERR,
				"Given Correction '%s' is invalid.",
				argv[6]);
			return (-1);
		}
		data[7] = correction >> 0;
		data[8] = correction >> 8;
		data[9] = correction >> 16;
		data[10] = correction >> 24;
		data[11] = 0x00;  /* reserved */
		data[12] = 0x00;  /* reserved */
		/* sample */
		if (str2ushort(argv[8], &sample) != 0) {
			lprintf(LOG_ERR,
				"Given Sample '%s' is invalid.",
				argv[8]);
			return (-1);
		}
		data[13] = sample >> 0;
		data[14] = sample >> 8;

		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>", LOG_ERR, 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;
		}
	}
	return rc;
}

static int
ipmi_dcmi_parse_power(struct ipmi_intf * intf, int argc, char **argv)
{
	int rc = 0;
	uint8_t sample_time = 0;
	/* power management */
	switch (str2val2(argv[0], dcmi_pwrmgmt_vals)) {
	case 0x00:
	/* get reading */
		if (argv[1] != NULL) {
			if (!(sample_time = str2val2(argv[1], dcmi_sampling_vals))) {
				print_strs(dcmi_sampling_vals,
					"Invalid sample time. Valid times are: ",
					LOG_ERR, 1);
				printf("\n");
				return -1;
			}
		}
		rc = ipmi_dcmi_pwr_rd(intf, sample_time);
		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>",
				LOG_ERR, 0);
			return -1;
		}
		if (ipmi_dcmi_set_limit(intf, argc, argv) < 0)
			return -1;
		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>", LOG_ERR, 0);
		break;
	}
	return rc;
}
/* end dcmi power command */

static int
ipmi_dcmi_thermalpolicy(struct ipmi_intf * intf, int argc, char **argv)
{
	int rc = 0;
	uint8_t entityID = 0;
	uint8_t entityInst = 0;
	uint8_t persistanceFlag;
	uint8_t actionHardPowerOff;
	uint8_t actionLogToSEL;
	uint8_t tempLimit = 0;
	uint8_t samplingTimeLSB;
	uint8_t samplingTimeMSB;
	uint16_t samplingTime = 0;
	/* Thermal policy get/set */
	/* dcmitool dcmi thermalpolicy get */
	switch (str2val2(argv[1], dcmi_thermalpolicy_vals)) {
	case 0x00:
		if (argc < 4) {
			lprintf(LOG_NOTICE, "Get <entityID> <instanceID>");
			return -1;
		}
		if (str2uchar(argv[2], &entityID) != 0) {
			lprintf(LOG_ERR,
					"Given Entity ID '%s' is invalid.",
					argv[2]);
			return (-1);
		}
		if (str2uchar(argv[3], &entityInst) != 0) {
			lprintf(LOG_ERR,
					"Given Instance ID '%s' is invalid.",
					argv[3]);
			return (-1);
		}
		rc = ipmi_dcmi_getthermalpolicy(intf,  entityID, entityInst);
		break;
	case 0x01:
		if (argc < 4) {
			lprintf(LOG_NOTICE, "Set <entityID> <instanceID>");
			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>",
					LOG_ERR, 0);
			return -1;
		}
		if (str2uchar(argv[2], &entityID) != 0) {
			lprintf(LOG_ERR,
					"Given Entity ID '%s' is invalid.",
					argv[2]);
			return (-1);
		}
		if (str2uchar(argv[3], &entityInst) != 0) {
			lprintf(LOG_ERR,
					"Given Instance ID '%s' is invalid.",
					argv[3]);
			return (-1);
		}
		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);
		if (str2uchar(argv[7], &tempLimit) != 0) {
			lprintf(LOG_ERR,
					"Given Temp Limit '%s' is invalid.",
					argv[7]);
			return (-1);
		}
		if (str2ushort(argv[8], &samplingTime) != 0) {
			lprintf(LOG_ERR,
					"Given Sampling Time '%s' is invalid.",
					argv[8]);
			return (-1);
		}
		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>",
				LOG_ERR, 0);
		return -1;
	}
	return rc;
}

/*  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;
	int i;
	struct ipmi_rs *rsp;

	if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) {
		print_strs(dcmi_cmd_vals,
				"Data Center Management Interface commands",
				LOG_ERR, 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) {
				lprintf(LOG_ERR,"Error discovering %s capabilities!\n",
						val2str2(i, dcmi_capable_vals));
				return -1;
			}
		}
		break;
	case 0x01:
		/* power */
		argv++;
		if (argv[0] == NULL) {
			print_strs(dcmi_pwrmgmt_vals, "power <command>",
					LOG_ERR, 0);
			return -1;
		}
		rc = ipmi_dcmi_parse_power(intf, argc, argv);
		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) {
			lprintf(LOG_ERR, "Error getting asset tag!");
			return -1;
		}
		break;
		/* end asset tag */
	case 0x04:
	{
		/* set asset tag */
		if (argc == 1 ) {
			print_strs(dcmi_cmd_vals,
					"Data Center Management Interface commands",
					LOG_ERR, 0);
			return -1;
		}
		if (ipmi_dcmi_prnt_setassettag(intf, (uint8_t *)argv[1]) < 0) {
			lprintf(LOG_ERR, "\nError setting asset tag!");
			return -1;
		}
		break;
	}
	/* end set asset tag */
	case 0x05:
		/* get management controller identifier string */
		if (ipmi_dcmi_prnt_getmngctrlids(intf) < 0) {
			lprintf(LOG_ERR,
					"Error getting management controller identifier string!");
			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",
					LOG_ERR, 0);
			return -1;
		}
		if (ipmi_dcmi_prnt_setmngctrlids(intf, (uint8_t *)argv[1]) < 0) {
			lprintf(LOG_ERR,
					"Error setting management controller identifier string!");
			return -1;
		}
		break;
	}
	/* end set management controller identifier string */
	case 0x07:
		/* get/set thermal policy */
		rc = ipmi_dcmi_thermalpolicy(intf, argc, argv);
		break;
	case 0x08:
		if(ipmi_dcmi_prnt_get_temp_readings(intf) < 0 ) {
			lprintf(LOG_ERR,
					"Error get temperature readings!");
			return -1;
		}
		break;
	case 0x09:
		if(ipmi_dcmi_prnt_getconfparam(intf) < 0 ) {
			lprintf(LOG_ERR,
					"Error Get DCMI Configuration Parameters!");
			return -1;
		};
		break;
	case 0x0A:
	{
		switch (argc) {
		case 2:
			if (strncmp(argv[1], "activate_dhcp", 13) != 0) {
				print_strs( dcmi_conf_param_vals,
						"DCMI Configuration Parameters",
						LOG_ERR, 0);
				return -1;
			}
			break;
		default:
			if (argc != 3 || strncmp(argv[1], "help", 4) == 0) {
				print_strs(dcmi_conf_param_vals,
						"DCMI Configuration Parameters",
						LOG_ERR, 0);
				return -1;
			}
		}
		if (strncmp(argv[1], "activate_dhcp", 13) == 0) {
			rsp = ipmi_dcmi_setconfparam(intf, 1, 1);
		} else {
			uint16_t tmp_val = 0;
			if (str2ushort(argv[2], &tmp_val) != 0) {
				lprintf(LOG_ERR,
						"Given %s '%s' is invalid.",
						argv[1], argv[2]);
				return (-1);
			}
			rsp = ipmi_dcmi_setconfparam(intf,
					str2val2(argv[1], dcmi_conf_param_vals),
					tmp_val);
		}
		if (chk_rsp(rsp)) {
			lprintf(LOG_ERR,
					"Error Set DCMI Configuration Parameters!");
		}
		break;
	}
	case 0x0B:
	{
		if (intf->session == NULL) {
			lprintf(LOG_ERR,
					"\nOOB discovery is available only via RMCP interface.");
			return -1;
		}
		if(ipmi_dcmi_prnt_oobDiscover(intf) < 0) {
			lprintf(LOG_ERR, "\nOOB discovering capabilities failed.");
			return -1;
		}
		break;
	}
	default:
		/* couldn't detect what the user entered */
		print_strs(dcmi_cmd_vals,
				"Data Center Management Interface commands",
				LOG_ERR, 0);
		return -1;
		break;
	}
	printf("\n");
	return rc;
}

/*  Node Manager main
 *
 * @intf:   nm interface handler
 * @argc:   argument count
 * @argv:   argument vector
 */
int
ipmi_nm_main(struct ipmi_intf * intf, int argc, char **argv)
{
	struct nm_discover disc;

	if ((argc == 0) || (strncmp(argv[0], "help", 4) == 0)) {
		print_strs(nm_cmd_vals,
			"Node Manager Interface commands",
			LOG_ERR, 0);
		return -1;
	}

	switch (str2val2(argv[0], nm_cmd_vals)) {
	/* discover */
	case 0x00:
		if (_ipmi_nm_discover(intf, &disc))
			return -1;
		printf("    Node Manager Version %s\n", val2str2(disc.nm_version, nm_version_vals));
		printf("    revision %d.%d%d  patch version %d\n", disc.major_rev,
				disc.minor_rev>>4, disc.minor_rev&0xf, disc.patch_version);
		break;
	/* capability */
	case 0x01:
		if (ipmi_nm_getcapabilities(intf, argc, argv))
			return -1;
		break;
	/*  policy control enable-disable */
	case 0x02:
		if (ipmi_nm_control(intf, argc, argv))
			return -1;
		break;
	/* policy */
	case 0x03:
		if (ipmi_nm_policy(intf, argc, argv))
			return -1;
		break;
	/* Get statistics */
	case 0x04:
		if (ipmi_nm_get_statistics(intf, argc, argv))
			return -1;
		break;
	/* set power draw range */
	case 0x05:
		if (ipmi_nm_set_range(intf, argc, argv))
			return -1;
		break;
	/* set/get suspend periods */
	case 0x06:
		if (ipmi_nm_suspend(intf, argc, argv))
			return -1;
		break;
	/* reset statistics */
	case 0x07:
		if (ipmi_nm_reset_statistics(intf, argc, argv))
			return -1;
		break;
	/* set/get alert destination */
	case 0x08:
		if (ipmi_nm_alert(intf, argc, argv))
			return -1;
		break;
	/* set/get alert thresholds */
	case 0x09:
		if (ipmi_nm_thresh(intf, argc, argv))
			return -1;
		break;
	default:
		print_strs(nm_cmd_vals, "Node Manager Interface commands", LOG_ERR, 0);
		break;
	}
	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;
	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) {
		lprintf(LOG_DEBUG, "header == NULL");
		ipmi_sdr_end(intf, itr);
		return (-1);
	}
	/* 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");
		ipmi_sdr_end(intf, itr);
		return (-1);
	}
	if ((header->type == SDR_RECORD_TYPE_FULL_SENSOR) ||
			(header->type == SDR_RECORD_TYPE_COMPACT_SENSOR)) {
		rc = ipmi_sdr_print_rawentry(intf, header->type,
				rec, header->length);
	} else {
		rc = (-1);
	}
	free(rec);
	rec = NULL;
	ipmi_sdr_end(intf, itr);
	return rc;
}
