int
fill_cmd_get_oem_netfn_iana_support (uint8_t channel_number,
                                     uint8_t net_fn,
                                     uint8_t list_index,
                                     fiid_obj_t obj_cmd_rq)
{
  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || (net_fn != IPMI_NET_FN_GROUP_EXTENSION_RQ
          && net_fn != IPMI_NET_FN_OEM_GROUP_RQ)
      || !fiid_obj_valid (obj_cmd_rq))
    {
      SET_ERRNO (EINVAL);
      return (-1);
    }
  
  if (FIID_OBJ_TEMPLATE_COMPARE (obj_cmd_rq, tmpl_cmd_get_oem_netfn_iana_support_rq) < 0)
    {
      ERRNO_TRACE (errno);
      return (-1);
    }

  FILL_FIID_OBJ_CLEAR (obj_cmd_rq);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "cmd", IPMI_CMD_GET_OEM_NETFN_IANA_SUPPORT);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "channel_number", channel_number);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved1", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn", net_fn);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved2", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "list_index", list_index);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved3", 0);
  
  return (0);
}
static int
_ipmi_config_parse_channel_number (char *arg,
                                   uint8_t *channel_number,
                                   int *channel_number_set)
{
  char *endptr;
  int tmp;

  assert (arg);
  assert (channel_number);
  assert (channel_number_set);

  errno = 0;
  tmp = strtol (arg, &endptr, 0);
  if (errno
      || endptr[0] != '\0')
    {
      fprintf (stderr, "invalid channel number\n");
      return (-1);
    }
  if (!IPMI_CHANNEL_NUMBER_VALID (tmp))
    {
      fprintf (stderr, "invalid channel number\n");
      return (-1);
    }
  (*channel_number) = (uint8_t)tmp;
  (*channel_number_set)++;
  return (0);
}
static void
_config_parse_channel_number (char *arg,
                              uint8_t *channel_number,
                              int *channel_number_set)
{
  char *endptr;
  int tmp;

  assert (arg);
  assert (channel_number);
  assert (channel_number_set);

  tmp = strtol (arg, &endptr, 0);
  if (errno
      || endptr[0] != '\0')
    {
      fprintf (stderr, "invalid channel number\n");
      exit (EXIT_FAILURE);
    }
  if (!IPMI_CHANNEL_NUMBER_VALID (tmp))
    {
      fprintf (stderr, "invalid channel number\n");
      exit(1);
    }
  (*channel_number) = (uint8_t)tmp;
  (*channel_number_set)++;
}
int
fill_cmd_set_command_enables (uint8_t channel_number,
                              uint8_t net_fn,
                              uint8_t operation,
                              uint8_t lun,
                              uint8_t *enable_disable_bitmask,
                              unsigned int enable_disable_bitmask_len,
                              uint32_t net_fn_data,
                              fiid_obj_t obj_cmd_rq)
{
  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || !IPMI_NET_FN_VALID (net_fn)
      || !IPMI_FIRMWARE_FIREWALL_COMMAND_DISCOVERY_OPERATION_VALID (operation)
      || !IPMI_BMC_LUN_VALID (lun)
      || !enable_disable_bitmask
      || enable_disable_bitmask_len < IPMI_FIRMWARE_FIREWALL_COMMAND_DISCOVERY_ENABLE_DISABLE_BITMASK_LEN
      || !fiid_obj_valid (obj_cmd_rq))
    {
      SET_ERRNO (EINVAL);
      return (-1);
    }
  
  if (FIID_OBJ_TEMPLATE_COMPARE (obj_cmd_rq, tmpl_cmd_set_command_enables_rq) < 0)
    {
      ERRNO_TRACE (errno);
      return (-1);
    }

  FILL_FIID_OBJ_CLEAR (obj_cmd_rq);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "cmd", IPMI_CMD_SET_COMMAND_ENABLES);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "channel_number", channel_number);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved1", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn", net_fn);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "operation", operation);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "lun", lun);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved2", 0);
  FILL_FIID_OBJ_SET_DATA (obj_cmd_rq,
                          "enable_disable_mask",
                          enable_disable_bitmask,
                          IPMI_FIRMWARE_FIREWALL_COMMAND_DISCOVERY_ENABLE_DISABLE_BITMASK_LEN);
  if (net_fn == IPMI_NET_FN_GROUP_EXTENSION_RQ
      || net_fn == IPMI_NET_FN_GROUP_EXTENSION_RS)
    {
      uint8_t tmp = net_fn_data;
      FILL_FIID_OBJ_SET_DATA (obj_cmd_rq, "net_fn_data", &tmp, 1);
    }
  else if (net_fn == IPMI_NET_FN_OEM_GROUP_RQ
           || net_fn == IPMI_NET_FN_OEM_GROUP_RS)
    FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn_data", net_fn_data);
  
  return (0);
}
int
ipmi_openipmi_cmd_ipmb (ipmi_openipmi_ctx_t ctx,
                        uint8_t channel_number,
                        uint8_t rs_addr,
                        uint8_t lun,
                        uint8_t net_fn,
                        fiid_obj_t obj_cmd_rq,
                        fiid_obj_t obj_cmd_rs)
{
  if (!ctx || ctx->magic != IPMI_OPENIPMI_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_openipmi_ctx_errormsg (ctx), ipmi_openipmi_ctx_errnum (ctx));
      return (-1);
    }

  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || !IPMI_BMC_LUN_VALID (lun)
      || !IPMI_NET_FN_RQ_VALID (net_fn)
      || !fiid_obj_valid (obj_cmd_rq)
      || !fiid_obj_valid (obj_cmd_rs)
      || fiid_obj_packet_valid (obj_cmd_rq) <= 0)
    {
      OPENIPMI_SET_ERRNUM (ctx, IPMI_OPENIPMI_ERR_PARAMETERS);
      return (-1);
    }

  if (!ctx->io_init)
    {
      OPENIPMI_SET_ERRNUM (ctx, IPMI_OPENIPMI_ERR_IO_NOT_INITIALIZED);
      return (-1);
    }

  if (_openipmi_write (ctx,
                       channel_number,
                       rs_addr,
                       lun,
                       net_fn,
                       obj_cmd_rq,
                       1) < 0)
    return (-1);

  if (_openipmi_read (ctx,
                      obj_cmd_rs) < 0)
    return (-1);

  return (0);
}
int
fill_cmd_get_command_sub_function_enables (uint8_t channel_number,
                                           uint8_t net_fn,
                                           uint8_t lun,
                                           uint8_t command,
                                           uint32_t net_fn_data,
                                           fiid_obj_t obj_cmd_rq)
{
  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || !IPMI_NET_FN_VALID (net_fn)
      || !IPMI_BMC_LUN_VALID (lun)
      || !fiid_obj_valid (obj_cmd_rq))
    {
      SET_ERRNO (EINVAL);
      return (-1);
    }
  
  if (FIID_OBJ_TEMPLATE_COMPARE (obj_cmd_rq, tmpl_cmd_get_command_sub_function_enables_rq) < 0)
    {
      ERRNO_TRACE (errno);
      return (-1);
    }

  FILL_FIID_OBJ_CLEAR (obj_cmd_rq);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "cmd", IPMI_CMD_GET_COMMAND_SUB_FUNCTION_ENABLES);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "channel_number", channel_number);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved1", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn", net_fn);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved2", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "lun", lun);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved3", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "command", command);
  if (net_fn == IPMI_NET_FN_GROUP_EXTENSION_RQ
      || net_fn == IPMI_NET_FN_GROUP_EXTENSION_RS)
    {
      uint8_t tmp = net_fn_data;
      FILL_FIID_OBJ_SET_DATA (obj_cmd_rq, "net_fn_data", &tmp, 1);
    }
  else if (net_fn == IPMI_NET_FN_OEM_GROUP_RQ
           || net_fn == IPMI_NET_FN_OEM_GROUP_RS)
    FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn_data", net_fn_data);
  
  return (0);
}
int
fill_cmd_get_configurable_commands (uint8_t channel_number,
                                    uint8_t net_fn,
                                    uint8_t operation,
                                    uint8_t lun,
                                    uint32_t net_fn_data,
                                    fiid_obj_t obj_cmd_rq)
{
  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || !IPMI_NET_FN_VALID (net_fn)
      || !IPMI_FIRMWARE_FIREWALL_COMMAND_DISCOVERY_OPERATION_VALID (operation)
      || !IPMI_BMC_LUN_VALID (lun)
      || !fiid_obj_valid (obj_cmd_rq))
    {
      SET_ERRNO (EINVAL);
      return (-1);
    }
  
  if (FIID_OBJ_TEMPLATE_COMPARE (obj_cmd_rq, tmpl_cmd_get_configurable_commands_rq) < 0)
    {
      ERRNO_TRACE (errno);
      return (-1);
    }

  FILL_FIID_OBJ_CLEAR (obj_cmd_rq);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "cmd", IPMI_CMD_GET_CONFIGURABLE_COMMANDS);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "channel_number", channel_number);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved1", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn", net_fn);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "operation", operation);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "lun", lun);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved2", 0);
  if (net_fn == IPMI_NET_FN_GROUP_EXTENSION_RQ
      || net_fn == IPMI_NET_FN_GROUP_EXTENSION_RS)
    {
      uint8_t tmp = net_fn_data;
      FILL_FIID_OBJ_SET_DATA (obj_cmd_rq, "net_fn_data", &tmp, 1);
    }
  else if (net_fn == IPMI_NET_FN_OEM_GROUP_RQ
           || net_fn == IPMI_NET_FN_OEM_GROUP_RS)
    FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn_data", net_fn_data);
  
  return (0);
}
int
fill_cmd_set_command_sub_function_enables_oem_iana (uint8_t channel_number,
                                                    uint8_t net_fn,
                                                    uint8_t lun,
                                                    uint8_t command,
                                                    uint32_t oem_iana,
                                                    uint32_t sub_function_enables1,
                                                    uint32_t *sub_function_enables2,
                                                    fiid_obj_t obj_cmd_rq)
{
  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || (net_fn != IPMI_NET_FN_OEM_GROUP_RQ
          && net_fn != IPMI_NET_FN_OEM_GROUP_RS)
      || !IPMI_BMC_LUN_VALID (lun)
      || !fiid_obj_valid (obj_cmd_rq))
    {
      SET_ERRNO (EINVAL);
      return (-1);
    }
  
  if (FIID_OBJ_TEMPLATE_COMPARE (obj_cmd_rq, tmpl_cmd_set_command_sub_function_enables_rq) < 0)
    {
      ERRNO_TRACE (errno);
      return (-1);
    }

  FILL_FIID_OBJ_CLEAR (obj_cmd_rq);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "cmd", IPMI_CMD_SET_COMMAND_SUB_FUNCTION_ENABLES);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "channel_number", channel_number);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved1", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "net_fn", net_fn);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved2", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "lun", lun);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved3", 0);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "command", command);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "oem_iana", oem_iana);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "sub_function_enables1", sub_function_enables1);
  if (sub_function_enables2)
    FILL_FIID_OBJ_SET (obj_cmd_rq, "sub_function_enables2", *sub_function_enables2);
  
  return (0);
}
int
fill_cmd_get_netfn_support (uint8_t channel_number,
                            fiid_obj_t obj_cmd_rq)
{
  if (!IPMI_CHANNEL_NUMBER_VALID (channel_number)
      || !fiid_obj_valid (obj_cmd_rq))
    {
      SET_ERRNO (EINVAL);
      return (-1);
    }
  
  if (FIID_OBJ_TEMPLATE_COMPARE (obj_cmd_rq, tmpl_cmd_get_netfn_support_rq) < 0)
    {
      ERRNO_TRACE (errno);
      return (-1);
    }

  FILL_FIID_OBJ_CLEAR (obj_cmd_rq);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "cmd", IPMI_CMD_GET_NETFN_SUPPORT);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "channel_number", channel_number);
  FILL_FIID_OBJ_SET (obj_cmd_rq, "reserved", 0);
  
  return (0);
}
static int
_openipmi_write (ipmi_openipmi_ctx_t ctx,
                 uint8_t channel_number,
                 uint8_t rs_addr,
                 uint8_t lun,
                 uint8_t net_fn,
                 fiid_obj_t obj_cmd_rq,
                 unsigned int is_ipmb)
{
  uint8_t rq_buf_temp[IPMI_OPENIPMI_BUFLEN];
  uint8_t rq_buf[IPMI_OPENIPMI_BUFLEN];
  uint8_t rq_cmd;
  unsigned int rq_buf_len;
  int len;
  struct ipmi_system_interface_addr system_interface_addr;
  struct ipmi_ipmb_addr ipmb_addr;
  struct ipmi_req rq_packet;

  assert (ctx);
  assert (ctx->magic == IPMI_OPENIPMI_CTX_MAGIC);
  assert (IPMI_CHANNEL_NUMBER_VALID (channel_number));
  assert (IPMI_BMC_LUN_VALID (lun));
  assert (IPMI_NET_FN_RQ_VALID (net_fn));
  assert (fiid_obj_valid (obj_cmd_rq));
  assert (fiid_obj_packet_valid (obj_cmd_rq) == 1);

  /* Due to API differences, we need to extract the cmd out of the
   * request.
   */
  memset (rq_buf_temp, '\0', IPMI_OPENIPMI_BUFLEN);

  if ((len = fiid_obj_get_all (obj_cmd_rq,
                               rq_buf_temp,
                               IPMI_OPENIPMI_BUFLEN)) <= 0)
    {
      OPENIPMI_SET_ERRNUM (ctx, IPMI_OPENIPMI_ERR_INTERNAL_ERROR);
      return (-1);
    }

  rq_cmd = rq_buf_temp[0];
  if (len > 1)
    {
      /* -1 b/c of cmd */
      memcpy (rq_buf, &rq_buf_temp[1], len - 1);
      rq_buf_len = len - 1;
    }
  else
    rq_buf_len = 0;

  if (!is_ipmb)
    {
      system_interface_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; /* openipmi macro */
      system_interface_addr.channel = IPMI_CHANNEL_NUMBER_SYSTEM_INTERFACE; /* freeipmi macro */
      system_interface_addr.lun = lun;

      rq_packet.addr = (unsigned char *)&system_interface_addr;
      rq_packet.addr_len = sizeof (struct ipmi_system_interface_addr);
    }
  else
    {
      ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; /* openipmi macro */
      ipmb_addr.channel = channel_number;
      ipmb_addr.slave_addr = rs_addr;
      ipmb_addr.lun = lun;

      rq_packet.addr = (unsigned char *)&ipmb_addr;
      rq_packet.addr_len = sizeof (struct ipmi_ipmb_addr);
    }

  rq_packet.msgid = 0;
  rq_packet.msg.netfn = net_fn;
  rq_packet.msg.cmd = rq_cmd;
  rq_packet.msg.data_len = rq_buf_len;
  rq_packet.msg.data = rq_buf;

  if (ioctl (ctx->device_fd,
             IPMICTL_SEND_COMMAND,
             &rq_packet) < 0)
    {
      OPENIPMI_ERRNO_TO_OPENIPMI_ERRNUM (ctx, errno);
      return (-1);
    }

  return (0);
}