Hi Arnaud,

On Tue, 2011-06-28 at 12:19 -0700, Arnaud Quette wrote:
> Hi Al,
> 
> 2011/6/28 Albert Chu <[email protected]>
>         On Tue, 2011-06-28 at 02:28 -0700, Arnaud Quette wrote:
>         (...)
>         > I'm *very* interested in!
>         > this could even serve as a simple example, shipped in the
>         examples/
>         > directory.
>         
>         
>         
>         Unfortunately, it will require knowledge of the IPMI
>         protocol/specification, which makes it difficult (and why I
>         probably
>         never bothered with an example).
>         
>         Everything is in the ipmi-fru and ipmi-sensors tools, however
>         I imagine
>         a lot of the options, permutations of things, IPMI spec
>         details, etc. is
>         what's making it confusing.
>         
>         Give me some time, and I'll try to "whittle" the ipmi-fru and
>         ipmi-sensors tools into a far simpler example that can give
>         you a basis
>         for what you're trying to accomplish.
> 
> sure, thanks a lot for your much appreciated proposition.
> since I have some hard deadlines, do you have any approximate idea on
> when you'd be able to release this?

Attached is a simplified ipmi-fru that you can hopefully use to extract
the FRU information you seek.  "gcc ipmi-fru-example.c -lfreeipmi" is
all you need to do.  I took out a lot of stuff, and there a number of
special cases not handled.  It may not work for all motherboards, but is
a good place to start.

As for sensors, I figured you would probably want to use
libipmimonitoring, since it's at a much higher level.  I would suggest
looking at the ipmimonitoring-sensors.c file.

Hopefully that's enough to get you going.  LMK if you need some help
deciphering the code more.

Al

> 
>         One question, are you looking to monitor the PSU's inband or
>         outofband?
>         That's another way I can whittle down the code.
> 
> inband is the priority, so this should help a bit, as far as I
> understood.
> 
> thanks again,
> Arnaud
> -- 
> Linux / Unix Expert R&D - Eaton - http://powerquality.eaton.com
> Network UPS Tools (NUT) Project Leader -
> http://www.networkupstools.org/
> Debian Developer - http://www.debian.org
> Free Software Developer - http://arnaud.quette.free.fr/
> 
-- 
Albert Chu
[email protected]
Computer Scientist
High Performance Systems Division
Lawrence Livermore National Laboratory
/*****************************************************************************\
 *  $Id: ipmi-fru.c,v 1.59 2010-02-08 22:20:58 chu11 Exp $
 *****************************************************************************
 *  Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
 *  Copyright (C) 2007 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Albert Chu <[email protected]>
 *  UCRL-CODE-232183
 *
 *  This file is part of Ipmi-fru, a tool used for retrieving
 *  motherboard field replaceable unit (FRU) information. For details,
 *  see http://www.llnl.gov/linux/.
 *
 *  Ipmi-fru is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 3 of the License, or (at your
 *  option) any later version.
 *
 *  Ipmi-fru is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with Ipmi-fru.  If not, see <http://www.gnu.org/licenses/>.
\*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>

#include <freeipmi/freeipmi.h>

/* haven't seen a motherboard with more than 2-3 so far, 64 should be more than enough */
#define IPMI_FRU_CUSTOM_FIELDS 64

#define IPMI_FRU_STR_BUFLEN    1024

ipmi_ctx_t ipmi_ctx = NULL;
ipmi_fru_parse_ctx_t fru_parse_ctx = NULL;

int
_output_field (uint8_t language_code,
               ipmi_fru_parse_field_t *field,
               char *str)
{
  char strbuf[IPMI_FRU_PARSE_AREA_STRING_MAX + 1];
  unsigned int strbuflen = IPMI_FRU_PARSE_AREA_STRING_MAX;

  assert (field);
  assert (str);

  if (!field->type_length_field_length)
    return (0);

  memset (strbuf, '\0', IPMI_FRU_PARSE_AREA_STRING_MAX + 1);
      
  if (ipmi_fru_parse_type_length_field_to_string (fru_parse_ctx,
                                                  field->type_length_field,
                                                  field->type_length_field_length,
                                                  language_code,
                                                  strbuf,
                                                  &strbuflen) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_type_length_field_to_string: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  if (strbuflen)
    printf ("  FRU %s: %s\n",
	    str,
	    strbuf);

  return (0);
}

int
ipmi_fru_output_chassis_info_area (const void *areabuf,
                                   unsigned int area_length)
{
  uint8_t chassis_type;
  ipmi_fru_parse_field_t chassis_part_number;
  ipmi_fru_parse_field_t chassis_serial_number;
  ipmi_fru_parse_field_t chassis_custom_fields[IPMI_FRU_CUSTOM_FIELDS];
  unsigned int i;

  assert (areabuf);
  assert (area_length);

  memset (&chassis_part_number, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&chassis_serial_number, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&chassis_custom_fields[0],
          '\0',
          sizeof (ipmi_fru_parse_field_t) * IPMI_FRU_CUSTOM_FIELDS);

  if (ipmi_fru_parse_chassis_info_area (fru_parse_ctx,
                                        areabuf,
                                        area_length,
                                        &chassis_type,
                                        &chassis_part_number,
                                        &chassis_serial_number,
                                        chassis_custom_fields,
                                        IPMI_FRU_CUSTOM_FIELDS) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_chassis_info_area: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  if (IPMI_FRU_CHASSIS_TYPE_VALID (chassis_type))
    printf ("  FRU Chassis Type: %s\n",
	    ipmi_fru_chassis_types[chassis_type]);
  else
    printf ("  FRU Chassis Type: %s\n",
	    ipmi_fru_chassis_types[IPMI_FRU_CHASSIS_TYPE_UNKNOWN]);

  /* achu: Chassis Info Area has no language code, assume English. */

  if (_output_field (IPMI_FRU_LANGUAGE_CODE_ENGLISH,
                     &chassis_part_number,
                     "Chassis Part Number") < 0)
    return (-1);

  if (_output_field (IPMI_FRU_LANGUAGE_CODE_ENGLISH,
                     &chassis_serial_number,
                     "Chassis Serial Number") < 0)
    return (-1);

  for (i = 0; i < IPMI_FRU_CUSTOM_FIELDS; i++)
    {
      if (_output_field (IPMI_FRU_LANGUAGE_CODE_ENGLISH,
                         &chassis_custom_fields[i],
                         "Chassis Custom Info") < 0)
        return (-1);
    }

  return (0);
}

int
ipmi_fru_output_board_info_area (const void *areabuf,
                                 unsigned int area_length)
{
  uint8_t language_code;
  uint32_t mfg_date_time;
  ipmi_fru_parse_field_t board_manufacturer;
  ipmi_fru_parse_field_t board_product_name;
  ipmi_fru_parse_field_t board_serial_number;
  ipmi_fru_parse_field_t board_part_number;
  ipmi_fru_parse_field_t board_fru_file_id;
  ipmi_fru_parse_field_t board_custom_fields[IPMI_FRU_CUSTOM_FIELDS];
  time_t timetmp;
  struct tm mfg_date_time_tm;
  char mfg_date_time_buf[IPMI_FRU_STR_BUFLEN + 1];
  unsigned int i;

  assert (areabuf);
  assert (area_length);

  memset (&board_manufacturer, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&board_product_name, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&board_serial_number, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&board_fru_file_id, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&board_custom_fields[0],
          '\0',
          sizeof (ipmi_fru_parse_field_t) * IPMI_FRU_CUSTOM_FIELDS);

  if (ipmi_fru_parse_board_info_area (fru_parse_ctx,
                                      areabuf,
                                      area_length,
                                      &language_code,
                                      &mfg_date_time,
                                      &board_manufacturer,
                                      &board_product_name,
                                      &board_serial_number,
                                      &board_part_number,
                                      &board_fru_file_id,
                                      board_custom_fields,
                                      IPMI_FRU_CUSTOM_FIELDS) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_board_info_area: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  if (IPMI_FRU_LANGUAGE_CODE_VALID (language_code))
    printf ("  FRU Board Language: %s\n",
	    ipmi_fru_language_codes[language_code]);
  else
    printf ("  FRU Board Language Code: %02Xh\n",
	    language_code);

  /* Posix says individual calls need not clear/set all portions of
   * 'struct tm', thus passing 'struct tm' between functions could
   * have issues.  So we need to memset.
   */
  memset (&mfg_date_time_tm, '\0', sizeof (struct tm));

  timetmp = mfg_date_time;
  localtime_r (&timetmp, &mfg_date_time_tm);
  memset (mfg_date_time_buf, '\0', IPMI_FRU_STR_BUFLEN + 1);
  strftime (mfg_date_time_buf, IPMI_FRU_STR_BUFLEN, "%D - %T", &mfg_date_time_tm);

  printf ("  FRU Board Manufacturing Date/Time: %s\n",
	  mfg_date_time_buf);

  if (_output_field (language_code,
                     &board_manufacturer,
                     "Board Manufacturer") < 0)
    return (-1);

  if (_output_field (language_code,
                     &board_product_name,
                     "Board Product Name") < 0)
    return (-1);


  if (_output_field (language_code,
                     &board_serial_number,
                     "Board Serial Number") < 0)
    return (-1);

  if (_output_field (language_code,
                     &board_part_number,
                     "Board Part Number") < 0)
    return (-1);

  if (_output_field (language_code,
                     &board_fru_file_id,
                     "FRU File ID") < 0)
    return (-1);

  for (i = 0; i < IPMI_FRU_CUSTOM_FIELDS; i++)
    {
      if (_output_field (language_code,
                         &board_custom_fields[i],
                         "Board Custom Info") < 0)
        return (-1);
    }

  return (0);
}

int
ipmi_fru_output_product_info_area (const void *areabuf,
                                   unsigned int area_length)
{
  uint8_t language_code;
  ipmi_fru_parse_field_t product_manufacturer_name;
  ipmi_fru_parse_field_t product_name;
  ipmi_fru_parse_field_t product_part_model_number;
  ipmi_fru_parse_field_t product_version;
  ipmi_fru_parse_field_t product_serial_number;
  ipmi_fru_parse_field_t product_asset_tag;
  ipmi_fru_parse_field_t product_fru_file_id;
  ipmi_fru_parse_field_t product_custom_fields[IPMI_FRU_CUSTOM_FIELDS];
  unsigned int i;

  assert (areabuf);
  assert (area_length);
  
  memset (&product_manufacturer_name, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_name, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_part_model_number, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_version, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_serial_number, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_asset_tag, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_fru_file_id, '\0', sizeof (ipmi_fru_parse_field_t));
  memset (&product_custom_fields[0],
          '\0',
          sizeof (ipmi_fru_parse_field_t) * IPMI_FRU_CUSTOM_FIELDS);
  
  if (ipmi_fru_parse_product_info_area (fru_parse_ctx,
                                        areabuf,
                                        area_length,
                                        &language_code,
                                        &product_manufacturer_name,
                                        &product_name,
                                        &product_part_model_number,
                                        &product_version,
                                        &product_serial_number,
                                        &product_asset_tag,
                                        &product_fru_file_id,
                                        product_custom_fields,
                                        IPMI_FRU_CUSTOM_FIELDS) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_product_info_area: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  if (IPMI_FRU_LANGUAGE_CODE_VALID (language_code))
    printf ("  FRU Product Language: %s\n",
	    ipmi_fru_language_codes[language_code]);
  else
    printf ("  FRU Product Language Code: %02Xh\n",
	    language_code);

  if (_output_field (language_code,
                     &product_manufacturer_name,
                     "Product Manufacturer Name") < 0)
    return (-1);

  if (_output_field (language_code,
                     &product_name,
                     "Product Name") < 0)
    return (-1);


  if (_output_field (language_code,
                     &product_part_model_number,
                     "Product Part/Model Number") < 0)
    return (-1);

  if (_output_field (language_code,
                     &product_version,
                     "Product Version") < 0)
    return (-1);

  if (_output_field (language_code,
                     &product_serial_number,
                     "Product Serial Number") < 0)
    return (-1);

  if (_output_field (language_code,
                     &product_asset_tag,
                     "Product Asset Tag") < 0)
    return (-1);

  if (_output_field (language_code,
                     &product_fru_file_id,
                     "FRU File ID") < 0)
    return (-1);

  for (i = 0; i < IPMI_FRU_CUSTOM_FIELDS; i++)
    {
      if (_output_field (language_code,
                         &product_custom_fields[i],
                         "Product Custom Info") < 0)
        return (-1);
    }

  return (0);
}

static char *
_voltage_str (uint8_t voltage)
{
  if (voltage == IPMI_FRU_VOLTAGE_12V)
    return "12V";
  else if (voltage == IPMI_FRU_VOLTAGE_MINUS12V)
    return "-12V";
  else if (voltage == IPMI_FRU_VOLTAGE_5V)
    return "5V";
  else if (voltage == IPMI_FRU_VOLTAGE_3_3V)
    return "3.3V";
  else
    return "";
}

int
ipmi_fru_output_power_supply_information (const void *areabuf,
                                          uint8_t area_length)
{
  unsigned int overall_capacity;
  unsigned int peak_va;
  unsigned int inrush_current;
  unsigned int inrush_interval;
  unsigned int low_end_input_voltage_range_1;
  unsigned int high_end_input_voltage_range_1;
  unsigned int low_end_input_voltage_range_2;
  unsigned int high_end_input_voltage_range_2;
  unsigned int low_end_input_frequency_range;
  unsigned int high_end_input_frequency_range;
  unsigned int ac_dropout_tolerance;
  unsigned int predictive_fail_support;
  unsigned int power_factor_correction;
  unsigned int autoswitch;
  unsigned int hot_swap_support;
  unsigned int tachometer_pulses_per_rotation_predictive_fail_polarity;
  unsigned int peak_capacity;
  unsigned int hold_up_time;
  unsigned int voltage_1;
  unsigned int voltage_2;
  unsigned int total_combined_wattage;
  unsigned int predictive_fail_tachometer_lower_threshold;

  assert (areabuf);
  assert (area_length);

  if (ipmi_fru_parse_multirecord_power_supply_information (fru_parse_ctx,
                                                           areabuf,
                                                           area_length,
                                                           &overall_capacity,
                                                           &peak_va,
                                                           &inrush_current,
                                                           &inrush_interval,
                                                           &low_end_input_voltage_range_1,
                                                           &high_end_input_voltage_range_1,
                                                           &low_end_input_voltage_range_2,
                                                           &high_end_input_voltage_range_2,
                                                           &low_end_input_frequency_range,
                                                           &high_end_input_frequency_range,
                                                           &ac_dropout_tolerance,
                                                           &predictive_fail_support,
                                                           &power_factor_correction,
                                                           &autoswitch,
                                                           &hot_swap_support,
                                                           &tachometer_pulses_per_rotation_predictive_fail_polarity,
                                                           &peak_capacity,
                                                           &hold_up_time,
                                                           &voltage_1,
                                                           &voltage_2,
                                                           &total_combined_wattage,
                                                           &predictive_fail_tachometer_lower_threshold) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_power_supply_information: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  printf ("  FRU Power Supply Overall Capacity: %u Watts\n",
	  overall_capacity);
  printf ("  FRU Power Supply Peak VA: %u VA\n",
	  peak_va);
  printf ("  FRU Power Supply Max Inrush Current: %u Amps\n",
	  inrush_current);
  printf ("  FRU Power Supply Inrush Interval: %u ms\n",
	  inrush_interval);
  printf ("  FRU Power Supply Low End Input Voltage 1: %u mV\n",
	  low_end_input_voltage_range_1);
  printf ("  FRU Power Supply High End Input Voltage 1: %u mV\n",
	  high_end_input_voltage_range_1);
  printf ("  FRU Power Supply Low End Input Voltage 2: %u mV\n",
	  low_end_input_voltage_range_2);
  printf ("  FRU Power Supply High End Input Voltage 2: %u mV\n",
	  high_end_input_voltage_range_2);
  printf ("  FRU Power Supply Low End Acceptable Frequencey: %u Hz\n",
	  low_end_input_frequency_range);
  printf ("  FRU Power Supply High End Acceptable Frequencey: %u Hz\n",
	  high_end_input_frequency_range);
  printf ("  FRU Power Supply A/C Dropout Tolerance: %u ms\n",
	  ac_dropout_tolerance);
  printf ("  FRU Power Supply Predictive Fail Support: %s\n",
	  (predictive_fail_support) ? "Yes" : "No");
  if (predictive_fail_support)
    {
      if (tachometer_pulses_per_rotation_predictive_fail_polarity)
        {
          if (predictive_fail_tachometer_lower_threshold)
            printf ("  FRU Power Supply Predictive Fail: Tach output, two pulses per rotation\n");
          else
            printf ("  FRU Power Supply Predictive Fail: Pass/Fail predictive fail pin (0 = fail)\n");
        }
      else
        {
          if (predictive_fail_tachometer_lower_threshold)
            printf ("  FRU Power Supply Predictive Fail: Tach output, one pulse per rotation\n");
          else
            printf ("  FRU Power Supply Predictive Fail: Pass/Fail predictive fail pin (1 = fail)\n");
        }
    }
  printf ("  FRU Power Supply Power Factor Correction Supported: %s\n",
	  (power_factor_correction) ? "Yes" : "No");
  printf ("  FRU Power Supply AutoSwitch Supprt: %s\n",
	  (autoswitch) ? "Yes" : "No");
  printf ("  FRU Power Supply Hot Swap Support: %s\n",
	  (hot_swap_support) ? "Yes" : "No");

  printf ("  FRU Power Supply Peak Capacity: %u Watts\n",
	  peak_capacity);
  printf ("  FRU Power Supply Hold Up Time: %u s\n",
	  hold_up_time);
  printf ("  FRU Power Supply Voltage 1: %s\n",
	  _voltage_str (voltage_1));
  printf ("  FRU Power Supply Voltage 2: %s\n",
	  _voltage_str (voltage_2));
  printf ("  FRU Power Supply Total Combined Wattage: %u Watts\n",
	  total_combined_wattage);

  return (0);
}

int
ipmi_fru_output_dc_output (const void *areabuf,
                           uint8_t area_length)
{
  unsigned int output_number;
  unsigned int standby;
  int nominal_voltage;
  int maximum_negative_voltage_deviation;
  int maximum_positive_voltage_deviation;
  unsigned int ripple_and_noise_pk_pk;
  unsigned int minimum_current_draw;
  unsigned int maximum_current_draw;

  assert (areabuf);
  assert (area_length);

  if (ipmi_fru_parse_multirecord_dc_output (fru_parse_ctx,
                                            areabuf,
                                            area_length,
                                            &output_number,
                                            &standby,
                                            &nominal_voltage,
                                            &maximum_negative_voltage_deviation,
                                            &maximum_positive_voltage_deviation,
                                            &ripple_and_noise_pk_pk,
                                            &minimum_current_draw,
                                            &maximum_current_draw) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_dc_output: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  printf ("  FRU DC Output Output Number: %u\n",
	  output_number);
  printf ("  FRU DC Output Output on Standy: %s\n",
	  (standby) ? "Yes" : "No");
  printf ("  FRU DC Output Nominal Voltage: %d mV\n",
	  (int16_t)nominal_voltage);
  printf ("  FRU DC Output Maximum Negative Voltage Deviation: %d mV\n",
	  (int16_t)maximum_negative_voltage_deviation);
  printf ("  FRU DC Output Maximum Positive Voltage Deviation: %d mV\n",
	  (int16_t)maximum_positive_voltage_deviation);
  printf ("  FRU DC Output Ripple and Noise pk-pk: %u mV\n",
	  ripple_and_noise_pk_pk);
  printf ("  FRU DC Output Minimum Current Draw: %u mA\n",
	  minimum_current_draw);
  printf ("  FRU DC Output Maximum Current Draw: %u mA\n",
	  maximum_current_draw);

  return (0);
}

int
ipmi_fru_output_dc_load (const void *areabuf,
                         uint8_t area_length)
{
  unsigned int output_number;
  unsigned int standby;
  int nominal_voltage;
  int specd_minimum_voltage;
  int specd_maximum_voltage;
  unsigned int specd_ripple_and_noise_pk_pk;
  unsigned int minimum_current_load;
  unsigned int maximum_current_load;

  assert (areabuf);
  assert (area_length);

  if (ipmi_fru_parse_multirecord_dc_load (fru_parse_ctx,
                                          areabuf,
                                          area_length,
                                          &output_number,
                                          &standby,
                                          &nominal_voltage,
                                          &specd_minimum_voltage,
                                          &specd_maximum_voltage,
                                          &specd_ripple_and_noise_pk_pk,
                                          &minimum_current_load,
                                          &maximum_current_load) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_dc_load: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  printf ("  FRU DC Load Output Number: %u\n",
	  output_number);
  printf ("  FRU DC Load Nominal Voltage: %d mV\n",
	  (int16_t)nominal_voltage);
  printf ("  FRU DC Load Spec'd Minimum Voltage: %d mV\n",
	  (int16_t)specd_minimum_voltage);
  printf ("  FRU DC Load Spec'd Maximum Voltage: %d mV\n",
	  (int16_t)specd_maximum_voltage);
  printf ("  FRU DC Load Spec'd Ripple and Noise pk-pk: %u mV\n",
	  specd_ripple_and_noise_pk_pk);
  printf ("  FRU DC Load Minimum Current Load: %u mA\n",
	  minimum_current_load);
  printf ("  FRU DC Load Maximum Current Load: %u mA\n",
	  maximum_current_load);

  return (0);
}

int
ipmi_fru_output_management_access_record (const void *areabuf,
                                          uint8_t area_length)
{
  uint8_t sub_record_type;
  uint8_t sub_record_data[IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1];
  unsigned int sub_record_data_len = IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX;
  unsigned int i;
  
  assert (areabuf);
  assert (area_length);
  
  memset (sub_record_data, '\0', IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1);
  
  if (ipmi_fru_parse_multirecord_management_access_record (fru_parse_ctx,
                                                           areabuf,
                                                           area_length,
                                                           &sub_record_type,
                                                           sub_record_data,
                                                           &sub_record_data_len) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_management_access_record: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_SYSTEM_MANAGEMENT_URL)
    printf ("  FRU Management Access System Management URL: %s\n",
	    (char *)sub_record_data);
  else if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_SYSTEM_NAME)
    printf ("  FRU Management Access System Name: %s\n",
	    (char *)sub_record_data);
  else if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_SYSTEM_PING_ADDRESS)
    printf ("  FRU Management Access System Ping Address: %s\n",
	    (char *)sub_record_data);
  else if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_COMPONENT_MANAGEMENT_URL)
    printf ("  FRU Management Access Component Management URL: %s\n",
	    (char *)sub_record_data);
  else if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_COMPONENT_NAME)
    printf ("  FRU Management Access Component Name: %s\n",
	    (char *)sub_record_data);
  else if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_COMPONENT_PING_ADDRESS)
    printf ("  FRU Management Access Component Ping Address: %s\n",
	    (char *)sub_record_data);
  else if (sub_record_type == IPMI_FRU_SUB_RECORD_TYPE_SYSTEM_UNIQUE_ID)
    {
      printf ("  FRU Management Access System Unique ID:");

      for (i = 0; i < sub_record_data_len; i++)
        {
          if (sub_record_data_len > 8 && (i % 8) == 0)
            printf ("\n    ");

          printf (" %02Xh",
		  sub_record_data[i]);
        }
      printf ("\n");
    }
  else
    {
      printf ("  FRU Management Access Record: Unknown Sub Record Type:");
      for (i = 0; i < sub_record_data_len; i++)
        {
          if (sub_record_data_len > 8 && (i % 8) == 0)
            printf ("\n    ");

          printf (" %02Xh",
		  sub_record_data[i]);
        }
      printf ("\n");
    }

  return (0);
}

int
ipmi_fru_output_base_compatibility_record (const void *areabuf,
                                           uint8_t area_length)
{
  uint32_t manufacturer_id;
  unsigned int entity_id_code;
  unsigned int compatibility_base;
  unsigned int compatibility_code_start_value;
  uint8_t code_range_mask[IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1];
  unsigned int code_range_mask_len = IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX;
  char iana_buf[IPMI_FRU_STR_BUFLEN + 1];
  int ret;

  assert (areabuf);
  assert (area_length);

  memset (code_range_mask, '\0', IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1);

  if (ipmi_fru_parse_multirecord_base_compatibility_record (fru_parse_ctx,
                                                            areabuf,
                                                            area_length,
                                                            &manufacturer_id,
                                                            &entity_id_code,
                                                            &compatibility_base,
                                                            &compatibility_code_start_value,
                                                            code_range_mask,
                                                            &code_range_mask_len) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_base_compatibility_record: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  memset (iana_buf, '\0', IPMI_FRU_STR_BUFLEN + 1);
  
  /* if ret == 0 means no string, < 0 means bad manufacturer id
   * either way, output just the number
   */
  ret = ipmi_iana_enterprise_numbers_string (manufacturer_id,
                                             iana_buf,
                                             IPMI_FRU_STR_BUFLEN);

  if (ret > 0)
    printf ("  FRU Base Compatibility Manufacturer ID: %s (%Xh)\n",
	    iana_buf,
	    manufacturer_id);
  else
    printf ("  FRU Base Compatibility Manufacturer ID: %Xh\n",
	    manufacturer_id);

  printf ("  FRU Base Compatibility Entity ID: %Xh\n",
	  entity_id_code);
  printf ("  FRU Base Compatibility Comptability Base: %Xh\n",
	  compatibility_base);
  printf ("  FRU Base Compatibility Comptability Code Start Value: %Xh\n",
	  compatibility_code_start_value);

  if (code_range_mask_len)
    {
      unsigned int i;

      printf ("  FRU Base Compatibility Code Mask:");

      for (i = 0; i < code_range_mask_len; i++)
        {
          if (code_range_mask_len > 8 && (i % 8) == 0)
            printf ("\n    ");
          
          printf (" %02Xh",
		  code_range_mask[i]);
        }
      printf ("\n");
    }

  return (0);
}

int
ipmi_fru_output_extended_compatibility_record (const void *areabuf,
                                               uint8_t area_length)
{
  uint32_t manufacturer_id;
  unsigned int entity_id_code;
  unsigned int compatibility_base;
  unsigned int compatibility_code_start_value;
  uint8_t code_range_mask[IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1];
  unsigned int code_range_mask_len = IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX;
  char iana_buf[IPMI_FRU_STR_BUFLEN + 1];
  int ret;

  assert (areabuf);
  assert (area_length);

  memset (code_range_mask, '\0', IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1);

  if (ipmi_fru_parse_multirecord_extended_compatibility_record (fru_parse_ctx,
                                                                areabuf,
                                                                area_length,
                                                                &manufacturer_id,
                                                                &entity_id_code,
                                                                &compatibility_base,
                                                                &compatibility_code_start_value,
                                                                code_range_mask,
                                                                &code_range_mask_len) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_extended_compatibility_record: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }

  memset (iana_buf, '\0', IPMI_FRU_STR_BUFLEN + 1);
  
  /* if ret == 0 means no string, < 0 means bad manufacturer id
   * either way, output just the number
   */
  ret = ipmi_iana_enterprise_numbers_string (manufacturer_id,
                                             iana_buf,
                                             IPMI_FRU_STR_BUFLEN);

  if (ret > 0)
    printf ("  FRU Extended Compatibility Manufacturer ID: %s (%Xh)\n",
	    iana_buf,
	    manufacturer_id);
  else
    printf ("  FRU Extended Compatibility Manufacturer ID: %Xh\n",
	    manufacturer_id);

  printf ("  FRU Extended Compatibility Entity ID: %Xh\n",
	  entity_id_code);
  printf ("  FRU Extended Compatibility Comptability Base: %Xh\n",
	  compatibility_base);
  printf ("  FRU Extended Compatibility Comptability Code Start Value: %Xh\n",
	  compatibility_code_start_value);

  if (code_range_mask_len)
    {
      unsigned int i;

      printf ("  FRU Extended Compatibility Code Mask:");

      for (i = 0; i < code_range_mask_len; i++)
        {
          if (code_range_mask_len > 8 && (i % 8) == 0)
            printf ("\n    ");
          
          printf (" %02Xh",
		  code_range_mask[i]);
        }
      printf ("\n");
    }

  return (0);
}

int
ipmi_fru_output_oem_record (const void *areabuf,
                            uint8_t area_length)
{
  uint32_t manufacturer_id;
  uint8_t oem_data[IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1];
  unsigned int oem_data_len = IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX;
  char iana_buf[IPMI_FRU_STR_BUFLEN + 1];
  int ret;

  assert (areabuf);
  assert (area_length);

  memset (oem_data, '\0', IPMI_FRU_PARSE_AREA_TYPE_LENGTH_FIELD_MAX + 1);

  if (ipmi_fru_parse_multirecord_oem_record (fru_parse_ctx,
                                             areabuf,
                                             area_length,
                                             &manufacturer_id,
                                             oem_data,
                                             &oem_data_len) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_multirecord_oem_record: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      return (-1);
    }
  
  memset (iana_buf, '\0', IPMI_FRU_STR_BUFLEN + 1);
  
  /* if ret == 0 means no string, < 0 means bad manufacturer id
   * either way, output just the number
   */
  ret = ipmi_iana_enterprise_numbers_string (manufacturer_id,
                                             iana_buf,
                                             IPMI_FRU_STR_BUFLEN);
  
  if (ret > 0)
    printf ("  FRU OEM Manufacturer ID: %s (%Xh)\n",
	    iana_buf,
	    manufacturer_id);
  else
    printf ("  FRU OEM Manufacturer ID: %Xh\n",
	    manufacturer_id);

  if (oem_data_len)
    {
      unsigned int i;

      printf ("  FRU OEM Code Mask:");

      for (i = 0; i < oem_data_len; i++)
        {
          if (oem_data_len > 8 && (i % 8) == 0)
            printf ("\n    ");

          printf (" %02Xh",
		  oem_data[i]);
        }
      printf ("\n");
    }

  return (0);
}

int
main (int argc, char **argv)
{
  int exit_code = 1;
  int ret;

  if (!(ipmi_ctx = ipmi_ctx_create ()))
    {
      perror ("ipmi_ctx_create");
      goto cleanup;
    }

  if ((ret = ipmi_ctx_find_inband (ipmi_ctx,
				   NULL,
				   0, /* don't disable auto-probe */
				   0,
				   0,
				   NULL,
				   0, /* workaround flags, none by default */
				   0  /* flags */
				   )) < 0)
    {
      fprintf (stderr,
	       "ipmi_ctx_find_inband: %s",
	       ipmi_ctx_errormsg (ipmi_ctx));
      goto cleanup;
    }

  if (!ret)
    {
      fprintf (stderr,
	       "could not find inband device");
      goto cleanup;
    }


  if (!(fru_parse_ctx = ipmi_fru_parse_ctx_create (ipmi_ctx)))
    {
      perror ("ipmi_fru_parse_ctx_create()");
      goto cleanup;
    }
      
  /* lots of motherboards calculate checksums incorrectly, maybe want to strongly consider setting this */
  if (ipmi_fru_parse_ctx_set_flags (fru_parse_ctx, IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_ctx_set_flags: %s\n",
	       ipmi_fru_parse_ctx_strerror (ipmi_fru_parse_ctx_errnum (fru_parse_ctx)));
      goto cleanup;
    }

  /* only consider default device id of 0 */
  if (ipmi_fru_parse_open_device_id (fru_parse_ctx, 0) < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_open_device_id: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      goto cleanup;
    }

  do 
    {
      uint8_t areabuf[IPMI_FRU_PARSE_AREA_SIZE_MAX+1];
      unsigned int area_type = 0;
      unsigned int area_length = 0;
      
      memset (areabuf, '\0', IPMI_FRU_PARSE_AREA_SIZE_MAX + 1);
      if (ipmi_fru_parse_read_data_area (fru_parse_ctx,
                                         &area_type,
                                         &area_length,
                                         areabuf,
                                         IPMI_FRU_PARSE_AREA_SIZE_MAX) < 0)
        {
          fprintf (stderr,
		   "ipmi_fru_parse_open_device_id: %s\n",
		   ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
          goto cleanup;
        }

      if (area_length)
        {
          switch (area_type)
            {
            case IPMI_FRU_PARSE_AREA_TYPE_CHASSIS_INFO_AREA:
              if (ipmi_fru_output_chassis_info_area (areabuf,
                                                     area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_BOARD_INFO_AREA:
              if (ipmi_fru_output_board_info_area (areabuf,
                                                   area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_PRODUCT_INFO_AREA:
              if (ipmi_fru_output_product_info_area (areabuf,
                                                     area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION:
              if (ipmi_fru_output_power_supply_information (areabuf,
                                                            area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_DC_OUTPUT:
              if (ipmi_fru_output_dc_output (areabuf,
                                             area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_DC_LOAD:
              if (ipmi_fru_output_dc_load (areabuf,
                                           area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_MANAGEMENT_ACCESS_RECORD:
              if (ipmi_fru_output_management_access_record (areabuf,
                                                            area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_BASE_COMPATABILITY_RECORD:
              if (ipmi_fru_output_base_compatibility_record (areabuf,
                                                             area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_EXTENDED_COMPATABILITY_RECORD:
              if (ipmi_fru_output_extended_compatibility_record (areabuf,
                                                                 area_length) < 0)
                goto cleanup;
              break;
            case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_OEM:
              if (ipmi_fru_output_oem_record (areabuf,
                                              area_length) < 0)
                goto cleanup;
              break;
            default:
              fprintf (stderr,
		       "  FRU Error: Unknown FRU Area Type Read: %02Xh\n",
		       area_type);
              goto next;
              break;
            }
        }

    next:
      printf ("\n");
      ;
    } while ((ret = ipmi_fru_parse_next (fru_parse_ctx)) == 1);
    
  if (ret < 0)
    {
      fprintf (stderr,
	       "ipmi_fru_parse_next: %s\n",
	       ipmi_fru_parse_ctx_errormsg (fru_parse_ctx));
      goto cleanup;
    }

  exit_code = 0;
 cleanup:
  if (fru_parse_ctx)
    {
      ipmi_fru_parse_close_device_id (fru_parse_ctx);
      ipmi_fru_parse_ctx_destroy (fru_parse_ctx);
    }
  if (ipmi_ctx)
    {
      ipmi_ctx_close (ipmi_ctx);
      ipmi_ctx_destroy (ipmi_ctx);
    }
  return (exit_code);
}

_______________________________________________
Freeipmi-devel mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/freeipmi-devel

Reply via email to