/***************************************************************************
**
** File name : comm.c
**
**
**
**
**    Copyright (C) 1993 Microtek International, Inc.
**    All Rights Reserved
**
****************************************************************************e

/***************************************************************************
**

**    Include files
**
***************************************************************************/
#include <conio.h>
#include <time.h>
#include <stdlib.h>
#include <windows.h>

#ifndef _COMM_
#include "comm.h"
#endif

#ifndef _ABI_DEF_
#include "abidef.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

#include <string.h>

U16 portAddr, portStatus, portInp;
U16 printerPort;
int portId;
/**************************************************************************
**
** Local define
**
***************************************************************************/

#define HighByte(x)          ((U8)((U16)(x) >> 8))
#define LowByte(x)           ((U8)(x))
#define InPortB(a)           inp(a)
#define OutPortB(a,b)        outp(a,b)

#define   RV_RDY         1   /* status of receive ready is 00000001 */
#define   SD_RDY         2   /* status of send ready is    00000010 */

#define XON_LIMIT 5
#define XOFF_LIMIT 1020

#define P_SWITCH 0
#define P_NORMAL 8
#define P_S_RDY  8
#define P_R_RDY  0x10
#define P_W_PCE  1
#define P_R_PCE  2
#define P_I_MASK 0xb8


/**************************************************************************
**
** Local define
**
***************************************************************************/

#define IO_LOOP   20000


CHAR portName[5][5] = { "","COM1","COM2","COM3","COM4"};
COMSTAT commStat;                /* global communication status */
DCB     comDcb;                  /* global Device Control Block of COM port */
U16 testBaudrate[7] = { 0, CBR_9600, CBR_19200,CBR_38400,
                        CBR_56000, CBR_128000, CBR_56000};

#define INI_FILENAME "micepack.ini"
#define INI_SECTION_NAME "Baud"
#define INI_BDM_SPEED "bdmSpeed"
#define INI_KEY_NAME "Rate"

RETCODE ComError(int curComId, COMSTAT *comStatus);
RETCODE MapComError(S16 commErr);
VOID SendStreamParallel(U8 *stream, U16 len);
VOID SendStreamSerial(U8 *stream, U16 len);
/**************************************************************************
**
** Externals
**
**************************************************************************/

/**************************************************************************
**
** Execution codes
**
**************************************************************************/


/**************************************************************************
**
** Name : chk_hlt
**
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
RETCODE chk_hlt(VOID) {
//BOOLEAN abortFromEsc;
//RETCODE  err;

//vvv   err = TskCheckAbort(&abortFromEsc);
//vvv   if(err!=GOOD) return err;
//vvv   if (abortFromEsc!=0) return ESC;
   return(GOOD);
}

/**************************************************************************
**
** Name : SetComm
**
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
RETCODE SetComm(U16 pData) {
   CHAR buf[20], *endPtr;
   S32 tryRate;
   RETCODE err;
   int i;

   if (pData == 5) pData = 0x378;
   else if (pData == 6) pData = 0x278;
   else if (pData == 7) pData = 0x3bc;  
   else if (pData >= 1 && pData <=  4) {
      CloseComm(pData-1);
      if ((portId=OpenComm(portName[pData],1024,1024)) >= 0) {
         FlushComm(portId,0);
         FlushComm(portId,1);
         if (GetCommState(portId,(DCB FAR *) &comDcb) < 0) {
            err = ComError(portId,&commStat);
            ErrDisplayError(err, FORCE_POPUP);
            CloseComm(portId);
            return(!GOOD);
         }
         GetPrivateProfileString(INI_SECTION_NAME, "LinkRate", "FF1F", buf,
            sizeof(buf), INI_FILENAME);
         tryRate = strtol(buf,&endPtr,16);
         for (i = 1; i < 6; i++) {
            if (tryRate == testBaudrate[i])
               break;
         }
         if (i == 1)
            comDcb.BaudRate = CBR_56000;
         else if ( i < 6)
            comDcb.BaudRate = (U16)(tryRate & 0xFFFF);
         else
            comDcb.BaudRate = CBR_56000;

         comDcb.ByteSize = 8;
         comDcb.Parity = NOPARITY;
         comDcb.StopBits = ONESTOPBIT;
         comDcb.fBinary = 1;
         comDcb.fDtrDisable = 0;
         comDcb.fRtsDisable = 0;
         comDcb.fOutxDsrFlow = 1;
         comDcb.fOutxCtsFlow = 0;
         comDcb.fDtrflow = 1;
         comDcb.fRtsflow = 0;
         comDcb.XoffLim = XOFF_LIMIT;
         comDcb.XonLim = XON_LIMIT;
         comDcb.Id = portId;
         if (SetCommState((DCB FAR *) &comDcb) < 0) {
            err = ComError(portId,&commStat);
            ErrDisplayError(err, FORCE_POPUP);
            CloseComm(portId);
            return(!GOOD);
         }
         printerPort = 2;
         return(GOOD);
      } else {
         err = ComError(portId,&commStat);
         ErrDisplayError(err, FORCE_POPUP);
         CloseComm(portId);
         return(!GOOD);
      }
   }
   else {
      printerPort = 0;
      portAddr = pData;
      portStatus = pData+1;
      return(GOOD);
   }
   printerPort = 1;
   portAddr = pData;
   portStatus = pData + 2;
   portInp = pData + 1;
   OutPortB(portStatus, P_SWITCH);
   return(GOOD);
}

/**************************************************************************
**
** Name : sendB()
**        
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
S16 sendB(U8 txData) {
   U16 i;
   RETCODE err;

   switch(printerPort) {
   case 1:
      OutPortB(portAddr,txData);
      for (i = 0; i < IO_LOOP; i++ )
         if ((!(InPortB(portInp) & P_S_RDY)) && 
             (!(InPortB(portInp) & P_S_RDY))){
            OutPortB(portStatus, (P_SWITCH | P_W_PCE)); // STROBE\ = low SWITCH=high xsq 
            OutPortB(portStatus, P_SWITCH); // STROBE\ = high, SWITCH = high 
            return(GOOD) ;
         }
      break;
   case 0:
      for (i = 0; i < IO_LOOP; i++)
         if (InPortB(portStatus) & SD_RDY) {
            OutPortB(portAddr,txData);
            return(GOOD) ;
         }
      break;
   case 2:
      do {
         err = ComError(portId, &commStat);
      } while (err == ER_COM_TXFULL);
      if (WriteComm(portId,(LPSTR)&txData,1) == 1)
         return(GOOD);
      err = ComError(portId, &commStat);
      ErrDisplayError(err, FORCE_POPUP);
   default:;
   }
   return(-1);
}        /* end of sendB() */

/**************************************************************************
**
** Name :  receiveWait()
**
** Function
**
**    Input  : none
**
**    Output :
**
** Notes:
**
**************************************************************************/
S16 receiveWait(U16 waitSec) {
int rxData;
time_t startTime,currentTime;

   time(&startTime);
   while (1) {
     rxData = receiveB();
     if (rxData != -1) return(rxData);
     chk_hlt();
     time(&currentTime);
     if ((currentTime - startTime) > (time_t)waitSec) return(ICE_REC_TIME_OUT);
   }
}        /* end of receiveWait() */

/**************************************************************************
**
** Name : sendWait()
**
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
int sendWait(U8 txData) {
//   int i;

   while(1) {
      if (sendB(txData) == GOOD) return(GOOD);
      chk_hlt();
   }
}        /* end of sendB() */

/**************************************************************************
**
** Name : receiveB()
**
** Function
**
**    Input  : none
**
**    Output :
**
** Notes:
**
**************************************************************************/
S16 receiveB() {
   U16 i;
   S16 rxData, readNum;
   U8 tmp, tmp1, tmp2;
   RETCODE err;
   DWORD startTime;
   
   switch(printerPort) {
   case 2:
      startTime = GetCurrentTime();
      do {
         err = ComError(portId, &commStat);
         if ((readNum=ReadComm(portId,(LPSTR)&rxData,1)) != 0)
            break;
      } while ((GetCurrentTime() - startTime) <= 60);
      if (readNum == 1) {
         rxData = rxData & 0x00FF;
         err = ComError(portId, &commStat);
      } else {
         err = ComError(portId, &commStat);
         ErrDisplayError(err, FORCE_POPUP);
         rxData = -1;
      }
      return(rxData);
   case 1:
      for (i = 0; i < IO_LOOP; i++)
         if ((!(InPortB(portInp) & P_R_RDY)) && 
             (!(InPortB(portInp) & P_R_RDY))){
            OutPortB(portStatus, P_NORMAL ); /* switch = LOW  xsq */
            OutPortB(portStatus, (P_NORMAL | P_R_PCE)); /* PC-RD-PCE\ = low */
            OutPortB(portStatus, (P_NORMAL | P_R_PCE)); /* PC-RD-PCE\ = low */
            tmp = P_I_MASK ; /* delay, wait data send from PCE */
            tmp1 = tmp2 = 0x80 ;
            tmp &= InPortB(portInp) ;  /* Get high nibble */
            OutPortB(portStatus, P_NORMAL); /* PC-RD-PCE\ = high */
            OutPortB(portStatus, P_NORMAL); /* PC-RD-PCE\ = high */
            tmp1 &= (~tmp) ;
            tmp1 |= ((tmp << 1) & 0x70) ;
            tmp = P_I_MASK ; /* delay, wait high/low nibble switch ready */
            tmp &= InPortB(portInp) ; /* Get low  nibble */
            OutPortB(portStatus, P_SWITCH);
            tmp2 &= (~tmp) ;
            tmp2 |= ((tmp << 1) & 0x70) ;
            tmp2 >>= 4 ;
            rxData = tmp1 | tmp2;
            return(rxData) ;
         }
      break;
   case 0:
      for (i = 0; i < IO_LOOP; i++)
            if (InPortB(portStatus) & RV_RDY) {
               rxData = InPortB(portAddr);
               return(rxData);
            }
   default:;
   }
   return(-1);
} /* end of receiveB() */

/**************************************************************************
**
** Name :
**
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
RETCODE SendAddrLen(U32 addr, U32 len)
{
int i;
U8 temp[8];

   _fmemcpy(temp, &addr,4);
   _fmemcpy(temp+4, &len, 4);

   sendWait(temp[0]);
   for (i = 1; i < 8; i++)
      if (sendB(temp[i]) != GOOD) return(!GOOD);
   return(GOOD);
}

/****************************************************************************
**
**  SendStream
**
**  Description: Send command protocol to communication port.
**
**  Parameters:
**     input:
**
**     output:
**
**        return data --
**
****************************************************************************/
VOID SendStream(U8 *stream, U16 len) {
   switch(printerPort) {
   case 0:
   case 1:
      SendStreamParallel(stream,len);
      break;
   case 2:
      SendStreamSerial(stream,len);
      break;
   }
}

VOID SendStreamParallel(U8 *stream, U16 len) {
U8 checksum,highLen,lowLen;//,tempData[0x512];
U16 lp,aa;
U8 sNo = 0;

   sNo++;
   checksum = sNo;
   for (lp = 0; lp < len; lp++)
      checksum += stream[lp];
   stream[len] = (~checksum + 1);
   len++;

   len++;         // added for serial no
   lowLen = LowByte(len);
   highLen = HighByte(len);
   checksum = ~(lowLen+highLen) + 1;

   for (;;) {
      sendWait(':');
      if (sendB(lowLen) != GOOD) continue;
      if (sendB(highLen) != GOOD) continue;
      if (sendB(checksum) != GOOD) continue;
      if (sendB(sNo) != GOOD) continue;
      for (lp = 0; lp < (len-1); lp++)
         if (sendB(stream[lp]) != GOOD) continue;
      aa = receiveB();
      if (aa == ACK) return;
      else if (aa == 0xa5) {
       //_fmemcpy(tempData,stream,len);
       //CheckFwStatus();
       //linkMICE(1);  // sd186 function or supported function of third  party.
       //_fmemcpy(stream,tempData,len);
      }
   }
}

VOID SendStreamSerial(U8 *stream, U16 len) {
   U8 checksum,highLen,lowLen;//,tempData[0x512];
   U16 lp;
   S16 aa, ByteInQ;
//U32 llp;
   STATIC U8 sNo = 0;
   BOOLEAN firstTime = TRUE;
   DWORD startTime, currentTime;
   RETCODE err;

   sNo++;
   checksum = sNo;
   for (lp = 0; lp < len; lp++)
      checksum += stream[lp];
   stream[len] = (~checksum + 1);
   len++;         // added for checksum

   len++;         // added for serial no
   lowLen = LowByte(len);
   highLen = HighByte(len);
   checksum = ~(lowLen+highLen) + 1;

   for (;;) {
      if (printerPort == 2 && !firstTime) {
         FlushComm(portId,0);
         FlushComm(portId,1);
      }
      sendWait(':');
      firstTime = FALSE;
      if (sendB(lowLen) != GOOD) continue;
      if (sendB(highLen) != GOOD) continue;
      if (sendB(checksum) != GOOD) continue;

      if (sendB(sNo) != GOOD) continue;
      for (lp = 0; lp < (len-1); lp++)
         if (sendB(stream[lp]) != GOOD) continue;

      ByteInQ = 0;
      startTime = GetCurrentTime();
      do {
         err = ComError(portId, &commStat);
         if ((commStat.cbOutQue != ByteInQ) && (commStat.cbOutQue != 0)) {
            startTime = GetCurrentTime();
            ByteInQ = commStat.cbOutQue;
         }
         currentTime = GetCurrentTime();
      } while ((commStat.cbOutQue != 0)
                && ((currentTime - startTime) <= 60));

      aa = receiveB();
      if ((aa & 0x1F) == ACK)
         return;
      else if (((aa & 0x1F) == NACK) || (aa == -1))
         continue;
      else
         aa = NACK; // used just for debugging communication *gene*
//      else if (aa == 0xa5) {
//       _fmemcpy(tempData,stream,len);
//       CheckFwStatus();
//       linkMICE(1);  // sd186 function or supported function of third  party.
//       _fmemcpy(stream,tempData,len);
//      }
   }
}

/****************************************************************************
**
**  ReceiveStream
**
**  Description: Receive output command protocol to communication port.
**
**  Parameters:
**     input:
**
**     output:
**
**        return data --
**
****************************************************************************/
RETCODE ReceiveStream(U8 *inStream, U16 *inStreamLen,U16 waitSec) {
S16 aa;
U16 len;
U8 checksum,receiveBuf[MAX_COMM_BUFF],finalFlag;
LOOP_VAR lp;

   for (;;) {
      aa = receiveWait(waitSec);
      if (aa == ICE_REC_TIME_OUT) return(ICE_REC_TIME_OUT);
      if (aa != ':') continue;
      receiveBuf[0] = (U8)aa;
      finalFlag = 1;
      for(lp = 1; lp < 4; lp++)
         if ((aa = receiveB()) != -1) receiveBuf[lp] = (U8)aa;
         else {
            finalFlag = OFF;
            break;
         }
      if (finalFlag == OFF) continue;
      checksum = 0;
      for(lp = 1; lp < 4; lp++) checksum+= receiveBuf[lp];
      len = *(U16 *)&receiveBuf[1];   /* data length */
      if (len > 1024) continue;       /* invalid length */
      if ((checksum == 0) && (receiveBuf[0] == ':')) {
         finalFlag = 1;
         for (lp = 0; lp < (LOOP_VAR)len; lp++) {
            if ((aa = receiveB()) != -1) receiveBuf[lp] = (U8)aa;
            else {
               finalFlag = OFF;
               break;
            }
            checksum += receiveBuf[lp];
         }
         if (checksum == 0 && finalFlag) {
            sendB(ACK);
            *inStreamLen = len;
            memcpy(inStream,&receiveBuf[0],len);
            return(GOOD);
         } else {
            sendB(NACK);
            continue;
         }
      } else {
         for (lp=3; lp>0; lp--)
            UngetCommChar(portId,receiveBuf[lp]);
      }
   }
}

/**************************************************************************
**
** ComError
**
** Description: Get Communication status of a COM port ID
**
** Parameters:
**    input:
**       curComId    Current com port ID
**       comStatus   Pointer to the Com port status
**
**    output:
**       0 if no error else error code
**
***************************************************************************/
RETCODE ComError(int curComId, COMSTAT *comStatus) {
    return(MapComError(GetCommError(curComId, (COMSTAT FAR *) comStatus)));
}

/*
** MapComError
** Convert error returned from GetCommError to Tbird error number
*/
RETCODE MapComError(S16 commErr) {
   RETCODE err;

   switch(commErr) {
   case 0:
      err = GOOD; break;
   case CE_OVERRUN:
      err = ER_COM_HWRCV; break;    /* uart overrun */
   case CE_RXOVER:
      err = ER_COM_RCVOVER; break;  /* rcv queue full*/
   case CE_DSRTO:
      err = ER_COM_TIMEDOUT; break; /* dsr timeout */
   case CE_TXFULL:
      err = ER_COM_TXFULL; break;   /* xmt queue full*/
   case CE_CTSTO:
      err = ER_COM_TIMEDOUT; break; /* dsr timeout */
   case CE_RLSDTO:
      err = ER_COM_TIMEDOUT; break; /* dsr timeout */
   case CE_FRAME:
      err = ER_COM_FRAME; break;    /* framing error */
   default:
      err = ER_COMM_PACKET_TIMEOUT;
   }
   return(err);     /* Other errors impossible or benign */
}

VOID EXPORT CloseCommPort() {
   CloseComm(portId);
}
/**************************** End of File **********************************/
