
#define SIO_C

/* ------------- */
/* - Includes. - */
/* ------------- */

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include "sio.h"
#include "mlMouse.h"

/* ------------ */
/* - Globals. - */
/* ------------ */

SIO_Vars_t sioVars;
//#define SIO_DataMutex(x) x
// Changed to NOP because we're not multi-threading
// 9 Feb 2012, rah
#define SIO_DataMutex(x) x

SIO_DataMutex(
HANDLE mutex;
)

extern tQuatPacket qData;

/* -------------- */
/* - Functions. - */
/* -------------- */

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOSetCom                                           */
/*                                                                    */
/*   Description: SIO Set Com.                                        */
/*                                                                    */
/*    Parameters: nPort  - port number                                */
/*                dwBaud - baud                                       */
/*                                                                    */
/*       Returns: nPort                                               */
/*                                                                    */
/**********************************************************************/
unsigned short SIOSetCom(unsigned short nPort, unsigned int dwBaud)
{
    if( nPort && dwBaud )
    {
        sioVars.port = nPort;
        sioVars.baud = dwBaud;
    }

    return nPort;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOInit                                             */
/*                                                                    */
/*   Description: SIO Init.                                           */
/*                                                                    */
/*    Parameters: nPort  - port number                                */
/*                dwBaud - baud                                       */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
int SIOInit(int nPort, DWORD dwBaud)
{
    DCB PortDCB;
    COMMTIMEOUTS CommTimeouts;
    DWORD dwError;
    char comStr[12] =
    {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    TCHAR g_pUartPortName[16];

    _stprintf_s(g_pUartPortName,
                sizeof(g_pUartPortName)/sizeof(g_pUartPortName[0]),
                _T("\\\\.\\COM%d"),
                nPort);

    sioVars.hDevice = CreateFile(g_pUartPortName, GENERIC_READ | GENERIC_WRITE,
                                 0, NULL, OPEN_EXISTING, 0, NULL);
    // If it fails to open the port, return FALSE.
    if( INVALID_HANDLE_VALUE == sioVars.hDevice )
    {
        // Could not open the port.
        MessageBox(NULL, TEXT("Unable to open the port"), TEXT("Error"), MB_OK);
        dwError = GetLastError();
        return SIO_ERROR;
    }

    PortDCB.DCBlength = sizeof(DCB);

    // Get the default port setting information.
    GetCommState(sioVars.hDevice, &PortDCB);

    // Change the DCB structure settings.

    PortDCB.BaudRate = dwBaud; // Current baud
    PortDCB.fBinary = TRUE; // Binary mode; no EOF check
    PortDCB.fParity = FALSE; // Enable parity checking
    PortDCB.fOutxCtsFlow = FALSE; // No CTS output flow control
    PortDCB.fOutxDsrFlow = FALSE; // No DSR output flow control
    PortDCB.fDtrControl = DTR_CONTROL_DISABLE;
    // DTR flow control type
    PortDCB.fDsrSensitivity = FALSE; // DSR sensitivity
    PortDCB.fTXContinueOnXoff = TRUE; // XOFF continues Tx
    PortDCB.fOutX = FALSE; // No XON/XOFF out flow control
    PortDCB.fInX = FALSE; // No XON/XOFF in flow control
    PortDCB.fErrorChar = FALSE; // Disable error replacement
    PortDCB.fNull = FALSE; // Disable null stripping
    PortDCB.fRtsControl = RTS_CONTROL_DISABLE;
    // RTS flow control
    PortDCB.fAbortOnError = FALSE; // Do not abort reads/writes on
    // error
    PortDCB.ByteSize = 8; // Number of bits/byte, 4-8
    PortDCB.Parity = NOPARITY; // 0-4=no,odd,even,mark,space
    PortDCB.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2

    // Configure the port according to the specifications of the DCB
    // structure.
    if( FALSE == SetCommState(sioVars.hDevice, &PortDCB) )
    {
        // Could not create the read thread.
        MessageBox(NULL, TEXT("Unable to configure the serial port"),
                   TEXT("Error"), MB_OK);
        dwError = GetLastError();
        return SIO_ERROR;
    }

    // Retrieve the time-out parameters for all read and write operations
    // on the port.
    GetCommTimeouts(sioVars.hDevice, &CommTimeouts);

    // Change the COMMTIMEOUTS structure settings.
    CommTimeouts.ReadIntervalTimeout = MAXDWORD;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = 0;
    CommTimeouts.WriteTotalTimeoutMultiplier = 10;
    CommTimeouts.WriteTotalTimeoutConstant = 1000;

    // Set the time-out parameters for all read and write operations
    // on the port.
    if( FALSE == SetCommTimeouts(sioVars.hDevice, &CommTimeouts) )
    {
        // Could not create the read thread.
        MessageBox(NULL, TEXT("Unable to set the time-out parameters"),
                   TEXT("Error"), MB_OK);
        dwError = GetLastError();
        return SIO_ERROR;
    }

    return SIO_SUCCESS;
}


/**********************************************************************/
/*                                                                    */
/* Function Name: SIOUartGetQStatus                                   */
/*                                                                    */
/*   Description: SIO Get Uart Queue Status.                          */
/*                                                                    */
/*    Parameters: None.                                               */
/*                                                                    */
/*       Returns: cbInQue value.                                      */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIO Get Uart Queue Status
 */
unsigned long SIOUartGetQStatus(void)
{
    COMSTAT stat;
    DWORD dwErrors;
    if( !ClearCommError(sioVars.hDevice, &dwErrors, &stat) )
    {
        //TRACE(_T("Failed in call to ClearCommError\n"));
        //AfxThrowSerialException();
    }

    return (unsigned long) (stat.cbInQue);
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOOpen                                             */
/*                                                                    */
/*   Description: SIO Open Function.                                  */
/*                                                                    */
/*    Parameters: autoProcess - Non-Zero:process data in thrread      */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
int SIOOpen(unsigned char autoProcess)
{

    LPCWSTR lpszDevKey = TEXT("SIO");
	char writedata[1];
    if( SIOInit(sioVars.port, sioVars.baud) == SIO_ERROR )
    {
        return SIO_ERROR;
    }

    sioVars.autoProcess = autoProcess;

    /* Check if buffer size has been set, else use default of the max size. */
    if( !sioVars.rBuffMax )
    {
        SIOSetBufferSize(SIO_RBUFF_MAX);
    }

#if SIO_RBUFF_DYNAMIC
    /* Allocate space for the buffer. */
    sioVars.rBuff = MLOSMalloc(sioVars.rBuffNumBytes);
    //	printf("sio.c module - sioVars.rBuff = %x\r\n", sioVars.rBuff);
#endif

    writedata[0] = 'w';
    SIOWrite(writedata, 1);
    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOClose                                            */
/*                                                                    */
/*   Description: SIO Close Function.                                 */
/*                                                                    */
/*    Parameters: None.                                               */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
int SIOClose(void)
{

    CloseHandle(sioVars.hDevice);

#if SIO_RBUFF_DYNAMIC
    /* Deallocate buffer space. */
    MLOSFree(sioVars.rBuff);
#endif

    return SIO_SUCCESS;

}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIORead                                             */
/*                                                                    */
/*   Description: SIO Read Function.                                  */
/*                                                                    */
/*    Parameters: None.                                               */
/*                                                                    */
/*       Returns: Number of bytes read or SIO_ERROR		      */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIORead
 */
int SIORead(void)
{

    LPCWSTR lpszDevKey = TEXT("SIO");

    BOOL boolRetCode;
    unsigned char *readBuffer;
    DWORD nNumberOfBytesToRead;
    DWORD nNumberOfBytesRead;

    //	DWORD         cnt;

    // =============================================
    // SIO Read
    // =============================================

    nNumberOfBytesToRead = 1; // Size of readBuffer

    readBuffer = sioVars.readBuffer;

    boolRetCode = ReadFile(sioVars.hDevice, // HANDLE hFile,
                           sioVars.readBuffer, // LPVOID lpBuffer,
                           nNumberOfBytesToRead, // DWORD nNumberOfBytesToRead,
                           &nNumberOfBytesRead, // LPDWORD lpNumberOfBytesRead,
                           NULL // LPOVERLAPPED lpOverlapped
                    );

    if( !boolRetCode )
    {
        printf("Can't read SIO driver (ERR2)\r\n");
        sioVars.readBuffer[0] = 'E'; // ERROR...
        return SIO_ERROR;
    }

    if( sioVars.readBuffer[0] != '$' )
    {
        return SIO_ERROR;
    }

    nNumberOfBytesToRead = 1; // Size of readBuffer

    boolRetCode = ReadFile(sioVars.hDevice, // HANDLE hFile,
                           &(sioVars.readBuffer[1]), // LPVOID lpBuffer,
                           nNumberOfBytesToRead, // DWORD nNumberOfBytesToRead,
                           &nNumberOfBytesRead, // LPDWORD lpNumberOfBytesRead,
                           NULL // LPOVERLAPPED lpOverlapped
                    );

    if( !boolRetCode )
    {
        printf("Can't read SIO driver (ERR2)\r\n");
        sioVars.readBuffer[0] = 'E'; // ERROR...
        return SIO_ERROR;
    }

    if( sioVars.readBuffer[1] > SIO_MAX_PKT_SIZE )
    {
        return SIO_ERROR;
    }

    nNumberOfBytesToRead = sioVars.readBuffer[1] - 2;

    boolRetCode = ReadFile(sioVars.hDevice, // HANDLE hFile,
                           &(sioVars.readBuffer[2]), // LPVOID lpBuffer,
                           nNumberOfBytesToRead, // DWORD nNumberOfBytesToRead,
                           &nNumberOfBytesRead, // LPDWORD lpNumberOfBytesRead,
                           NULL // LPOVERLAPPED lpOverlapped
                    );

    if( !boolRetCode )
    {
        printf("Can't read SIO driver (ERR3)\r\n");
        sioVars.readBuffer[0] = 'E'; // ERROR...
        return SIO_ERROR;
    }

    return(nNumberOfBytesRead + 2);

}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOWrite                                            */
/*                                                                    */
/*   Description: SIO Write Function.                                 */
/*                                                                    */
/*    Parameters: None.                                               */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOWrite
 */
int SIOWrite(char *data, int len)
{

    LPCWSTR lpszDevKey = TEXT("SIO");

    BOOL boolRetCode;
    unsigned char *writeBuffer;

    DWORD nNumberOfBytesToRead;
    DWORD nNumberOfBytesRead;

    // =============================================
    // SIO Write Test.................
    // =============================================

	nNumberOfBytesToRead = len; // Size of write buffer
    writeBuffer = sioVars.writeBuffer;

    memcpy(writeBuffer, data, len);
	//writeBuffer[0] = "t"; // Write Cmd
    
    boolRetCode = WriteFile(sioVars.hDevice, // HANDLE hFile,
                            sioVars.writeBuffer, // LPVOID lpBuffer,
                            nNumberOfBytesToRead, // DWORD nNumberOfBytesToRead,
                            &nNumberOfBytesRead, // LPDWORD lpNumberOfBytesRead,
                            NULL // LPOVERLAPPED lpOverlapped
                    );

    if( !boolRetCode )
    {
        printf("Can't write SIO driver (ERR4)\r\n");
    }

    return SIO_SUCCESS;

}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOSetBufferSize                                    */
/*                                                                    */
/*   Description: Set SIO Buffer Size Function.                       */
/*                                                                    */
/*    Parameters: bufferSize - SIO buffer size.                       */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOSetBufferSize
 */
int SIOSetBufferSize(unsigned short bufferSize)
{

    /* Set SIO buffer size. */
    if( (bufferSize >= 1) && (bufferSize <= SIO_RBUFF_MAX) )
    {
        sioVars.rBuffMax = bufferSize;
    }
    else
    {
        sioVars.rBuffMax = SIO_RBUFF_MAX;
    }

    /* Calculate size in bytes. */
    sioVars.rBuffNumBytes = sioVars.rBuffMax * (unsigned short) SIO_RBUFF_SIZE;

    //	printf("sio.c module - sioVars.rBuffNumBytes = %x\r\n", sioVars.rBuffNumBytes);

    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOUpdateBuffer                                     */
/*                                                                    */
/*   Description: SIO Update Buffer Function.                         */
/*                                                                    */
/*    Parameters: None.                                               */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOUpdateBuffer
 */
int SIOUpdateBuffer(void)
{
    U16 index, cnt;
#if SIO_RBUFF_DYNAMIC
    U16 rBuffIndex;
#endif

    if( sioVars.readBuffer[0] == '$' )
    {

        // Mutex Lock
        SIO_DataMutex(SIOLockMutex();)

	cnt = sioVars.readBuffer[1];

        // Buffer the data.
        for( index = 0; index < cnt; ++index )
        {

#if !SIO_RBUFF_DYNAMIC
            sioVars.rBuff[sioVars.rBuffWIndex][index] = sioVars.readBuffer[index];
#else
            rBuffIndex = (sioVars.rBuffWIndex * (U16)SIO_RBUFF_SIZE) + index;
            sioVars.rBuff[rBuffIndex] = sioVars.readBuffer[index];
#endif

        }

        // Adjust the read and write index.
        ++sioVars.rBuffWIndex;
        if( sioVars.rBuffWIndex >= sioVars.rBuffMax )
        {
            sioVars.rBuffWIndex = 0;
        }
        if( sioVars.rBuffWIndex == sioVars.rBuffRIndex )
        {

            ++sioVars.rBuffRIndex;
            if( sioVars.rBuffRIndex >= sioVars.rBuffMax )
            {
                sioVars.rBuffRIndex = 0;
            }

        }

        // Mutex Unlock
        SIO_DataMutex(SIOUnlockMutex();)
    }


    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOHandler                                          */
/*                                                                    */
/*   Description: SIO Handler Function.                               */
/*                                                                    */
/*    Parameters: None.                                               */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/**********************************************************************/
int SIOHandler(void)
{

    int byteCnt;
    int rtnCode;
//    int processedCnt;

    /* Read SIO data. */
    byteCnt = SIOUartGetQStatus();
    if( byteCnt > 2048 )
        printf("******** serial buffer has %d bytes\n", byteCnt);

//    for (processedCnt = 0; processedCnt < byteCnt; )
    while (SIOUartGetQStatus() > 14)
    {
        /* SIO Read. */
        rtnCode = SIORead();
	if (rtnCode < 0)
	    break;

//        processedCnt += rtnCode;

        /* Update Buffer. */
        SIOUpdateBuffer();
    }

    /* Exit. */
    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOReadBuffer                                       */
/*                                                                    */
/*   Description: SIO Read Buffer                                     */
/*                                                                    */
/*    Parameters: buffermode - Zero: read direct                      */
/*                             Non-Zero: read under lock              */
/*                rBuff - pointer of read buffer                      */
/*                                                                    */
/*       Returns: Return code.                                        */
/*                                                                    */
/*       Returns:                                                     */
/*                                                                    */
/**********************************************************************/
int SIOReadBuffer(unsigned char bufferMode, unsigned char *rBuff)
{

    int retCode;
    U16 index, cnt;
#if SIO_RBUFF_DYNAMIC
    U16 rBuffIndex;
#endif

    retCode = SIO_SUCCESS;

    if( !bufferMode )
    {
	cnt = sioVars.readBuffer[1];

        // Get the data.
        for( index = 0; index < cnt; ++index )
        {
            rBuff[index] = sioVars.readBuffer[index];
        }

        return retCode;

    }

    // Mutex Lock.
    SIO_DataMutex(SIOLockMutex();)

    // Get data from buffer.
    if( sioVars.rBuffWIndex != sioVars.rBuffRIndex )
    {
#if !SIO_RBUFF_DYNAMIC
	cnt = sioVars.rBuff[sioVars.rBuffRIndex][1];
#else
	cnt = sioVars.rBuff[(sioVars.rBuffRIndex * (U16)SIO_RBUFF_SIZE) + 1];
#endif

        // Get the data from the buffer.
        for( index = 0; index < cnt; ++index )
        {

#if !SIO_RBUFF_DYNAMIC
            rBuff[index] = sioVars.rBuff[sioVars.rBuffRIndex][index];
#else
            rBuffIndex = (sioVars.rBuffRIndex * (U16)SIO_RBUFF_SIZE) + index;
            rBuff[index] = sioVars.rBuff[rBuffIndex];
#endif

        }

        // Adjust the read index.
        ++sioVars.rBuffRIndex;
        if( sioVars.rBuffRIndex >= sioVars.rBuffMax )
        {
            sioVars.rBuffRIndex = 0;
        }

        if( rBuff[0] != '$' )
        {
            retCode = SIO_ERROR;
        }

    }
    else
    {
        retCode = SIO_ERROR;
    }

    // Mutex Unlock
    SIO_DataMutex(SIOUnlockMutex();)

    return retCode;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOEmptyBuffer                                      */
/*                                                                    */
/*   Description: SIO EmptyBuffer                                     */
/*                                                                    */
/*    Parameters:                                                     */
/*                                                                    */
/*       Returns:                                                     */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOEmptyBuffer
 */
int SIOEmptyBuffer(void)
{
    // Mutex Lock.
    SIO_DataMutex(SIOLockMutex();)

    // Empty the buffer.
    if( sioVars.rBuffWIndex != sioVars.rBuffRIndex )
    {
        sioVars.rBuffWIndex = 0;
        sioVars.rBuffRIndex = 0;
    }

    // Mutex Unlock
    SIO_DataMutex(SIOUnlockMutex();)

    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOPktsInBuffer                                     */
/*                                                                    */
/*   Description: SIOPktsInBuffer                                     */
/*                                                                    */
/*    Parameters: pktsInBuffer - pointer to store packet num          */
/*                                                                    */
/*       Returns:                                                     */
/*                                                                    */
/**********************************************************************/
int SIOPktsInBuffer(unsigned short *pktsInBuffer)
{
    // Mutex Lock.
    SIO_DataMutex(SIOLockMutex();)

    // Get number of packets in buffer.
    if( sioVars.rBuffWIndex != sioVars.rBuffRIndex )
    {

        if( sioVars.rBuffWIndex > sioVars.rBuffRIndex )
        {

            *pktsInBuffer = (sioVars.rBuffWIndex - 1) - sioVars.rBuffRIndex;

        }
        else
        {

            *pktsInBuffer = (sioVars.rBuffWIndex + sioVars.rBuffMax)
                            - sioVars.rBuffRIndex;
        }

    }
    else
    {

        *pktsInBuffer = 0;
    }


    // Mutex Unlock
    SIO_DataMutex(SIOUnlockMutex();)

    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOCreateMutex                                      */
/*                                                                    */
/*   Description: SIO mutex create function.                          */
/*                                                                    */
/*    Parameters:                                                     */
/*                                                                    */
/*       Returns:                                                     */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOCreateMutex
 */
int SIOCreateMutex(void)
{

    SIO_DataMutex(
        mutex = CreateMutex(0,FALSE,0);
    )

    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOLockMutex                                        */
/*                                                                    */
/*   Description: SIO mutex lock function                             */
/*                                                                    */
/*    Parameters:                                                     */
/*                                                                    */
/*       Returns:                                                     */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOLockMutex
 */
int SIOLockMutex(void)
{

    DWORD rc;

    SIO_DataMutex(
        // Mutex Lock.
        if ((rc = WaitForSingleObject(mutex, INFINITE)) == WAIT_FAILED)
        {
            //   return RC_LOCK_ERROR ;
        }
    )

    return SIO_SUCCESS;
}

/**********************************************************************/
/*                                                                    */
/* Function Name: SIOUnlockMutex                                      */
/*                                                                    */
/*   Description: SIO mutex unlock function.                          */
/*                                                                    */
/*    Parameters:                                                     */
/*                                                                    */
/*       Returns:                                                     */
/*                                                                    */
/**********************************************************************/
/**
 * @brief SIOMutexUnlock
 */
int SIOUnlockMutex(void)
{

    SIO_DataMutex(
        // Mutex Unlock
        if (!ReleaseMutex(mutex))
        {
            //  rc = GetLastError();
            //  return RC_UNLOCK_ERROR;
        }
    )

    return SIO_SUCCESS;
}


/********************/
/**\}*//* defgroup */
/********************/
