int
ipmi_openipmi_ctx_io_init (ipmi_openipmi_ctx_t ctx)
{
  unsigned int addr = IPMI_SLAVE_ADDRESS_BMC;
  char *driver_device;
  int flags;

  if (!ctx || ctx->magic != IPMI_OPENIPMI_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_openipmi_ctx_errormsg (ctx), ipmi_openipmi_ctx_errnum (ctx));
      return (-1);
    }

  if (ctx->io_init)
    goto out;

  if (ctx->driver_device)
    driver_device = ctx->driver_device;
  else
    driver_device = IPMI_OPENIPMI_DRIVER_DEVICE_DEFAULT;

  if ((ctx->device_fd = open (driver_device,
                              O_RDWR)) < 0)
    {
      OPENIPMI_ERRNO_TO_OPENIPMI_ERRNUM (ctx, errno);
      goto cleanup;
    }

  flags = fcntl(ctx->device_fd, F_GETFD);
  if (flags < 0)
    {
      OPENIPMI_ERRNO_TO_OPENIPMI_ERRNUM (ctx, errno);
      goto cleanup;
    }
  flags |= FD_CLOEXEC;
  if (fcntl(ctx->device_fd, F_SETFD, flags) < 0)
    {
      OPENIPMI_ERRNO_TO_OPENIPMI_ERRNUM (ctx, errno);
      goto cleanup;
    }

  if (ioctl (ctx->device_fd,
             IPMICTL_SET_MY_ADDRESS_CMD,
             &addr) < 0)
    {
      OPENIPMI_ERRNO_TO_OPENIPMI_ERRNUM (ctx, errno);
      goto cleanup;
    }

  ctx->io_init = 1;
 out:
  ctx->errnum = IPMI_OPENIPMI_ERR_SUCCESS;
  return (0);

 cleanup:
  /* ignore potential error, error path */
  close (ctx->device_fd);
  ctx->device_fd = -1;
  return (-1);
}
static int
_openipmi_read (ipmi_openipmi_ctx_t ctx,
                fiid_obj_t obj_cmd_rs)
{
  uint8_t rs_buf_temp[IPMI_OPENIPMI_BUFLEN];
  uint8_t rs_buf[IPMI_OPENIPMI_BUFLEN];
  struct ipmi_system_interface_addr rs_addr;
  struct ipmi_recv rs_packet;
  fd_set read_fds;
  struct timeval tv;
  int n;

  assert (ctx);
  assert (ctx->magic == IPMI_OPENIPMI_CTX_MAGIC);
  assert (fiid_obj_valid (obj_cmd_rs));

  rs_packet.addr = (unsigned char *)&rs_addr;
  rs_packet.addr_len = sizeof (struct ipmi_system_interface_addr);
  rs_packet.msg.data = rs_buf_temp;
  rs_packet.msg.data_len = IPMI_OPENIPMI_BUFLEN;

  FD_ZERO (&read_fds);
  FD_SET (ctx->device_fd, &read_fds);

  tv.tv_sec = IPMI_OPENIPMI_TIMEOUT;
  tv.tv_usec = 0;

  if ((n = select (ctx->device_fd + 1,
                   &read_fds,
                   NULL,
                   NULL,
                   &tv)) < 0)
    {
      OPENIPMI_ERRNO_TO_OPENIPMI_ERRNUM (ctx, errno);
      return (-1);
    }

  if (!n)
    {
      /* Could be due to a different error, but we assume a timeout */
      OPENIPMI_SET_ERRNUM (ctx, IPMI_OPENIPMI_ERR_DRIVER_TIMEOUT);
      return (-1);
    }

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

  /* achu: atleast the completion code should be returned */
  if (!rs_packet.msg.data_len)
    {
      OPENIPMI_SET_ERRNUM (ctx, IPMI_OPENIPMI_ERR_SYSTEM_ERROR);
      return (-1);
    }

  rs_buf[0] = rs_packet.msg.cmd;
  /* -1 b/c of cmd */
  if (rs_packet.msg.data_len >= (IPMI_OPENIPMI_BUFLEN - 1))
    rs_packet.msg.data_len = IPMI_OPENIPMI_BUFLEN - 1;
  memcpy (rs_buf + 1, rs_buf_temp, rs_packet.msg.data_len);

  if (fiid_obj_set_all (obj_cmd_rs,
                        rs_buf,
                        rs_packet.msg.data_len + 1) < 0)
    {
      OPENIPMI_SET_ERRNUM (ctx, IPMI_OPENIPMI_ERR_INTERNAL_ERROR);
      return (-1);
    }

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