static int
_ipmi_kcs_cmd_read (ipmi_kcs_ctx_t ctx,
                    fiid_obj_t obj_cmd_rs)
{
  uint8_t *pkt = NULL;
  unsigned int pkt_len;
  int hdr_len, cmd_len, read_len, ret, rv = -1;
  fiid_obj_t obj_hdr = NULL;
  fiid_field_t *tmpl = NULL;

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

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

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

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

  if (!(obj_hdr = fiid_obj_create (tmpl_hdr_kcs)))
    {
      KCS_ERRNO_TO_KCS_ERRNUM (ctx, errno);
      goto cleanup;
    }

  pkt_len = hdr_len + cmd_len;

  if (!(pkt = (uint8_t *)malloc (pkt_len)))
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_OUT_OF_MEMORY);
      goto cleanup;
    }

  if ((read_len = ipmi_kcs_read (ctx,
                                 pkt,
                                 pkt_len)) < 0)
    goto cleanup;

  if (!read_len)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_SYSTEM_ERROR);
      goto cleanup;
    }

  if ((ret = unassemble_ipmi_kcs_pkt (pkt,
                                      read_len,
                                      obj_hdr,
                                      obj_cmd_rs,
                                      IPMI_INTERFACE_FLAGS_DEFAULT)) < 0)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_INTERNAL_ERROR);
      goto cleanup;
    }

  /* IPMI didn't return enough data back to you */
  if (!ret)
    {
      KCS_SET_ERRNUM (ctx, IPMI_KCS_ERR_IPMI_ERROR);
      goto cleanup;
    }

  rv = 0;
 cleanup:
  fiid_template_free (tmpl);
  fiid_obj_destroy (obj_hdr);
  free (pkt);
  return (rv);
}
static int
_ssif_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_ssif_read (ctx->io.inband.ssif_ctx, pkt, pkt_len)) < 0)
    {
      API_SSIF_ERRNUM_TO_API_ERRNUM (ctx, ipmi_ssif_ctx_errnum (ctx->io.inband.ssif_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_ssif_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);
}
int
ipmi_ssif_cmd_api (ipmi_ctx_t ctx,
		   fiid_obj_t obj_cmd_rq,
		   fiid_obj_t obj_cmd_rs)
{
  API_ERR_CTX_CHECK (ctx && ctx->magic == IPMI_CTX_MAGIC);

  API_ERR_PARAMETERS (fiid_obj_valid(obj_cmd_rq)
                      && fiid_obj_valid(obj_cmd_rs));

  API_FIID_OBJ_PACKET_VALID(obj_cmd_rq);

  API_ERR_INTERNAL_ERROR(ctx->type == IPMI_DEVICE_SSIF);

  {
    uint8_t *pkt;
    uint32_t pkt_len;
    int32_t hdr_len, cmd_len;

    API_FIID_TEMPLATE_LEN_BYTES(hdr_len, tmpl_hdr_kcs);
    API_FIID_OBJ_LEN_BYTES (cmd_len, obj_cmd_rq);
    pkt_len = hdr_len + cmd_len;

    pkt = alloca (pkt_len);
    memset (pkt, 0, pkt_len);
    API_ERR (pkt);

    API_ERR (fill_hdr_ipmi_kcs (ctx->lun,
				ctx->net_fn,
				ctx->io.inband.rq.obj_hdr) == 0);
    API_ERR (assemble_ipmi_kcs_pkt (ctx->io.inband.rq.obj_hdr,
				    obj_cmd_rq,
				    pkt,
				    pkt_len) > 0);

    API_ERR_SSIF (ipmi_ssif_write (ctx->io.inband.ssif_ctx, pkt, pkt_len) != -1);
  }

  {
    uint8_t *pkt;
    uint32_t pkt_len;
    ssize_t read_len;
    int32_t hdr_len, cmd_len;
    fiid_field_t *tmpl = NULL;
    int8_t rv = -1;

    API_FIID_TEMPLATE_LEN_BYTES_CLEANUP(hdr_len, tmpl_hdr_kcs);
    API_FIID_OBJ_TEMPLATE_CLEANUP(tmpl, obj_cmd_rs);
    API_FIID_TEMPLATE_LEN_BYTES_CLEANUP(cmd_len, tmpl);
    pkt_len = hdr_len + cmd_len;

    API_ERR_CLEANUP ((pkt = alloca (pkt_len)));
    memset (pkt, 0, pkt_len);

    API_ERR_SSIF_CLEANUP (!((read_len = ipmi_ssif_read (ctx->io.inband.ssif_ctx, pkt, pkt_len)) < 0));

    if (!read_len)
      API_ERR_SET_ERRNUM_CLEANUP(IPMI_ERR_SYSTEM_ERROR);

    API_ERR_CLEANUP (!(unassemble_ipmi_kcs_pkt (pkt,
						read_len,
						ctx->io.inband.rs.obj_hdr,
						obj_cmd_rs) < 0));

    rv = 0;
  cleanup:
    API_FIID_TEMPLATE_FREE(tmpl);
    if (rv < 0)
      return (rv);
  }

  return (0);
}