/***************************************************************************
**
** 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>

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

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

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

#ifndef _HOSTERRS__
#include "hosterrs.h"
#endif

#include <string.h>

extern U8 BASE_TIME;

U16 portAddr, portStatus, portInp;
U16 printerPort;
int portId;
#define CBR_9600     0xFF16
#define CBR_14400    0xFF17
#define CBR_19200    0xFF18
#define CBR_38400    0xFF1B
#define CBR_56000    0xFF1F
#define CBR_128000   0xFF23
#define CBR_256000   0xFF27

/**************************************************************************
**
** 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

#define   ACK            6
#define   NACK           0x15

/**************************************************************************
**
** 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 */

#define INI_FILENAME "mp186.ini"
#define INI_SECTION_NAME "Serial"
#define INI_BDM_SPEED "bdmSpeed"
#define INI_KEY_NAME "BaudRate"

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

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


/**************************************************************************
**
** Name : chk_hlt
**
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
RETCODE chk_hlt(VOID) {
#define ESC 0x1b
BOOLEAN abortFromEsc;
RETCODE  err;

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

/**************************************************************************
**
** Name : SetComm
**
** Function
**
**    Input  :
**
**    Output :
**
** Notes:
**
**************************************************************************/
RETCODE SetComm(U16 pData) {
   U32 BRate;
   CHAR buf[10];

   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) {
            CloseComm(portId);
            return(!GOOD);
         }
         GetPrivateProfileString(INI_SECTION_NAME, INI_KEY_NAME,"57600",
              buf,sizeof(buf), INI_FILENAME);
         BRate = atol(buf);
         switch (BRate) {
         case 19200l:
            comDcb.BaudRate = CBR_19200; break;
         case 38400l:
            comDcb.BaudRate = CBR_38400; break;
         case 57600l:
            comDcb.BaudRate = CBR_56000; break;
         case 115200l:
            comDcb.BaudRate = CBR_128000; break;
         default:
            comDcb.BaudRate = CBR_56000; break;
         }
         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.XonLim = XON_LIMIT;
         comDcb.XoffLim = XOFF_LIMIT;
         comDcb.Id = portId;
         if (SetCommState((DCB FAR *) &comDcb) < 0) {
            comDcb.BaudRate = CBR_56000;
            if (SetCommState((DCB FAR *) &comDcb) < 0)
               return(!GOOD);
         }
         ltoa(comDcb.BaudRate,buf,16);
         WritePrivateProfileString("Serial", "LinkRate", buf,
                      INI_FILENAME);
         printerPort = 2;
         EscapeCommFunction(portId,SETDTR);
         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,(COMSTAT FAR *) &commStat);
      } while (err == ER_COM_TXFULL);
      if (WriteComm(portId,(LPSTR)&txData,1) == 1)
         return(GOOD);
      err = ComError(portId,(COMSTAT FAR *) &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, U8 waitSec) {
//   int i;
time_t startTime,currentTime;

   time(&startTime);
   while(1) {
      if (sendB(txData) == GOOD) return(GOOD);
//      chk_hlt();
      time(&currentTime);
      if ((currentTime - startTime) > (time_t)waitSec) return(ICE_REC_TIME_OUT);
   }
}        /* 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) <= 1000/*60-Hera 4/23/98*/);
      if (readNum == 1) {
          rxData = rxData & 0x00FF;
//          err = ComError(portId,(COMSTAT FAR *) &commStat);
          return(rxData);
       } else {
          err = ComError(portId,(COMSTAT FAR *) &commStat);
//          ErrDisplayError(err, FORCE_POPUP);
          return(-1);
       }
   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 SendAddrLenSerial(U32 addr, U32 len);
RETCODE SendAddrLenParallel(U32 addr, U32 len);

RETCODE SendAddrLen(U32 addr, U32 len) {
   if (printerPort == 2)
      return (SendAddrLenSerial(addr,len));
   else
      return (SendAddrLenParallel(addr,len));
}

RETCODE SendAddrLenParallel(U32 addr, U32 len) {
int i;
U8 temp[8];

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

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


RETCODE SendAddrLenSerial(U32 addr, U32 len) {
int i;
U8 temp[10], checksum;

   temp[0] = ':'; // leader code
   _fmemcpy(temp+1, &addr,4);
   _fmemcpy(temp+5, &len, 4);
   checksum = 0;   // header checksum
   for (i = 0; i < 9; i++)
      checksum += temp[i];
   temp[9] = ~checksum + 1;
   sendWait(temp[0],BASE_TIME);
   for (i = 1; i < 10; 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) {

   if (printerPort == 2)
      SendStreamSerial(stream,len);
   else
      SendStreamParallel(stream,len);
}

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

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

   lowLen = LowByte(len);
   highLen = HighByte(len);
   checksum = ~(lowLen+highLen) + 1;

   for (;;) {
      sendWait(':',BASE_TIME);
      if (sendB(lowLen) != GOOD) continue;
      if (sendB(highLen) != GOOD) continue;
      if (sendB(checksum) != GOOD) continue;
      for (lp = 0; lp < len; 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[2];
//   RETCODE err;

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

   lowLen = LowByte(len);
   highLen = HighByte(len);
   checksum = ~(lowLen+highLen) + 1;

   for (;;) {
      FlushComm(portId,0);
      FlushComm(portId,1);

      sendWait(':',BASE_TIME);
      if (sendB(lowLen) != GOOD) continue;
      if (sendB(highLen) != GOOD) continue;
      if (sendB(checksum) != GOOD) continue;
      for (lp = 0; lp < len; lp++)
         if (sendB(stream[lp]) != GOOD) continue;
      do {
         ComError(portId, &commStat);
      } while (commStat.cbOutQue != 0);

      aa[0] = receiveB();
      if (aa[0] == ACK) return;
      else if (aa[0] == 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) {

   if (printerPort == 2)
      return (ReceiveStreamSerial(inStream,inStreamLen,waitSec));
   else
      return (ReceiveStreamParallel(inStream,inStreamLen,waitSec));
}

RETCODE ReceiveStreamParallel(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 == -1) return(ICE_REC_TIME_OUT);
      receiveBuf[0] = (U8)aa;
      //finalFlag = ON;
      finalFlag = 1;
      for(lp = 1; lp < 4; lp++)
         if ((aa = receiveB()) != -1) receiveBuf[lp] = (U8)aa;
         else {
            finalFlag = OFF;
            break;
         }
      if (finalFlag == OFF) return(GOOD); //continue; // new statement
      len = *(U16 *)&receiveBuf[1];   /* data length */
      checksum = 0;
      for(lp = 1; lp < 4; lp++) checksum+= receiveBuf[lp];
      if ((checksum == 0) && (receiveBuf[0] == ':')) {
         //finalFlag = ON;
         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 (finalFlag == OFF) return(GOOD); //continue; // new statement
         if (checksum == 0) {
            *inStreamLen = len;
            _fmemcpy(inStream,&receiveBuf[0],len);
            if (sendB(ACK) != GOOD) continue;
            return(GOOD);
         }
         else if (sendB(NACK) != GOOD) continue;
      }
   }
}

//RETCODE ReceiveStreamSerial2nd(U8 *inStream, U16 *inStreamLen,U16 waitSec);

//RETCODE ReceiveStreamSerial(U8 *inStream, U16 *inStreamLen,U16 waitSec) {
//
//   while (ReceiveStreamSerial2nd(inStream,inStreamLen,waitSec) != GOOD);
//   return(GOOD);
//}

//RETCODE ReceiveStreamSerial2nd(U8 *inStream, U16 *inStreamLen,U16 waitSec) {
RETCODE ReceiveStreamSerial(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 ((U8)aa != ':')
         continue;
      receiveBuf[0] = (U8)aa;
      //finalFlag = ON;
      finalFlag = 1;
      for(lp = 1; lp < 4; lp++)
         if ((aa = receiveB()) != -1) receiveBuf[lp] = (U8)aa;
         else {
            finalFlag = OFF;
            break;
         }
      if (finalFlag == OFF)
         continue;
      len = *(U16 *)&receiveBuf[1];   /* data length */
      if (len > 1024)
         continue;
      checksum = 0;
      for(lp = 1; lp < 4; lp++) checksum+= receiveBuf[lp];
//      if ((checksum == 0) && (receiveBuf[0] == ':')) {
      if (checksum == 0) {
         //finalFlag = ON;
         finalFlag = 1;
         for (lp = 0; lp < (LOOP_VAR)len; lp++) {
            if ((aa = receiveB()) != -1) {
                receiveBuf[lp] = (U8)aa;
                inStream[lp] = (U8)aa;
            }
            else {
               finalFlag = OFF;
               break;
            }
            checksum += receiveBuf[lp];
         }
//         if (finalFlag == OFF) continue; // new statement
         if ((checksum == 0) && finalFlag) {
            *inStreamLen = len;
//            _fmemcpy(inStream,&receiveBuf[0],len);
            if (sendB(ACK) != GOOD)
               continue;
            return(GOOD);
         }
         else {
            sendB(NACK);
            continue;
         }
      }
   }
}

/**************************************************************************
**
** 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 = GOOD; break;
//      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 **********************************/
