/* devChinoLog.c */
/****************************************************************************
 *                         COPYRIGHT NOTIFICATION
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ****************************************************************************/
/* Author : Makoto Tobiyama 19/Jul/2005 */
/* Modification Log:
 * -----------------
 */
#include        <dbFldTypes.h>

#include        "drvNetMpf.h"
#include        "devNetDev.h"

#ifndef EPICS_REVISION
#include <epicsVersion.h>
#endif
#if EPICS_REVISION == 14 && EPICS_MODIFICATION >= 2
#include <epicsExport.h>
#endif

#define CHINO_CMND_LENGTH 8  /* not correct */
#define CHINO_UDP_PORT    11111
#define CHINO_GET_PROTO chino_get_protocol()
#define CHINO_MAX_NDATA 5120
#define CHINO_DATA_OFFSET 3

LOCAL int finsUseSamePortNumber = MPF_SAMEPORT;
LOCAL unsigned short calCRC(unsigned char cd[], int nmax);
LOCAL int TwoRawToVal(unsigned char bu[],int offset, float *res );
LOCAL long chino_parse_link(
			  struct link *,
			  struct sockaddr_in *,
			  int *,
			  void *
			  );
LOCAL int chino_get_protocol(void);
LOCAL void *chino_calloc(int, int, int, int, int);


typedef struct
{
  int      unit;
  int      type;
  int      chan;
  int      bit;
  int      width;
  int      slvA;
  int      cmd;
  int      start;
  int      ndata;
  char    *lopt;
} CHINO_LOG;

LOCAL long chino_config_command(uint8_t *, int *, void *, int, int, int *, CHINO_LOG *, int);
LOCAL long chino_parse_response(uint8_t *, int *, void *, int, int, int *, CHINO_LOG *, int);

int chinoLogUseTcp = 1;


LOCAL int chino_get_protocol(void)
{
  if (chinoLogUseTcp)
    {
      return MPF_TCP;
    }

  return MPF_UDP;
}


LOCAL void *chino_calloc(
		       int unit,
		       int type,
		       int chan,
		       int bit,
		       int width
		       )
{
  CHINO_LOG *d;

  d = (CHINO_LOG *) calloc(1, sizeof(CHINO_LOG));
  if (!d)
    {
      errlogPrintf("devChinoLog: calloc failed\n");
      return NULL;
    }
      
  d->unit   = unit;
  d->type   = type;
  d->chan   = chan;
  d->bit    = bit;
  d->width  = width;

  return d;
}



#include        "devWaveformChinoLog.c"





/*********************************************************************************
 * Link field parser for command/response I/O
 *********************************************************************************/
LOCAL long chino_parse_link(
			  struct link *plink,
			  struct sockaddr_in *peer_addr,
			  int *option,
			  void *device
			  )
{
  char *protocol = NULL;
  char *unit  = NULL;
  char *type  = NULL;
  char *addr  = NULL;
  char *route = NULL;
  char *lopt  = NULL;
  CHINO_LOG *d = (CHINO_LOG *) device;

  if (parseLinkPlcCommon(
			 plink,
			 peer_addr,
			 &protocol,
			 &route, /* dummy */
			 &unit,
			 &type,
			 &addr,
			 &lopt
			 ))
    {
      errlogPrintf("devChinoLog: illeagal input specification\n");
      return ERROR;
    }

  if (sscanf(unit,"%x",&d->slvA) !=1)
    {
      errlogPrintf("devChinoLog :cannot get slave address\n");
    }
  if (sscanf(type,"%x",&d->cmd) !=1)
    {
      errlogPrintf("devChinoLog :cannot get command\n");
    }
  if (sscanf(addr,"%d",&d->start) != 1)
    {
      errlogPrintf("devChinoLog : cannot get start address\n");
    }

  return OK;
}





/******************************************************************************
 * Command constructor for command/response I/O
 ******************************************************************************/
LOCAL unsigned short calCRC(unsigned char cd[], int nmax)
{
  int i,j;
  unsigned short iCRC;
  unsigned short iCY,iP;
  unsigned char iC1,iC2;
                                                                                
  iCRC = 0xffff;
                                                                                
  for (i=0;i<nmax;i++)
    {
      iCRC = iCRC ^ cd[i];
      for (j=1; j<=8; j++)
        {
          iCY = iCRC & 0x1;
          if ((iCRC & 0x8000) == 0x8000)
            {
              iP = 0x4000;
              iCRC = iCRC & 0x7fff;
            }
          else
            {
              iP = 0x0;
            }
          iCRC = iCRC>>1;
          iCRC = iCRC | iP;
          if (iCY == 1){
            iCRC = iCRC ^ 0xa001;
          }
        }
    }
  if ((iCRC & 0x8000) == 0x8000)
    {
      iP = 0x80;
      iCRC = iCRC & 0x7fff;
    }
  else
    {
      iP = 0;
    }
  iC1 = iCRC & 0xff;
  iC2 = ((iCRC & 0x7f00) >>8) | iP;
                                                                                
  return iC1*256 + iC2;
                                                                                
}

LOCAL long chino_config_command(
			      uint8_t *buf,    /* driver buf addr     */
			      int     *len,    /* driver buf size     */
			      void    *bptr,   /* record buf addr     */
			      int      ftvl,   /* record field type   */
			      int      ndata,  /* n to be transferred */
			      int     *option, /* direction etc.      */
			      CHINO_LOG *d,
			      int      sid
			      )
{
  int nwrite;
  int n;

  int resCRC;

  LOGMSG("devChinoLog: chino_config_command(0x%08x,%d,0x%08x,%d,%d,%d,0x%08x)\n",
	 buf,*len,bptr,ftvl,ndata,*option,d,0,0);

  n = ndata;

  nwrite = isWrite(*option) ? (d->width)*n : 0;

  if (*len < CHINO_CMND_LENGTH + nwrite)
    {
      errlogPrintf("devChinoLog: buffer is running short\n");
      return ERROR;
    }

  buf[ 0] = d->slvA;                    /* slave address */
  buf[ 1] = d->cmd;                       /* command     */
  buf[ 2] = d->start>>8;                /* start address (H)*/
  buf[ 3] = d->start;                   /* start address (L)*/
  buf[ 4] = 0x00;                      /* number (H) */
  buf[ 5] = 0x18;                      /* number (L) 0x0c */
 
  resCRC = calCRC(buf,6);

  buf[ 6] = resCRC>>8;                     /* crc (H) */
  buf[ 7] = resCRC     ;                  /* crc (L) */

  /*  errlogPrintf("devChinoLog: command = %x %x %x %x %x %x %x %x\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); */
  *len = CHINO_CMND_LENGTH;

  /*  if (isWrite(*option))
    {
      if (fromRecordVal(
			&buf[CHINO_CMND_LENGTH],
			d->width,
			bptr,
			d->noff,
			ftvl,
			n,
			CHINO_NEEDS_SWAP
			))
	{
	  errlogPrintf("devChinoLog: illeagal command\n");
	  return ERROR;
	}
    }
  */
  /*
  nread  = isRead(*option)? (d->width)*n:0;
  return (CHINO_DATA_OFFSET + nread);
  */
  return 0;
}




/*******************************************************************************
 * Response parser for command/response I/O
 *******************************************************************************/
LOCAL int TwoRawToVal(unsigned char bu[],int offset, float *res )
{

  float f1;
  
  if ((bu[offset+2] & 0x20) == 0x20){
    f1 = bu[offset]*256 + bu[offset+1];
      }
  else if ((bu[offset] & 0x80) == 0x80){
    f1 = (bu[offset]-255)*256 +(bu[offset+1]-255);
  }
  else{
    f1 = bu[offset]*256+bu[offset+1];
  }

  switch (bu[offset+3] & 0x0f)
    {
    case 0:
      *res = f1;
      break;
    case 1:
      *res = f1*0.1;
      break;
    case 2:
      *res = f1*0.01;
      break;
    case 3:
      *res = f1*0.001;
      break;
    case 4:
      *res = f1*0.0001;
      break;
    default:
      return -1;
    }

  return (bu[offset+3] & 0xf0);
} 


LOCAL long chino_parse_response(
			      uint8_t *buf,    /* driver buf addr     */
			      int     *len,    /* driver buf size     */
			      void    *bptr,   /* record buf addr     */
			      int      ftvl,   /* record field type   */
			      int      ndata,  /* n to be transferred */
			      int     *option, /* direction etc.      */
			      CHINO_LOG *d,
			      int      sid
			      )
{
  int i;
  int ret;
  float temp[1000],*rawVal,*ptemp;

  rawVal = bptr;

  LOGMSG("devChinoLog: chino_parse_response(0x%08x,%d,0x%08x,%d,%d,%d,0x%08x)\n",
	 buf,len,bptr,ftvl,ndata,*option,d,0,0);
 
  /*  for (i=0;i<12;i++)
    {
       errlogPrintf("devChinoLog buffer %d = %x %x %x %x\n",i*4,buf[CHINO_DATA_OFFSET+i*4],buf[CHINO_DATA_OFFSET+i*4+1],buf[CHINO_DATA_OFFSET+i*4+2],buf[CHINO_DATA_OFFSET+i*4+3]);
       } */
  if (isRead(*option))
    {
      for (i=0;i<12;i++)
	{
	  ret = TwoRawToVal(buf,CHINO_DATA_OFFSET+i*4,&temp[i]);
	  if (ret != 0){
	    errlogPrintf("devGhinoLog Emergency code for %d =%x\n",i,ret);
	  }
	}
    }
  
  ndata = 12;
  ptemp = temp;
  i= 12;
  while (i--)
    {
      *rawVal++ = (float) *ptemp ++;
    }
  ret = 0;

  return ret;
}