/*M*
//  PVCS:
//      $Workfile:   ipmignu.c  $
//      $Revision:   1.0  $
//      $Modtime:   21 Oct 2004 15:31:14  $
//      $Author:   arcress at users.sourceforge.net  $  
//
//  This implements support for the GNU FreeIPMI interface.
//  The latest FreeIPMI source is at http://www.gnu.org/software/freeipmi/.
//  See also ../freeipmi/README.
// 
//  10/21/03 ARC - created.
//  10/26/04 ARC - kcs path working
//  10/29/04 ARC - lan path working
//  11/10/04 ARC - use MD5 instead of MD2 for ipmi_lan,
//		   changes for comp code in _lan_cmd & _kcs_cmd,
//                 added sig_timeout routine
//  11/11/04 ARC - added DEBUG_LIB flag
//  11/23/04 ARC - added ipmi_lan ping before open_session, 
//		   added connect_state
//  03/03/05 ARC - fix MAX length for IPMI 2.0
//  05/25/05 ARC - pass lun & sa down to _kcs_cmd & _lan_cmd (for fru cmds)
//  07/07/06 ARC - changed to work with freeipmi-0.2.2 lib for inband.
//                 outofband is coded but not used due to BUILTIN_LAN.
//                 #define OLDLIB if using freeipmi-0.1.3
//  07/27/06 ARC - include ipmignustub.c here via GNU_STUB flag
//  08/08/06 ARC - fix: set sresp even if errors in ipmicmd_gnu
//  01/11/07 ARC - default to GNU_STUB, use HAVE_GNU to include gnu
//
//  NOTE: ipmicmd.o must be linked before libfreeipmi.so to avoid a 
//  conflict with ipmi_cmd().  (New issue with freeipmi 0.2.3.)
//  If we ever want to re-enable GNU freeipmi, we must resolve the
//  naming conflict between our ipmi_cmd() and freeipmi's ipmi_cmd().
 *M*/
/*----------------------------------------------------------------------*
The BSD License 

Copyright (c) 2002, Intel Corporation
Copyright (c) 2009 Kontron America, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:

  a.. Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer. 
  b.. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution. 
  c.. Neither the name of Intel Corporation nor the names of its contributors 
      may be used to endorse or promote products derived from this software 
      without specific prior written permission. 

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *----------------------------------------------------------------------*/

#if !defined(HAVE_GNU)
/****************************************/
/* ipmignustub.c                        */
/****************************************/
#define uchar    unsigned char
#define ushort   unsigned short
#define MAX_LEN_RQ   	25
#define MAX_LEN_RS   	100

int ipmi_open_gnu(char *node, char *user, char *pswd, int fdebuglan)
{
   return -1;
}
 
int ipmi_close_gnu(char *node)
{
   return -1;
}
 
int ipmicmd_gnu(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa,
		uchar *pdata, uchar sdata, uchar *presp, int *sresp, 
		uchar *pcc, char fdebugcmd)
{
   int rv = -1;
   /* check sdata/sresp against MAX_ */
   if (sdata > MAX_LEN_RQ)  return(-5);
   if (*sresp > MAX_LEN_RS)  return(-5);
   return(rv);
}

int ipmi_cmdraw_gnu(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa,
		uchar bus, uchar *pdata, uchar sdata, uchar *presp, int *sresp, 
		uchar *pcc, char fdebugcmd)
{
   return(-1);
}

int ipmi_cmd_gnu(char *node, ushort cmd, uchar *pdata, uchar sdata, 
	 	uchar *presp, int *sresp, uchar *pcc, char fdebugcmd)
{
   return (-1);
}

#else
/**********************************************************/
/* not stub, HAVE_GNU is defined, use freeipmi library   */
/**********************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <signal.h>

#include <freeipmi/freeipmi.h>
#include "ipmicmd.h"  /* for uchar, CMDMASK, NCMDS */

// #define uchar      unsigned char
// #define CMDMASK         0xff
#define MAXHOSTNAMELEN    64
#define RQ_HDR   	2
#define MAX_LEN_RQ   	25
#define MAX_LEN_RS   	100
// #define DEBUG_LIB   1
// #define OLDLIB   1   /* freeipmi.0 (0.1.3) library */

#ifdef GNU_LAN
#define gnu_signal(sig, rtn)   signal(sig, rtn)
#else
#define gnu_signal(sig, rtn)   /*ignore*/
#endif

extern FILE *fperr;  /*defined in ipmicmd.c*/
extern FILE *fpdbg;  /*defined in ipmicmd.c*/
extern int get_cmd_rslen(uchar cmd, uchar netfn);  /* in ipmicmd.c */
extern ipmi_cmd_t ipmi_cmds[NCMDS];
#ifdef __IA64__
int kcs_port = 0x8a2;  
int ssif_port = 0x042; /* SSIF: I2C Slave Address 0x42 (<< 1 = 0x84) */
#else
int kcs_port = 0xca2;  /* KCS:  use 0xca2 for ia32, 0x8a2 for ia64 */
                       /*       some use 0xca8/0xcac w register spacing */
		       /* SMIC: use 0xca9 & 0x8a9 */
int ssif_port = 0x042; /* SSIF: I2C Slave Address 0x42 (<< 1 = 0x84) */
#endif
int gnu_retry_wait_time = 1;
int gnu_retry_attempts  = 3; /* was 5, now 3 */
/* 
 * These variables pertain to ipmi_lan, for the node given at open time.
 * The assumption here is that these are utilities, so no more than one
 * node will be open at a given time.  
 * See also gnode, guser, gpswd in ipmicmd.c
 */
static char nodename[33] = "";
static int sockfd = 0;
static char _dest[MAXHOSTNAMELEN+1];
static char _dest_ip[INET_ADDRSTRLEN+1];
static int  _destaddr_len = 0;
static struct sockaddr_in _destaddr;
static struct sockaddr_in _srcaddr;
static uchar 	auth_type = 0;
static char *authcode = NULL;
static int authcode_len = 0;
static u_int32_t session_id = 0;
static u_int32_t session_seqnum = 0x01;   /* inbound */
static u_int32_t rq_seq = 1;		  /* outbound */
static int ipmi_timeout = 10;  /* 10 sec */
static int connect_state = 0;   
	/*  0 = init, 1 = socket() complete, 2 = bind/gethost complete,
	 *  3 = ping sent, 4 = pong received, 5 = session activated.  */
static char *conn_state_str[6] = {
	"init state", "socket complete", "gethost complete", 
	"ping sent", "pong received", "session activated" };
#ifdef OLDLIB
int polltime = IPMI_KCS_SLEEP_USECS;  /*=0x01*/
#else
ipmi_device_t *pdev = NULL;
ipmi_device_t  gnudev;
char  *driver_device = NULL;
#endif

#ifdef DEBUG_LIB
#ifdef OLDLIB
extern int fdebug_ipmilan;
#else
static int fdebug_ipmilan;
#endif
#endif


#define GERR_INVPARAM   -8  /*null pointers, etc. */
#define GERR_NOTSUPPORT -7  /*slave address != 0x20, support later? */
#define GERR_BADLENGTH  -5  /*length > MAX */
#define GERR_TIMEOUT    -4
#define GERR_ABORT      -3  /*abort signal recvd */
#define GERR_CONNECT    -2  /*problem connecting to BMC*/
#define GERR_OTHER      -1

#define BMC_SA    0x20  /* slave address of BMC */

static void
sig_timeout(int sig)
{
   alarm(0);
   signal(SIGALRM,SIG_DFL);
   printf("ipmignu_cmd timeout, after %s\n",conn_state_str[connect_state]);
   exit(GERR_TIMEOUT);
}

static void
sig_abort(int sig)
{
   static int sig_aborting = 0;
   uchar buf_rs[4]; 
   fiid_obj_t cmd_rs;
   int rv;

   if (sig_aborting == 0) {
     sig_aborting = 1;
     if (sockfd != 0) {  /* socket is open */
	  if (session_id != 0) {  /* session is open */
  	    cmd_rs = buf_rs;
	    rv = ipmi_lan_close_session(sockfd, (struct sockaddr *)&_destaddr, 
			  _destaddr_len, auth_type, session_seqnum, session_id,
			  authcode, authcode_len, rq_seq, session_id, cmd_rs);
	  }
	  close(sockfd);
     }
     signal(SIGINT,SIG_DFL);
     printf("ipmignu_cmd interrupt, after %s\n", conn_state_str[connect_state]);
     exit(GERR_ABORT);
   }  /*endif*/
} /*end sig_abort*/


#ifdef OLD
int nodeislocal(char *nodename);
{
   if (nodename == NULL) return 1;
   if (nodename[0] == 0) return 1;
   if (strcmp(nodename,"localhost") == 0) return 1;
   return 0;
}
#else
/* extern int nodeislocal(char *nodename);  *in ipmicmd.h*/
#endif

/* signal handlers + sleep(3) is a bad idea */
static int 
_sleep(unsigned int sleep_len)
{
  struct timeval tv;
  
  if (sleep_len == 0)
    return 0;

  tv.tv_sec = sleep_len;
  tv.tv_usec = 0;
  if (select(1, NULL, NULL, NULL, &tv) < 0)
    {
      if (errno != EINTR) return(errno);
    }
  return 0;
}

#ifdef OLDLIB
static int
_kcs_cmd(char *str, int retry_wait_time, int retry_attempt, 
     u_int8_t netfn, u_int8_t lun, u_int8_t sa,
     fiid_obj_t cmd_rq, fiid_template_t tmpl_rq,
     fiid_obj_t cmd_rs, fiid_template_t tmpl_rs, int fdebugcmd)
{
  u_int64_t comp_code;
  int retry_count = 0;
  int rv = 0;

  cmd_rs[1] = 0;  /*initialize completion code*/
  if (str == NULL || retry_wait_time < 0 || retry_attempt < 0 ||
      cmd_rq == NULL || tmpl_rq == NULL || cmd_rs == NULL || tmpl_rs == NULL)
  	return(GERR_INVPARAM);

  if (sa != BMC_SA) return(GERR_NOTSUPPORT);
  while (1)
  {
      alarm(ipmi_timeout);
      rv = ipmi_kcs_cmd_interruptible(kcs_port, lun, netfn, 
				cmd_rq, tmpl_rq, cmd_rs, tmpl_rs);
      alarm(0);
      if (rv < 0) {
          if (errno != EAGAIN && errno != EBUSY) {
	      if (fdebugcmd)
		  fprintf(fperr,"%s: ipmi_kcs_cmd_interruptible: %s \n", 
			str, strerror(errno));
              return (GERR_CONNECT);
          } else {
              if (retry_count >= retry_attempt) {
	 	if (fdebugcmd)
                  fprintf(fperr,"%s: ipmi_kcs_cmd_interruptible: BMC too busy: "
                          "retry_wait_time=%d, retry_attempt=%d", 
                          str, retry_wait_time, retry_attempt);
                return (GERR_CONNECT);
              }
              rv = _sleep(retry_wait_time);
              retry_count++;
            }
      } else { /* success, done */
	  break;
      }
  }

  /* could get cc directly from cmd_rs[1] or use IPMI_COMP_CODE(cmd_rs) */
  // could use _FIID_OBJ_GET(cmd_rs, tmpl_rs, "comp_code", &comp_code, str);
  comp_code = IPMI_COMP_CODE(cmd_rs);

  if (rv == 0 && comp_code != IPMI_COMMAND_SUCCESS) {
	if (fdebugcmd)
	   fprintf(fperr,"%s: cmd error: 0x%x\n", str, comp_code);
	rv = comp_code;
  } 

  return (rv);
}  /*end _kcs_cmd()*/


static int
_glan_cmd(char *str, struct sockaddr *hostaddr, int hostaddr_len, 
     u_int8_t netfn, u_int8_t lun, u_int8_t sa,
     fiid_obj_t cmd_rq, fiid_template_t tmpl_rq,
     fiid_obj_t cmd_rs, fiid_template_t tmpl_rs, int fdebugcmd)
{
  int rv = 0;
  uchar cc;

  /* Note: str = nodename.  Could be used if more than one node. */
  if (str == NULL || hostaddr == NULL ||
      cmd_rq == NULL || tmpl_rq == NULL || cmd_rs == NULL || tmpl_rs == NULL)
  	return(GERR_INVPARAM);;

  if (sa != BMC_SA) return(GERR_NOTSUPPORT);
  memset(&cmd_rs[1],0,4);  /*init minimal recv data*/
  if (fdebugcmd) fprintf(fperr,"_glan_cmd[%x]: rq_seq = %d\n",
			 cmd_rq[0],rq_seq);
  alarm(ipmi_timeout);
  rv = ipmi_lan_cmd(sockfd, hostaddr, hostaddr_len, 
		    auth_type, session_seqnum, session_id,  
		    authcode, authcode_len, 
		    netfn, lun, rq_seq,
		    cmd_rq, tmpl_rq, cmd_rs, tmpl_rs);
  alarm(0);
  cc = cmd_rs[1];
  if (fdebugcmd) fprintf(fperr,"_glan_cmd[%x]: rv = %d, cc=%x, errno = %d\n",
			 cmd_rq[0],rv,cc,errno);
  if (rv == 0 && cc != 0) {     /* completion code error */
	rv = cc;
  	if (fdebugcmd) {
		fprintf(fperr,"cmd_rq: %02x %02x %02x %02x \n",
			cmd_rq[0],cmd_rq[1], cmd_rq[2],cmd_rq[3]);
		fprintf(fperr,"cmd_rs: %02x %02x %02x %02x \n",
			cmd_rs[0],cmd_rs[1],cmd_rs[2],cmd_rs[3]);
	}
  }

  {    /* increment seqnum - even if error */
	rq_seq++;
        rq_seq = (rq_seq % (IPMI_LAN_SEQ_NUM_MAX + 1));
	session_seqnum++;
  }
  
  return (rv);
}  /*end _glan_cmd()*/
#endif

static int get_iftype(void)
{
   char *if_file = "/var/lib/ipmiutil/ipmi_if.txt";
   char *if_file2 = "/usr/share/ipmiutil/ipmi_if.txt";
   FILE *fp;
   char line[80];
   char *p;
   int t; 
   /* Only care about Linux since freeipmi is not used for Windows. */
   t = IPMI_DEVICE_KCS;
   fp = fopen(if_file,"r");
   if (fp != NULL) fp = fopen(if_file2,"r");
   if (fp != NULL) {
      p = fgets(line,sizeof(line),fp);
      if (p != NULL) {
         if (strstr(line,"SSIF") != NULL) 
             t = IPMI_DEVICE_SSIF;
      }
      fclose(fp);
   }
   return(t);
}

int ipmi_open_gnu(char *node, char *user, char *pswd, int fdebuglan)
{
   char *username;
   uchar priv_level;
   int rv = -1;
   uchar asf_pkt[40] = {06,0,0xFF,06,0,0,0x11,0xBE,0x80,1,0,0 };
   struct sockaddr from_addr;
   int from_len;
   char *ifstr;
   int iftype;
   int port;

#ifdef DEBUG_LIB
   if (fdebuglan) fdebug_ipmilan = 1;
#endif
   if (nodeislocal(node)) {
	gnu_signal(SIGALRM,sig_timeout);
#ifdef OLDLIB
	/* TODO: Get kcs port. Could check at runtime for ia64 (getenv/uname)*/
	/* Open kcs interface */
        port = kcs_port;
	rv = ipmi_kcs_io_init(kcs_port,polltime);
#else
        iftype = get_iftype();
        if (iftype == IPMI_DEVICE_SSIF) {
           ifstr = "ssif";
           driver_device = "/dev/i2c-0";
           port = ssif_port;
           memset (&gnudev, 0, sizeof (ipmi_device_t));
           rv = ipmi_open_inband (&gnudev,
                               1,      /* disable_auto_probe, */
                               IPMI_DEVICE_SSIF,
                               port,   /*args->common.driver_address,*/
                               0,      /* reg_space */
                               driver_device, /* driver_device, */
                               IPMI_MODE_DEFAULT);
           if (fdebuglan && rv != 0) perror("ipmi_open_inband(ssif)");
        } else  { // (iftype == IPMI_DEVICE_KCS) 
           ifstr = "kcs";
           driver_device = NULL;
           port = kcs_port;
           memset (&gnudev, 0, sizeof (ipmi_device_t));
           rv = ipmi_open_inband (&gnudev,
                               0,      /* disable_auto_probe, */
                               IPMI_DEVICE_KCS,
                               port,   /*args->common.driver_address,*/
                               0,      /* reg_space */
                               driver_device, /* driver_device, */
                               IPMI_MODE_DEFAULT);
           if (fdebuglan && rv != 0) perror("ipmi_open_inband(kcs)");
        } 
#endif
	if (fdebuglan) 
	   fprintf(fperr,"ipmi_open_gnu: open_inband(%s port %x) rv = %d\n",
		   ifstr,port,rv);
	if (rv == 0) connect_state = 5;
   } else {
	struct hostent *hptr;
	char *temp;

	fprintf(fperr,"Opening connection to node %s ...\n",node);
	/* save nodename for sig_abort later */
	if (strlen(node) > 32) {
	       strncpy(nodename, node, 32); nodename[32] = 0;
	} else strcpy(nodename, node);

	/* Open lan interface */
	rv = socket(AF_INET, SOCK_DGRAM, 0); 
	if (rv < 0) return (rv);
	else sockfd = rv;

	connect_state = 1;  
	memset(&_srcaddr, 0, sizeof(_srcaddr));
	_srcaddr.sin_family = AF_INET;
	_srcaddr.sin_port = htons(0);
	_srcaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	rv = bind(sockfd, (struct sockaddr *)&_srcaddr, sizeof(_srcaddr));
	if (rv < 0) return (rv);

	memset(&_destaddr, 0, sizeof(_destaddr));
	_destaddr.sin_family = AF_INET;
	_destaddr.sin_port = htons(RMCP_PRI_RMCP_PORT);  /*0x26f = 623.*/
	if ((hptr = gethostbyname(node)) == NULL) {
		printf("gnu, gethostbyname(%s): %s\n", node,hstrerror(errno));
		return(errno);
	}
	_destaddr.sin_addr = *((struct in_addr *)hptr->h_addr);
	_destaddr_len = sizeof(_destaddr);

	temp = inet_ntoa(_destaddr.sin_addr);
	strncpy(_dest_ip, temp, INET_ADDRSTRLEN);
	_dest_ip[INET_ADDRSTRLEN] = 0;

	/* Set up signals to handle errors & timeouts. */
	gnu_signal(SIGINT,sig_abort);
	gnu_signal(SIGALRM,sig_timeout);

	connect_state = 2;
	/* Send RMCP ASF ping to verify IPMI LAN connection. */
	asf_pkt[9] = 1; /*tag*/	
	rv = ipmi_lan_sendto(sockfd, asf_pkt, 12, 0,
			(struct sockaddr *)&_destaddr, _destaddr_len);
	if (fdebuglan) {
	    if (rv < 0) perror("gnu:ipmi_lan_sendto");
	    fprintf(fperr,"gnu:ipmi_lan ping, sendto rv=%d\n",rv);
        }
	if (rv < 0) return(GERR_CONNECT);
	connect_state = 3;

	from_len = sizeof(struct sockaddr);
	alarm(ipmi_timeout);
	rv = ipmi_lan_recvfrom(sockfd, asf_pkt, sizeof(asf_pkt), 0,
			       &from_addr,&from_len);
	alarm(0);
	if (fdebuglan) {
	    if (rv < 0) perror("gnu:ipmi_lan_recvfrom");
	    fprintf(fperr,"gnu:ipmi_lan pong, recvfrom rv=%d errno=%d\n", 
			rv, errno);
        }
	if (rv < 0) return(GERR_CONNECT);
	connect_state = 4;

	{
		auth_type  = gauth_type;
		priv_level = gpriv_level;
		username = user;
		authcode = pswd;
		authcode_len = (pswd) ? strlen(authcode) : 0;
	}
#ifdef OLDLIB
	alarm(ipmi_timeout);
	rv = ipmi_lan_open_session(sockfd, (struct sockaddr *)&_destaddr, 
			_destaddr_len, auth_type, username, 
			authcode, authcode_len, 0x01, priv_level, 
			&session_seqnum, &session_id);
	alarm(0);
#else
        close(sockfd);
        printf("before open_outofband\n");  /*++++ error in new call*/
        rv = ipmi_open_outofband (&gnudev,
                               IPMI_DEVICE_LAN,
                               IPMI_MODE_DEFAULT,
                               gnu_retry_wait_time, /*retry_timeout*/
                               gnu_retry_attempts,  /*packet_retry_max*/
                               (struct sockaddr *) &_destaddr,
                               sizeof (struct sockaddr),
                               auth_type,
                               username, authcode, /*password*/
                               priv_level);
        printf("after open_outofband, rv = %d\n",rv); /*++++ error in new call*/
	if (fdebuglan && rv != 0) 
           perror("gnu:ipmi_open_outofband");
#endif
	if (rv == 0) { /* successful (session active) */
	   session_seqnum++;  /*increment session sequence number */
	   connect_state = 5; /* bump connection state to active */
	} 
	else fprintf(fperr,"gnu:ipmi_lan_open_session error, rv = %d\n",rv);
   }
#ifndef OLDLIB
   if (rv == 0) pdev = &gnudev;
#endif
   if (fdebuglan) fprintf(fperr,"ipmi_open_gnu rv=%d\n",rv);
   return(rv);
}

int ipmi_close_gnu(char *node)
{
   int rv;

#ifdef OLDLIB
   uchar buf_rs[4];  /* tmpl_cmd_set_session_priv_level_rs is 3 bytes */
   fiid_obj_t cmd_rs;

   /* use node for matching */
   if (!nodeislocal(node)) {  /* ipmi_lan, need to close & cleanup */
	if (sockfd != 0) {  /* socket is open */
	  if (session_id != 0) {  /* session is open */
  	    cmd_rs = buf_rs;
	    rv = ipmi_lan_close_session(sockfd, (struct sockaddr *)&_destaddr, 
			  _destaddr_len, auth_type, session_seqnum, session_id,
			  authcode, authcode_len, rq_seq, session_id, cmd_rs);
	  }
	  alarm(0);
	  gnu_signal(SIGALRM,SIG_DFL);
	  gnu_signal(SIGINT,SIG_DFL);
	  close(sockfd);  /*close lan socket */
	}
   } else {  /* kcs cleanup */
	alarm(0);
	gnu_signal(SIGALRM,SIG_DFL);
   }
#else 
   if (pdev != NULL) {
       ipmi_close(pdev);
       pdev = NULL;
   }
   alarm(0);
   gnu_signal(SIGALRM,SIG_DFL);
   gnu_signal(SIGINT,SIG_DFL);
#endif
   return (rv);
}
 
int ipmicmd_gnu(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa,
		uchar *pdata, uchar sdata, uchar *presp, int *sresp, 
		uchar *pcc, char fdebugcmd)
{
   uchar cc;
   int i,j;
   int rlen; 
   int rv = -1;

#ifdef OLDLIB
   fiid_obj_t cmd_rq = NULL;
   fiid_obj_t cmd_rs = NULL;
   fiid_field_t tmpl_cmd_rq[MAX_LEN_RQ+3] = {
       {8, "cmd"}, {0,""} };
   fiid_field_t tmpl_cmd_rs[MAX_LEN_RS+4] = {
       {8, "cmd"}, {8, "comp_code"}, {0,""} };

   /* check sdata/sresp against MAX_ */
   if (sdata > MAX_LEN_RQ)  return(GERR_BADLENGTH);
   if (*sresp > MAX_LEN_RS) return(GERR_BADLENGTH);
   rlen = *sresp;

   /* fill in mock tmpl_cmd_rq */
   for (i=0; i< (MAX_LEN_RQ+1); i++) {
	j = i+1;
	if (i < sdata) {
		tmpl_cmd_rq[j].len = 8; 
		sprintf(tmpl_cmd_rq[j].key,"data%02d",i+1);
	} else {
		tmpl_cmd_rq[j].len = 0; 
		strcpy(tmpl_cmd_rq[j].key,"");
		break;
	}
   }
   /* fill in mock tmpl_cmd_rs */
   for (i=0; i<(MAX_LEN_RS+1); i++) {
	j = i+2;
	if (i < rlen) {
		tmpl_cmd_rs[j].len = 8; 
		sprintf(tmpl_cmd_rs[j].key,"data%02d",i+1);
	} else {
		tmpl_cmd_rs[j].len = 0; 
		strcpy(tmpl_cmd_rs[j].key,"");
		break;
	}
   }
   
   /* Allocate cmd_rq and cmd_rs */
   // See also cmd_rq = ipmi_xcalloc (fiid_obj_len_bytes (tmpl), 1);
   if ((cmd_rq = fiid_obj_alloc(tmpl_cmd_rq)) == NULL) {
      if (fdebugcmd) 
	fprintf(fperr,"ipmicmd_gnu: fiid_obj_alloc: %s", strerror(errno));
      goto cleanup;
   }
   if ((cmd_rs = fiid_obj_alloc(tmpl_cmd_rs)) == NULL) {
      if (fdebugcmd)
	fprintf(fperr,"ipmicmd_gnu: fiid_obj_alloc: %s", strerror(errno));
      goto cleanup;
   }

   /* fill in the IPMI command and data here */
   cmd_rq[0] = cmd;
   memcpy(&cmd_rq[1],pdata,sdata);
 
   if (nodeislocal(node)) {  /*local, use kcs*/
	rv = _kcs_cmd("Gnu KCS", gnu_retry_wait_time, gnu_retry_attempts, 
		netfn, lun, sa, cmd_rq, tmpl_cmd_rq,
                cmd_rs, tmpl_cmd_rs,fdebugcmd);
   } else { /* ipmi_lan */
	rv = _glan_cmd(node, (struct sockaddr *)&_destaddr, _destaddr_len,
		netfn, lun, sa, cmd_rq, tmpl_cmd_rq,
                cmd_rs, tmpl_cmd_rs,fdebugcmd);
   }

   cc = cmd_rs[1];
   rlen = get_cmd_rslen(cmd,netfn);  /* get expected response data size */
   if (fdebugcmd) 
	fprintf(fperr,"ipmicmd_gnu: cmd = %02x rv = %d, cc = %02x, rlen = %d\n",
		cmd,rv,cc,rlen);
   if (rv == 0 && cc == 0) {  /* success */
	if (fdebugcmd) {
	   fprintf(fperr,"gnu cmd_rs(len=%d): ",rlen);
	   for (i=0; i<rlen+2; i++) fprintf(fperr,"%02x ",cmd_rs[i]);
	   fprintf(fperr,"\n");
	}
	memcpy(presp,&cmd_rs[2],rlen);
	*sresp = rlen;
   } else {  /* error */
	*sresp = 0;
	memset(presp,0,rlen);
   }
   *pcc = cc;
 
cleanup:
   fiid_obj_free(cmd_rq);
   fiid_obj_free(cmd_rs);

#else
   uchar psend[MAX_LEN_RQ+RQ_HDR];
   uchar precv[MAX_LEN_RS+3];
   int ssend;

   /* check sdata/sresp against MAX_ */
   if (sdata > MAX_LEN_RQ)  return(GERR_BADLENGTH);
   if (*sresp > MAX_LEN_RS) return(GERR_BADLENGTH);
   rlen = *sresp + 3;

   psend[0] = (netfn << 2) + (lun & 0x03);
   psend[1] = cmd;
   memcpy(&psend[RQ_HDR],pdata,sdata);
   ssend = sdata + RQ_HDR;
   rv = ipmi_cmd_raw (&gnudev, psend, ssend, precv, &rlen);
   if (rv == 0) {
      cc = precv[2];  /* precv format = 0:netfn/lun, 1:cmd, 2:cc, 3+:data  */
      *pcc = cc;
      if (rlen > 3) {
          memcpy(presp,&precv[3],rlen-3);
          *sresp = rlen-3;
      } else *sresp = 0;
      if (fdebugcmd) {
         fprintf(fpdbg,"ipmicmd_gnu ok, len=%d: ",rlen);
         for (i=0; i<rlen; i++) fprintf(fpdbg,"%02x ",precv[i]);
         fprintf(fpdbg,"\n");
      }
   }
   else if (fdebugcmd) 
       fprintf(fpdbg,"ipmicmd_gnu rv = %d\n",rv);
#endif

   return(rv);
}  /*end ipmicmd_gnu()*/

int ipmi_cmdraw_gnu(char *node, uchar cmd, uchar netfn, uchar lun, uchar sa,
		uchar bus, uchar *pdata, uchar sdata, uchar *presp, int *sresp, 
		uchar *pcc, char fdebugcmd)
{
   int rv;
   rv = ipmicmd_gnu(node, cmd, netfn, lun, sa,
		    pdata,sdata,presp,sresp,pcc,fdebugcmd);
   return(rv);
}

int ipmi_cmd_gnu(char *node, ushort cmd, uchar *pdata, uchar sdata, 
	 	uchar *presp, int *sresp, uchar *pcc, char fdebugcmd)
{
    int rc, i;
    for (i = 0; i < NCMDS; i++) {
       if (ipmi_cmds[i].cmdtyp == cmd) break;
    }
    if (i >= NCMDS) {
	fprintf(fperr, "ipmi_cmd_gnu: Unknown command %x\n",cmd);
	return(-1); 
	}
   if (cmd >= CMDMASK) cmd = cmd & CMDMASK;  /* unmask it */

   rc = ipmicmd_gnu(node,cmd,ipmi_cmds[i].netfn,ipmi_cmds[i].lun,
		ipmi_cmds[i].sa, pdata,sdata,presp,sresp,pcc,fdebugcmd);
   return (rc);
}
#endif   /*end-else HAVE_GNU*/

/* end ipmignu.c */
