//=================================================================
//
//        eCosTestSerial.cpp
//
//        Serial test class
//
//=================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Cygnus eCos Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://sourceware.cygnus.com/ecos
// 
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
// License for the specific language governing rights and limitations under
// the License.
// 
// The Original Code is eCos - Embedded Cygnus Operating System, released
// September 30, 1998.
// 
// The Initial Developer of the Original Code is Cygnus.  Portions created
// by Cygnus are Copyright (C) 1998, 1999 Cygnus Solutions.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//=================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     sdf
// Contributors:  sdf
// Date:          1999-04-01
// Description:   This class abstracts the serial port for use in the testing infrastructure
// Usage:
//
//####DESCRIPTIONEND####

#include "stdafx.h"
#include "eCosTestUtils.h"
#include "eCosTestSerial.h"

CeCosTestSerial::CeCosTestSerial():
    m_pHandle(0),
    m_nBaud(0),
    m_bParity(false),
    m_nDataBits(8),
    m_nStopBits(ONE),
    m_nTotalReadTimeout(10*1000),
    m_nTotalWriteTimeout(10*1000),
    m_nInterCharReadTimeout(500),
    m_nInterCharWriteTimeout(500),
    m_bBlockingReads(true)
{
}

CeCosTestSerial::~CeCosTestSerial()
{
    Close();
}

bool CeCosTestSerial::Close()
{
    bool rc=m_pHandle && (TRUE==CloseHandle((HANDLE)m_pHandle));
    m_pHandle=0;
    return rc;
}

CeCosTestSerial::CeCosTestSerial(const char *pszPort,int nBaud):
    m_pHandle(0),
    m_bParity(false),
    m_nDataBits(8),
    m_nStopBits(ONE),
    m_nTotalReadTimeout(10*1000),
    m_nTotalWriteTimeout(10*1000),
    m_nInterCharReadTimeout(500),
    m_nInterCharWriteTimeout(500) 
{
    Open(pszPort,nBaud);
}

bool CeCosTestSerial::Open(const char *pszPort,int nBaud)
{
	bool rc=false;
    m_nBaud=nBaud,
    m_strPort=pszPort;
	HANDLE hCom = CreateFile(pszPort,GENERIC_READ|GENERIC_WRITE, 0,NULL,OPEN_EXISTING,0,NULL);
    Flush();
    if (INVALID_HANDLE_VALUE==hCom) { 
		TRACE("Failed to open port %s\n",pszPort);
	} else {
        m_pHandle=(void *)hCom;
        if(ApplySettings()){
            rc=true;
        } else {
            Close();
        }
	}
    return rc;
}

bool CeCosTestSerial::ApplySettings()
{
    bool rc=false;
    DCB dcb;

    ZeroMemory(&dcb,sizeof dcb);
    dcb.DCBlength=sizeof dcb;
    dcb.BaudRate=m_nBaud;
    dcb.fBinary=m_bParity;
    dcb.StopBits=(BYTE)m_nStopBits;
    dcb.ByteSize=(BYTE)m_nDataBits;
    const char *arpszStopbits[3]={"1","1.5","2"};
    TRACE("Applysettings baud=%d bParity=%d stopbits=%s databits=%d\n",
        dcb.BaudRate, 
        dcb.fBinary, 
        arpszStopbits[dcb.StopBits],
        dcb.ByteSize);

    // No control over the following yet
    dcb.fDtrControl=DTR_CONTROL_ENABLE;
    dcb.fTXContinueOnXoff=1;
    dcb.fRtsControl=1;
    dcb.fAbortOnError=1;
    dcb.XonLim=2048;
    dcb.XoffLim=512;
    dcb.XonChar=17;
    dcb.XoffChar=19;

    HANDLE hCom=(HANDLE)m_pHandle;
    if (!SetCommState(hCom, &dcb)) { 
		TRACE("Failed to set comm state - port %s handle=%d err=%d\n",(const char *)m_strPort,hCom,GetLastError());
	} else {
		COMMTIMEOUTS commtimeouts;
        if(m_bBlockingReads){		
		    commtimeouts.ReadIntervalTimeout=m_nInterCharReadTimeout;
		    commtimeouts.ReadTotalTimeoutMultiplier=0;
		    commtimeouts.ReadTotalTimeoutConstant=m_nTotalReadTimeout;
        } else {
		    commtimeouts.ReadIntervalTimeout=MAXDWORD;
		    commtimeouts.ReadTotalTimeoutMultiplier=0;
		    commtimeouts.ReadTotalTimeoutConstant=0;
        }
		commtimeouts.WriteTotalTimeoutMultiplier=m_nTotalWriteTimeout;
		commtimeouts.WriteTotalTimeoutConstant=m_nInterCharWriteTimeout;

		if (SetCommTimeouts(hCom, &commtimeouts)) { 
            rc=true;
        } else {
			TRACE("Failed to set comm timeouts - port %s\n",(const char *)m_strPort);
		}
	}
    return rc;
}

bool CeCosTestSerial::Read (CeCosTestUtils::String &str)
{
    char *c=str.GetBuffer();
    unsigned int nRead=0;
    bool rc=Read(c,str.GetLength(),nRead);
    c[nRead]='\0';
    str.ReleaseBuffer();
    return rc;
}

bool CeCosTestSerial::Write(const CeCosTestUtils::String &str)
{
    unsigned int nWritten=0;
    return Write((void *)(const char *)str,str.GetLength(),nWritten) && nWritten==str.GetLength();
}

bool CeCosTestSerial::Read (void *pBuf,unsigned int nSize,unsigned int &nRead)
{
    bool rc=(TRUE==ReadFile((HANDLE)m_pHandle,pBuf,nSize,(LPDWORD)&nRead,0));
    return rc;
}

bool CeCosTestSerial::Write(void *pBuf,unsigned int nSize,unsigned int &nWritten)
{
    return TRUE==WriteFile((HANDLE)m_pHandle,pBuf,nSize,(LPDWORD)&nWritten,0);
}

bool CeCosTestSerial::ClearError()
{
    DWORD dwErrors;
    bool rc=(TRUE==ClearCommError(HANDLE(m_pHandle),&dwErrors,0));
    if(dwErrors&CE_BREAK)TRACE("The hardware detected a break condition.\n");
    if(dwErrors&CE_DNS)TRACE("Windows 95 and Windows 98: A parallel device is not selected.\n");
    if(dwErrors&CE_FRAME)TRACE("The hardware detected a framing error.\n");
    if(dwErrors&CE_IOE)TRACE("An I/O error occurred during communications with the device.\n");
    if(dwErrors&CE_MODE)TRACE("The requested mode is not supported, or the hFile parameter is invalid. If this value is specified, it is the only valid error.\n");
    if(dwErrors&CE_OOP)TRACE("Windows 95 and Windows 98: A parallel device signaled that it is out of paper.\n");
    if(dwErrors&CE_OVERRUN)TRACE("A character-buffer overrun has occurred. The next character is lost.\n");
    if(dwErrors&CE_PTO)TRACE("Windows 95 and Windows 98: A time-out occurred on a parallel device.\n");
    if(dwErrors&CE_RXOVER)TRACE("An input buffer overflow has occurred. There is either no room in the input buffer, or a character was received after the end-of-file (EOF) character.\n");
    if(dwErrors&CE_RXPARITY)TRACE("The hardware detected a parity error.\n");
    if(dwErrors&CE_TXFULL)TRACE("The application tried to transmit a character, but the output buffer was full.\n");
    return rc;
}

bool CeCosTestSerial::SetBlockingReads(bool b,bool bApplySettingsNow/*=true*/)
{
    m_bBlockingReads=b;
    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial:: SetBaud(unsigned int nBaud,bool bApplySettingsNow/*=true*/)
{
    m_nBaud=nBaud;
    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial:: SetParity(bool bParityOn,bool bApplySettingsNow/*=true*/)
{
    m_bParity=bParityOn;
    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial:: SetDataBits(int n,bool bApplySettingsNow/*=true*/)
{
    m_nDataBits=n;
    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial:: SetStopBits(StopBitsType n,bool bApplySettingsNow/*=true*/)
{
    m_nStopBits=n;
    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial:: SetReadTimeOuts(int nTotal,int nBetweenChars,bool bApplySettingsNow/*=true*/) // mSec
{
    m_nTotalReadTimeout=nTotal;
    m_nInterCharReadTimeout=nBetweenChars;

    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial:: SetWriteTimeOuts(int nTotal,int nBetweenChars,bool bApplySettingsNow/*=true*/) // mSec
{
    m_nTotalWriteTimeout=nTotal;
    m_nInterCharWriteTimeout=nBetweenChars;
    return 0==m_pHandle || !bApplySettingsNow || ApplySettings();
}

bool CeCosTestSerial::Flush (void)
{
    return TRUE==PurgeComm ((HANDLE)m_pHandle,PURGE_TXCLEAR|PURGE_RXCLEAR);
}

