int
ipmi_kcs_ctx_set_poll_interval (ipmi_kcs_ctx_t ctx, uint8_t poll_interval)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  ctx->poll_interval = poll_interval;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_set_driver_address (ipmi_kcs_ctx_t ctx, uint16_t driver_address)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  ctx->driver_address = driver_address;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_set_register_spacing (ipmi_kcs_ctx_t ctx, uint8_t register_spacing)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  ctx->register_spacing = register_spacing;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_io_init (ipmi_kcs_ctx_t ctx)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (ctx->io_init)
    goto out;

#ifdef __FreeBSD__
#ifdef USE_IOPERM
  /* i386_set_ioperm has known problems on FBSD 5.x (bus errors). */
  if (i386_set_ioperm (ctx->driver_address, 0x02, 0x01))
    {
      KCS_ERRNO_TO_KCS_ERRNUM (ctx, errno);
      return (-1);
    }
#else  /* !USE_IOPERM */
  /* Opening /dev/io raises IOPL bits for current process. */
  if ((ctx->dev_fd = open ("/dev/io", O_RDONLY)) < 0)
    {
      KCS_ERRNO_TO_KCS_ERRNUM (ctx, errno);
      return (-1);
    }
#endif /* !USE_IOPERM */
#else  /* !__FreeBSD__ */
#if HAVE_IOPL
  if (iopl (3) < 0)
    {
      KCS_ERRNO_TO_KCS_ERRNUM (ctx, errno);
      return (-1);
    }
#else /* !HAVE_IOPL */
  /* otherwise, we always return a system error */
  KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_SYSTEM_ERROR);
  return (-1);
#endif /* !HAVE_IOPL */
#endif /* !__FreeBSD__ */

  ctx->io_init = 1;
 out:
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_set_flags (ipmi_kcs_ctx_t ctx, unsigned int flags)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (flags & ~IPMI_KCS_FLAGS_MASK)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      return (-1);
    }

  ctx->flags = flags;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_get_register_spacing (ipmi_kcs_ctx_t ctx, uint8_t *register_spacing)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (!register_spacing)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      return (-1);
    }

  *register_spacing = ctx->register_spacing;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_get_poll_interval (ipmi_kcs_ctx_t ctx, uint8_t *poll_interval)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (!poll_interval)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      return (-1);
    }

  *poll_interval = ctx->poll_interval;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_ctx_get_driver_address (ipmi_kcs_ctx_t ctx, uint16_t *driver_address)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (!driver_address)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      return (-1);
    }

  *driver_address = ctx->driver_address;
  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (0);
}
int
ipmi_kcs_cmd (ipmi_kcs_ctx_t ctx,
              uint8_t lun,
              uint8_t net_fn,
              fiid_obj_t obj_cmd_rq,
              fiid_obj_t obj_cmd_rs)
{
  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (!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)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      return (-1);
    }

  if (!ctx->io_init)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_IO_NOT_INITIALIZED);
      return (-1);
    }

  if (_ipmi_kcs_cmd_write (ctx, lun, net_fn, obj_cmd_rq) < 0)
    return (-1);

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

  return (0);
}
/*
 * Main read loop.
 */
int
ipmi_kcs_read (ipmi_kcs_ctx_t ctx,
               void *buf,
               unsigned int buf_len)
{
  uint8_t *p = buf;
  unsigned int count = 0;
  int rv = -1;

  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      goto cleanup;
    }

  if (!buf || !buf_len)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      goto cleanup;
    }

  if (!ctx->io_init)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_IO_NOT_INITIALIZED);
      goto cleanup;
    }

  if (_ipmi_kcs_wait_for_ibf_clear (ctx) < 0)
    goto cleanup;

  if (!_ipmi_kcs_test_if_state (ctx, IPMI_KCS_STATE_READ))
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_BUSY);
      goto cleanup;
    }

  while (_ipmi_kcs_test_if_state (ctx, IPMI_KCS_STATE_READ))
    {
      char c;
      if (_ipmi_kcs_wait_for_obf_set (ctx) < 0)
        goto cleanup;
      c = _ipmi_kcs_read_byte (ctx);
      if (count < buf_len)
        {
          *(p++) = c;
          count++;
        }
      _ipmi_kcs_read_next (ctx);
      if (_ipmi_kcs_wait_for_ibf_clear (ctx) < 0)
        goto cleanup;
    }

  if (_ipmi_kcs_test_if_state (ctx, IPMI_KCS_STATE_IDLE))
    {
      /* Clean up */
      if (_ipmi_kcs_wait_for_obf_set (ctx) < 0)
        goto cleanup;
      _ipmi_kcs_read_byte (ctx); /* toss it, ACK */
    }
  else
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_DRIVER_TIMEOUT);
      goto cleanup;
    }

  if (count > buf_len)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_OVERFLOW);
      goto cleanup;
    }

  if (count > INT_MAX)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_OVERFLOW);
      goto cleanup;
    }

  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  rv = count;
 cleanup:
  if (ctx && ctx->magic == IPMI_KCS_CTX_MAGIC)
    driver_mutex_unlock (ctx->semid);
  return (rv);
}
int
ipmi_kcs_write (ipmi_kcs_ctx_t ctx,
                const void *buf,
                unsigned int buf_len)
{
  const uint8_t *p = buf;
  unsigned int count = 0;
  int lock_flag = 0;

  if (!ctx || ctx->magic != IPMI_KCS_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_kcs_ctx_errormsg (ctx), ipmi_kcs_ctx_errnum (ctx));
      return (-1);
    }

  if (!buf || !buf_len)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_PARAMETERS);
      return (-1);
    }

  if (!ctx->io_init)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_IO_NOT_INITIALIZED);
      return (-1);
    }

  if (!(ctx->flags & IPMI_KCS_FLAGS_NONBLOCKING))
    {
      if (driver_mutex_lock (ctx->semid) < 0)
        {
          KCS_ERRNO_TO_KCS_ERRNUM (ctx, errno);
          goto cleanup;
        }
    }
  else
    {
      if (driver_mutex_lock_interruptible (ctx->semid) < 0)
        {
          KCS_ERRNO_TO_KCS_ERRNUM (ctx, errno);
          goto cleanup;
        }
    }
  lock_flag++;

  if (_ipmi_kcs_wait_for_ibf_clear (ctx) < 0)
    goto cleanup;

  _ipmi_kcs_clear_obf (ctx);

  _ipmi_kcs_start_write (ctx);

  if (_ipmi_kcs_wait_for_ibf_clear (ctx) < 0)
    goto cleanup;

  if (!_ipmi_kcs_test_if_state (ctx, IPMI_KCS_STATE_WRITE))
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_BUSY);
      goto cleanup;
    }

  _ipmi_kcs_clear_obf (ctx);

  /* note we have to save last byte. */
  /* for (buf=data; data+len-1 < buf; buf++) */
  for (; buf_len > 1; buf_len--)
    {
      _ipmi_kcs_write_byte (ctx, *p);

      if (_ipmi_kcs_wait_for_ibf_clear (ctx) < 0)
        goto cleanup;

      if (!_ipmi_kcs_test_if_state (ctx, IPMI_KCS_STATE_WRITE))
        {
          KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_BUSY);
          goto cleanup;
        }

      _ipmi_kcs_clear_obf (ctx);
      p++;
      count++;
    }
  _ipmi_kcs_end_write (ctx);

  if (_ipmi_kcs_wait_for_ibf_clear (ctx) < 0)
    goto cleanup;

  if (!_ipmi_kcs_test_if_state (ctx, IPMI_KCS_STATE_WRITE))
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_BUSY);
      goto cleanup;
    }

  _ipmi_kcs_clear_obf (ctx);

  _ipmi_kcs_write_byte (ctx, *p);

  count++;

#if 0
  if (!_ipmi_kcs_test_if_state (IPMI_KCS_STATE_READ))
    {
      printf ("Not in READ state after writing last byte?\n");
      ipmi_kcs_print_state (ipmi_kcs_get_state ());
      exit (EXIT_FAILURE);
    }
#endif
  
  if (count > INT_MAX)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_OVERFLOW);
      goto cleanup;
    }

  ctx->errnum = IPMI_KCS_ERR_SUCCESS;
  return (count);

 cleanup:
  if (lock_flag)
    driver_mutex_unlock (ctx->semid);
  return (-1);
}
char *
ipmi_kcs_ctx_errormsg (ipmi_kcs_ctx_t ctx)
{
  return (ipmi_kcs_ctx_strerror (ipmi_kcs_ctx_errnum (ctx)));
}
static int
_kcs_cmd_read (ipmi_ctx_t ctx,
	       uint8_t cmd,
	       uint8_t group_extension,
	       fiid_obj_t obj_cmd_rs)
{
  uint8_t *pkt = NULL;
  unsigned int pkt_len;
  int hdr_len, cmd_len, read_len;
  fiid_field_t *tmpl = NULL;
  int ret, rv = -1;
  unsigned int intf_flags = IPMI_INTERFACE_FLAGS_DEFAULT;

  assert (ctx
          && ctx->magic == IPMI_CTX_MAGIC
          && fiid_obj_valid (obj_cmd_rs));

  if (ctx->flags & IPMI_FLAGS_NO_LEGAL_CHECK)
    intf_flags |= IPMI_INTERFACE_FLAGS_NO_LEGAL_CHECK;

  if ((hdr_len = fiid_template_len_bytes (tmpl_hdr_kcs)) < 0)
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }

  if (!(tmpl = fiid_obj_template (obj_cmd_rs)))
    {
      API_FIID_OBJECT_ERROR_TO_API_ERRNUM (ctx, obj_cmd_rs);
      goto cleanup;
    }

  if ((cmd_len = fiid_template_len_bytes (tmpl)) < 0)
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }

  pkt_len = hdr_len + cmd_len;
  
  if (!(pkt = malloc (pkt_len)))
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }

  memset (pkt, '\0', pkt_len);

  if ((read_len = ipmi_kcs_read (ctx->io.inband.kcs_ctx,
                                 pkt,
                                 pkt_len)) < 0)
    {
      API_KCS_ERRNUM_TO_API_ERRNUM (ctx, ipmi_kcs_ctx_errnum (ctx->io.inband.kcs_ctx));
      goto cleanup;
    }
  
  if (!read_len)
    {
      API_SET_ERRNUM (ctx, IPMI_ERR_SYSTEM_ERROR);
      goto cleanup;
    }
  
  if (ctx->flags & IPMI_FLAGS_DEBUG_DUMP && read_len)
    _api_kcs_dump_rs (ctx,
		      pkt,
		      read_len,
		      cmd,
		      ctx->target.net_fn,
		      group_extension,
		      obj_cmd_rs);
  
  if ((ret = unassemble_ipmi_kcs_pkt (pkt,
                                      read_len,
                                      ctx->io.inband.rs.obj_hdr,
                                      obj_cmd_rs,
				      intf_flags)) < 0)
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }

  /* IPMI didn't return enough data back to you */
  if (!ret)
    {
      API_SET_ERRNUM (ctx, IPMI_ERR_IPMI_ERROR);
      goto cleanup;
    }
  
  rv = 0;
 cleanup:
  free (pkt);
  fiid_template_free (tmpl);
  return (rv);
}
static int
_kcs_cmd_write (ipmi_ctx_t ctx,
		uint8_t cmd,
		uint8_t group_extension,
		fiid_obj_t obj_cmd_rq)
{
  uint8_t *pkt = NULL;
  unsigned int pkt_len;
  int hdr_len, cmd_len, send_len, rv = -1;

  assert (ctx
          && ctx->magic == IPMI_CTX_MAGIC
          && fiid_obj_valid (obj_cmd_rq));

  if ((hdr_len = fiid_template_len_bytes (tmpl_hdr_kcs)) < 0)
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }
  
  if ((cmd_len = fiid_obj_len_bytes (obj_cmd_rq)) < 0)
    {
      API_FIID_OBJECT_ERROR_TO_API_ERRNUM (ctx, obj_cmd_rq);
      goto cleanup;
    }
  
  pkt_len = hdr_len + cmd_len;
    
  if (!(pkt = malloc (pkt_len)))
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }
  memset (pkt, '\0', pkt_len);

  if (fill_hdr_ipmi_kcs (ctx->target.lun,
                         ctx->target.net_fn,
                         ctx->io.inband.rq.obj_hdr) < 0)
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }
  
  if ((send_len = assemble_ipmi_kcs_pkt (ctx->io.inband.rq.obj_hdr,
                                         obj_cmd_rq,
                                         pkt,
                                         pkt_len,
					 IPMI_INTERFACE_FLAGS_DEFAULT)) < 0)
    {
      API_ERRNO_TO_API_ERRNUM (ctx, errno);
      goto cleanup;
    }
  
  if (ctx->flags & IPMI_FLAGS_DEBUG_DUMP && send_len)
    _api_kcs_dump_rq (ctx,
		      pkt,
		      send_len,
		      cmd,
		      ctx->target.net_fn,
		      group_extension,
		      obj_cmd_rq);
  
  if (ipmi_kcs_write (ctx->io.inband.kcs_ctx, pkt, send_len) < 0)
    {
      API_KCS_ERRNUM_TO_API_ERRNUM (ctx, ipmi_kcs_ctx_errnum (ctx->io.inband.kcs_ctx));
      goto cleanup;
    }

  rv = 0;
 cleanup:
  free (pkt);
  return (rv);
}