/* ------------------------------------------------------------------------ --
--                                                                          --
--                        PC serial port connection object                  --
--                           for  event-driven programs                     --
--                                                                          --
--                                                                          --
--                                                                          --
--  Copyright @ 2001-2002     Thierry Schneider                             --
--                            thierry@tetraedre.com                         --
--                                                                          --
--                                                                          --
--                                                                          --
-- ------------------------------------------------------------------------ --
--                                                                          --
--  Filename : Tserial_event.cpp                                            --
--  Author   : Thierry Schneider                                            --
--  Created  : April 4th 2000                                               --
--  Modified : June 22nd 2002                                               --
--  Plateform: Windows 95, 98, NT, 2000, XP (Win32)                         --
-- ------------------------------------------------------------------------ --
--                                                                          --
--  This software is given without any warranty. It can be distributed      --
--  free of charge as long as this header remains, unchanged.               --
--                                                                          --
-- ------------------------------------------------------------------------ --
--                                                                          --
-- ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------ */

#define STRICT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <conio.h>
#include <windows.h>
#include <wchar.h>

#include "Tserial_event.h"

void Tserial_event_thread_start(void *arg);

typedef unsigned (WINAPI *PBEGINTHREADEX_THREADFUNC) (LPVOID lpThreadParameter);
typedef unsigned *PBEGINTHREADEX_THREADID;

/* ---------------------------------------------------------------------- */
/* ---------------------  Tserial_event_thread_start  ------------------- */
/* ---------------------------------------------------------------------- */
/**
    This function is not part of the Tserial_event object. It is simply used
    to start the thread from an external point of the object.
*/
void Tserial_event_thread_start(void *arg)
{
    class Tserial_event *serial_unit;

    serial_unit = (Tserial_event *) arg;
    
    if (serial_unit != 0)
        serial_unit->run();
}

/* -------------------------------------------------------------------- */
/* ----------    Tserial_event ----  Constructor  --------------------- */
/* -------------------------------------------------------------------- */
Tserial_event::Tserial_event()
{
    int i;

    ready            = false;
    parityMode       = SERIAL_PARITY_NONE;
    stopbits		 = SERIAL_STOP_1;
    port[0]          = 0;
    rate             = 0;
    threadid         = 0;
    serial_handle    = INVALID_HANDLE_VALUE;
    thread_handle    = 0;
    owner            = 0;
    tx_in_progress   = 0;
    rx_in_progress   = 0;
    max_rx_size      = 1;
    tx_size          = 0;
    received_size    = 0;
    check_modem      = false;
    manager          = 0;

    /* -------------------------------------------------------------- */
    // creating Events for the different sources
    for (i=0; i < SERIAL_SIGNAL_NBR; i++) {
        if ((i == SIG_READER) || (i == SIG_WRITER) || (i == SIG_MODEM_EVENTS))
            serial_events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);  // Manual Reset
        else
            serial_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto reset
    }
}

/* -------------------------------------------------------------------- */
/*--------    ~Tserial_event ------  Destructor ----------------------- */
/* -------------------------------------------------------------------- */
Tserial_event::~Tserial_event()
{
    int i;

    if (thread_handle != 0)
        WaitForSingleObject(thread_handle, 200);
    thread_handle = 0;

    /* -------------------------------------------------------- */
    for (i=0; i < SERIAL_SIGNAL_NBR; i++) {         // deleting the events
        if (serial_events[i] != INVALID_HANDLE_VALUE)
            CloseHandle(serial_events[i]);
        serial_events[i] = INVALID_HANDLE_VALUE;
    }

    if (serial_handle != INVALID_HANDLE_VALUE)
        CloseHandle(serial_handle);
    serial_handle = INVALID_HANDLE_VALUE;
}
/* -------------------------------------------------------------------- */
/* --------------------------    disconnect   ------------------------- */
/* -------------------------------------------------------------------- */
void Tserial_event::disconnect(void)
{
    ready = false;
    SetEvent(serial_events[SIG_POWER_DOWN]);

    if (thread_handle != 0)
        WaitForSingleObject(thread_handle, 200);
    thread_handle = 0;
}
/* -------------------------------------------------------------------- */
/* --------------------------    connect      ------------------------- */
/* -------------------------------------------------------------------- */
/**
     Serial port, file and overlapped structures initialization

     Return 0 on success, > 0 on failure.
*/
int  Tserial_event::connect (TCHAR *port_arg, int  rate_arg,  int parity_arg,
                             char ByteSize , int  stop_arg, bool modem_events)
{
    int  erreur;
    DCB  dcb;
    int  i;
    COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };

    /* --------------------------------------------- */
    if (serial_handle != INVALID_HANDLE_VALUE)
        CloseHandle(serial_handle);
    serial_handle = INVALID_HANDLE_VALUE;

    if (port_arg!=0)
    {
		_tcsncpy(port, port_arg, 25);
        rate        = rate_arg;
        parityMode  = parity_arg;
        stopbits    = stop_arg;
        check_modem = modem_events;
        erreur      = 0;

        ZeroMemory(&ovReader   ,sizeof(ovReader)   );  // clearing the overlapped
        ZeroMemory(&ovWriter   ,sizeof(ovWriter)   );
        ZeroMemory(&ovWaitEvent,sizeof(ovWaitEvent));
        memset(&dcb,0,sizeof(dcb));

        /* -------------------------------------------------------------------- */
        serial_handle    = CreateFile(port, GENERIC_READ | GENERIC_WRITE,
                   0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
                   // opening serial port

    	if (serial_handle != INVALID_HANDLE_VALUE)
            // set DCB
            if(!GetCommState(serial_handle,&dcb))
                erreur = 32;
        /* -------------------------------------------------------------------- */
        // set DCB to configure the serial port
        dcb.DCBlength       = sizeof(dcb);
        
        /* ---------- Serial Port Config ------- */
        dcb.BaudRate        = rate;

        switch(parityMode) {
            case SERIAL_PARITY_NONE:
                            dcb.Parity      = NOPARITY;
                            dcb.fParity     = false;
                            break;
            case SERIAL_PARITY_EVEN:
                            dcb.Parity      = EVENPARITY;
                            dcb.fParity     = true;
                            break;
            case SERIAL_PARITY_ODD:
                            dcb.Parity      = ODDPARITY;
                            dcb.fParity     = true;
                            break;
        }

        switch (stopbits) {
            case SERIAL_STOP_1:
					        dcb.StopBits        = ONESTOPBIT;
            				break;
            case SERIAL_STOP_2:
					        dcb.StopBits        = TWOSTOPBITS;
            				break;
        }

        dcb.ByteSize        = (BYTE) ByteSize;

        // -------------- modified 2005-05-16 ---------------
        dcb.fInX            = false;
        dcb.fOutX           = false;

        // no hardware handshake support
        dcb.fOutxDsrFlow    = false;
        dcb.fOutxCtsFlow    = false;
        //
        /* Turn off RTS and DTR at start */
        dcb.fRtsControl     = RTS_CONTROL_DISABLE;
        dcb.fDtrControl     = DTR_CONTROL_DISABLE;
        //
        /* ----------------- misc parameters ----- */
        dcb.fErrorChar      = false;
        dcb.fBinary         = true;
        dcb.fNull           = false;
        dcb.fAbortOnError   = false;
        dcb.wReserved       = 0;
        dcb.XonLim          = 2;
        dcb.XoffLim         = 4;
        dcb.XonChar         = 0x13;
        dcb.XoffChar        = 0x19;
        dcb.EvtChar         = 0;

        ovReader.hEvent    = serial_events[SIG_READER];
        ovWriter.hEvent    = serial_events[SIG_WRITER];
        ovWaitEvent.hEvent = serial_events[SIG_MODEM_EVENTS];

        if (serial_handle != INVALID_HANDLE_VALUE) {
            if (check_modem) {
                if(!SetCommMask(serial_handle, EV_RING | EV_RLSD |EV_CTS | EV_DSR | EV_BREAK | EV_ERR))
                	// 
                    erreur = 1;
            	} else {
                if(!SetCommMask(serial_handle, 0))
                    erreur = 1;
            	}
            // set timeouts
            if(!SetCommTimeouts(serial_handle,&cto))
                erreur = 2;

            // set DCB
            if(!SetCommState(serial_handle,&dcb))
                erreur = 4;
        	} else
            erreur = 8;
    	} else
        erreur = 16;


    /* --------------------------------------------- */
    for (i=0; i < SERIAL_SIGNAL_NBR; i++)
    {
        if (serial_events[i] == INVALID_HANDLE_VALUE)
            erreur = 32;
    }

    /* --------------------------------------------- */
    if (erreur != 0)
    {
        CloseHandle(serial_handle);
        serial_handle = INVALID_HANDLE_VALUE;
    }
    else
    {
        // start thread
        thread_handle = (HANDLE) _beginthreadex(NULL,0,
                  (PBEGINTHREADEX_THREADFUNC) Tserial_event_thread_start,
                   this, 0, &threadid);
        /*if (thread_handle == -1)
            thread_handle=0;   */
        SetThreadPriority(thread_handle, THREAD_PRIORITY_TIME_CRITICAL);
    }

    /* --------------------------------------------- */
    return(erreur);
}
/* -------------------------------------------------------------------- */
/* ---------------------           setManager     --------------------- */
/* -------------------------------------------------------------------- */
void         Tserial_event::setManager(type_myCallBack manager_arg)
{
        manager = manager_arg;
}
/* -------------------------------------------------------------------- */
/* ---------------------           setRxSize      --------------------- */
/* -------------------------------------------------------------------- */
void         Tserial_event::setRxSize(int size)
{
        max_rx_size = size;
        if (max_rx_size > SERIAL_MAX_RX)
            max_rx_size = SERIAL_MAX_RX;
}
/* -------------------------------------------------------------------- */
/* ---------------------      getDataInBuffer     --------------------- */
/* -------------------------------------------------------------------- */
char *      Tserial_event::getDataInBuffer(void)
{
    return(rxBuffer);
}
/* -------------------------------------------------------------------- */
/* ------------------           getDataInSize     --------------------- */
/* -------------------------------------------------------------------- */
int      Tserial_event::getDataInSize(void)
{
    return(received_size);
}
/* -------------------------------------------------------------------- */
/* -------------------        dataHasBeenRead     --------------------- */
/* -------------------------------------------------------------------- */
void   Tserial_event::dataHasBeenRead(void)
{
    SetEvent(serial_events[SIG_READ_DONE]);
}
/* -------------------------------------------------------------------- */
/* -----------------------   getNbrOfBytes  --------------------------- */
/* -------------------------------------------------------------------- */
int Tserial_event::getNbrOfBytes    (void)
{
    struct _COMSTAT status;
    int             n;
    unsigned long   etat;

    n = 0;

    if (serial_handle != INVALID_HANDLE_VALUE) {
        ClearCommError(serial_handle, &etat, &status);
        n = status.cbInQue;
    }
    return(n);
}
/* -------------------------------------------------------------------- */
/* -----------------------   getCommError  ---------------------------- */
/* -------------------------------------------------------------------- */
DWORD Tserial_event::getCommError    (void)
{
    struct _COMSTAT status;
    unsigned long   etat;

    etat = 0;

    if (serial_handle != INVALID_HANDLE_VALUE) {
        ClearCommError(serial_handle, &etat, &status);
    }
    return(etat);
}
/* -------------------------------------------------------------------- */
/* ------------------           getThreadId       --------------------- */
/* -------------------------------------------------------------------- */
int Tserial_event::getThreadId(void)
{
    return(threadid);
}
/* -------------------------------------------------------------------- */
/* --------------------------    sendData     ------------------------- */
/* -------------------------------------------------------------------- */

// return 0 on sucess, 1 on failure, 2 on busy

int Tserial_event::sendData (char *buffer, int size)
{
	if (tx_in_progress) {
        return 2;     	// busy
        }

    if ((!tx_in_progress) && (size < SERIAL_MAX_TX) && (buffer != 0)) {
        tx_in_progress = 1;
        memcpy(txBuffer, buffer, size);
        tx_size = size;
        SetEvent(serial_events[SIG_DATA_TO_TX]);
        // indicating data to be sent
        return 0;
    } else {
    return 1;	// failed because of bad parameters passed in
    }
}
/* -------------------------------------------------------------------- */
/* ------------------------    sendDataChar     ----------------------- */
/* -------------------------------------------------------------------- */

// return 0 on sucess, 2 on busy

int Tserial_event::sendDataChar (char d)
{
	if (tx_in_progress) {
        return 2;     	// busy
        }
    tx_in_progress = 1;
    txBuffer[0] = d;
    tx_size = 1;
    SetEvent(serial_events[SIG_DATA_TO_TX]);
    // indicating data to be sent
    return 0;
}
/* -------------------------------------------------------------------- */
/* ---------------------    OnModemEvent      ------------------------- */
/* -------------------------------------------------------------------- */
void Tserial_event::OnModemEvent (unsigned long events)
{
    unsigned long ModemStat;

    if (!GetCommModemStatus(serial_handle, &ModemStat)) {
    	return; // failed
        }

    if ((events & EV_RING)!= 0){
        if ((ModemStat &  MS_RING_ON)!= 0) {
            if (manager != 0)
                manager((uint32) this, SERIAL_RING_ON);
        } else {
            if (manager != 0)
                manager((uint32) this, SERIAL_RING_OFF);
        }
    }
    if ((events & EV_ERR)!= 0) {
        if (manager != 0)
            manager((uint32) this, SERIAL_ERR);
    }

    if ((events & EV_BREAK)!= 0) {
        if (manager != 0)
            manager((uint32) this, SERIAL_BREAK);
    }

    if ((events & EV_RLSD)!= 0) {
        if ((ModemStat &  MS_RLSD_ON)!= 0) {
            if (manager != 0)
                manager((uint32) this, SERIAL_CD_ON);
        } else {
            if (manager != 0)
                manager((uint32) this, SERIAL_CD_OFF);
        }
    }
    if ((events & EV_CTS)!= 0){
        if ((ModemStat &  MS_CTS_ON)!= 0) {
            if (manager!=0)
                manager((uint32) this, SERIAL_CTS_ON);
        } else {
            if (manager != 0)
                manager((uint32) this, SERIAL_CTS_OFF);
        }
    }
    if ((events & EV_DSR)!= 0) {
        if ((ModemStat &  MS_DSR_ON)!= 0) {
            if (manager != 0)
                manager((uint32) this, SERIAL_DSR_ON);
        } else {
            if (manager != 0)
                manager((uint32) this, SERIAL_DSR_OFF);
        }
    }
}
/* -------------------------------------------------------------------- */
/* ------------------------- Read CTS & DSR & CD   -------------------- */
/* -------------------------------------------------------------------- */
bool Tserial_event::CTS_State()
{
    unsigned long ModemStat;

    if (!GetCommModemStatus(serial_handle, &ModemStat)) {
    	return 0; // failed
        }
	return(ModemStat & MS_CTS_ON);
}

bool Tserial_event::DSR_State()
{
    unsigned long ModemStat;

    if (!GetCommModemStatus(serial_handle, &ModemStat)) {
    	return 0; // failed
        }
	return(ModemStat & MS_DSR_ON);
}

bool Tserial_event::CD_State()
{
    unsigned long ModemStat;

    if (!GetCommModemStatus(serial_handle, &ModemStat)) {
    	return 0; // failed
        }
	return(ModemStat & MS_RLSD_ON);
}
/* -------------------------------------------------------------------- */
/* ------------------------- Set RTS & DTR & BREAK   ------------------ */
/* -------------------------------------------------------------------- */
void Tserial_event::RTS(bool state)
{
if (state) {
	EscapeCommFunction(serial_handle, SETRTS);
	} else {
    EscapeCommFunction(serial_handle, CLRRTS);
    }
}
void Tserial_event::DTR(bool state)
{
if (state) {
	EscapeCommFunction(serial_handle, SETDTR);
	} else {
    EscapeCommFunction(serial_handle, CLRDTR);
    }
}
void Tserial_event::BREAK(bool state)
{
if (state) {
	EscapeCommFunction(serial_handle, SETBREAK);
	} else {
    EscapeCommFunction(serial_handle, CLRBREAK);
    }
}
/* -------------------------------------------------------------------- */
/* --------------------------       run       ------------------------- */
/* -------------------------------------------------------------------- */

/**
 this function is the main loop of the Tserial object. There is a
 do while() loop executed until either an error or a PowerDown is 
 received.
 this is not a busy wait since we use the WaitForMultipleObject function
*/

/* */
//#define DEBUG_EVENTS
/* */

void Tserial_event::run(void)
{
    bool          done;
    long          status;
    unsigned long read_nbr, result_nbr;
    char          success;

    ready                   = true;
    done                    = false;
    tx_in_progress          = 0;
    rx_in_progress          = 0;
    WaitCommEventInProgress = 0;
    DoneReason				= 100;	// default reason -

    if (manager != 0)
        manager((uint32) this, SERIAL_CONNECTED);

    GetLastError();               // just to clear any pending error
    SetEvent(serial_events[SIG_READ_DONE]);

    if (check_modem) {     // start checking the Modem control lines if requested
        SetEvent(serial_events[SIG_MODEM_CHECKED]);
	}

    /* ----------------------------------------------------------- */
    while(!done)
    {
        /* ------------------------------------------------------------------ */
        /*                                                                    */
        /*                          Waiting  for signals                      */
        /*                                                                    */
        /* ------------------------------------------------------------------ */
        // Main wait function. Waiting for something to happen.
        // This may be either the completion of a Read or a Write or
        // the reception of modem events, Power Down, new Tx
        //
        status = WaitForMultipleObjects(SERIAL_SIGNAL_NBR, serial_events,
                                        FALSE, INFINITE);

        // processing answer to filter other failures
        status = status - WAIT_OBJECT_0;
		if ((status < 0) || (status >= SERIAL_SIGNAL_NBR)) {
			done=true;   // error
            DoneReason = 1;
			}
        else
        {
            /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
            /* ++++++++++++++++++++ EVENT DISPATCHER ++++++++++++++++++ */
            /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
            switch(status)
            {
                /* ######################################################## */
                case SIG_POWER_DOWN:
                    // receiving a POWER down signal. Stopping the thread
                    done = true;
		            DoneReason = 2;
                    break;
                /* ######################################################## */
                /* #                                                      # */
                /* #                                                      # */
                /* #                       RX                             # */
                /* #                                                      # */
                /* #                                                      # */
                /* ######################################################## */
                case SIG_READ_DONE:
                    // previous reading is finished
                    // I start a new one here
                    if (!rx_in_progress)
                    {
                        // locking reading
                        rx_in_progress = 1;
                        // starting a new read
                        success = (char) ReadFile(serial_handle,&rxBuffer,
                                         max_rx_size,&read_nbr,&ovReader);
                        if (!success)
                        {
                            // failure
                            if(GetLastError() != ERROR_IO_PENDING )
                            {
                                // real failure => quiting
                                done = true;
               		            DoneReason = 3;
                                #ifdef DEBUG_EVENTS
                                Debug Events code not used
                                printf("Readfile error (not pending)\n");
                                #endif DEBUG_EVENTS
                            }
                            #ifdef DEBUG_EVENTS
                            else
                                printf("ReadFile pending\n");
                            #endif DEBUG_EVENTS
                        }
                        #ifdef DEBUG_EVENTS
                        else
                        {
                            // I make nothing here since the overlapped
                            // will be signaled anyway, so I'll make
                            // the processing there
                            printf("ReadFile immediate success\n");
                        }
                        #endif
                    }
                    break;
                /* ######################################################## */
                case SIG_READER:
                    // reading the result of the terminated read
                    //BOOL GetOverlappedResult(
                    //    HANDLE hFile,	// handle of file, pipe, or communications device
                    //    LPOVERLAPPED lpOverlapped,	// address of overlapped structure
                    //    LPDWORD lpNumberOfBytesTransferred,	// address of actual bytes count
                    //    BOOL bWait 	// wait flag
                    //   );
                    //
                    if (GetOverlappedResult(serial_handle, &ovReader,
                        &result_nbr, FALSE))
                    {
                        #ifdef DEBUG_EVENTS
                            printf("ReadFile => GetOverlappedResult done\n");
                        #endif DEBUG_EVENTS
                        // no error => OK
                        // Read operation completed successfully
                        ResetEvent(serial_events[SIG_READER]);
                        // Write operation completed successfully
                        received_size  = result_nbr;
                        rx_in_progress = 0; // read has ended
                        // if incoming data, I process them
                        if ((result_nbr != 0) &&(manager != 0))
                            manager((uint32) this, SERIAL_DATA_ARRIVAL);
                        // I automatically restart a new read once the
                        // previous is completed.
                        //SetEvent(serial_events[SIG_READ_DONE]);
                        // BUG CORRECTION 02.06.22
                    }
                    else
                    {
                        // GetOverlapped didn't succeed !
                        // What's the reason ?
                        if(GetLastError()!= ERROR_IO_PENDING ) {
                            done = true;  // failure
				            DoneReason = 4;
                            }
                    }
                    break;
                /* ######################################################## */
                /* #                                                      # */
                /* #                                                      # */
                /* #                       TX                             # */
                /* #                                                      # */
                /* #                                                      # */
                /* ######################################################## */
                case SIG_DATA_TO_TX:
                    // Signal asserted that there is a new valid message
                    // in the "txBuffer" variable
                    // sending data to the port
                    success = (char) WriteFile(serial_handle, txBuffer, tx_size,
                                        &result_nbr, &ovWriter);
                        if (!success)
                        {
                            // ouups, failure
                            if(GetLastError() != ERROR_IO_PENDING )
                            {
                                // real failure => quiting
                                done = true;
               		            DoneReason = 5;
                                #ifdef DEBUG_EVENTS
                                printf("WriteFile error (not pending)\n");
                                #endif DEBUG_EVENTS
                            }
                            #ifdef DEBUG_EVENTS
                            else
                                printf("WriteFile pending\n");
                            #endif DEBUG_EVENTS
                        }
                        #ifdef DEBUG_EVENTS
                        else
                        {
                            // I make nothing here since the overlapped
                            // will be signaled anyway, so I'll make
                            // the processing there
                            printf("WriteFile immediate success\n");
                        }
                        #endif
                    break;
                /* ######################################################## */
                case SIG_WRITER:
                    // WriteFile has terminated
                    // checking the result of the operation
                    if (GetOverlappedResult(serial_handle, &ovWriter,
                        &result_nbr, FALSE))
                    {
                        // Write operation completed successfully
                        ResetEvent(serial_events[SIG_WRITER]);
                        // further write are now allowed
                        tx_in_progress = 0;
                        // telling it to the manager
                        if (manager != 0)
                            manager((uint32) this, SERIAL_DATA_SENT);
                    }
                    else
                    {
                        // GetOverlapped didn't succeed !
                        // What's the reason ?
                        if(GetLastError() != ERROR_IO_PENDING ) {
                            done = true;  // failure
				            DoneReason = 6;
							}
                    }
                    break;
                /* ######################################################## */
                /* #                                                      # */
                /* #                                                      # */
                /* #             MODEM_EVENTS EVENTS                      # */
                /* #                                                      # */
                /* #                                                      # */
                /* ######################################################## */
                case SIG_MODEM_CHECKED:
                    if ((!WaitCommEventInProgress) && check_modem)
                    // if no wait is in progress I start a new one
                    {            
                        WaitCommEventInProgress=1;
                        success = (char) WaitCommEvent(serial_handle,&dwCommEvent,
                                                       &ovWaitEvent);
                        // reading one byte only to have immediate answer on each byte
                        if (!success)
                        {
                            // failure
                            if(GetLastError() != ERROR_IO_PENDING )
                            {
                                // real failure => quiting
	                            done = true;
					            DoneReason = 7;
                                #ifdef DEBUG_EVENTS
                                printf("WaitCommEvent error (not pending)\n");
                                #endif DEBUG_EVENTS
                            }
                            #ifdef DEBUG_EVENTS
                            else
                                printf("WaitCommEvent pending\n");
                            #endif DEBUG_EVENTS
                        }
                        #ifdef DEBUG_EVENTS
                        else
                        {
                            // I make nothing here since the overlapped
                            // will be signaled anyway, so I'll make
                            // the processing there
                            printf("WaitCommEvent immediate success\n");
                        }
                        #endif
                    }
                    break;
                /* ######################################################## */
                case SIG_MODEM_EVENTS:
                    // reading the result of the terminated wait
                    if (GetOverlappedResult(serial_handle, &ovWaitEvent,
                        &result_nbr, FALSE))
                    {
                        // Wait operation completed successfully
                        ResetEvent(serial_events[SIG_MODEM_EVENTS]);
                        WaitCommEventInProgress = 0;
                        // if incoming data, I process them
                        OnModemEvent(dwCommEvent);
                        // automatically starting a new check
                        SetEvent(serial_events[SIG_MODEM_CHECKED]);
                    }
                    else
                    {
                        // GetOverlapped didn't succeed !
                        // What's the reason ?
                        if(GetLastError() != ERROR_IO_PENDING ) {
                            done = true;  // failure
				            DoneReason = 8;
                            }
                    }
                    break;
                /* ######################################################## */
            }
            /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
            /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
            /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
        }
    };

    // --------------------- Disconnecting ----------------
    // this is disconnecting on SIG_POWER_DOWN or on Error (when "DoneReason" is set to cause of error)
    ready = false;
    if (serial_handle != INVALID_HANDLE_VALUE)
        CloseHandle(serial_handle);
    serial_handle = INVALID_HANDLE_VALUE;

    if (manager != 0)
        manager((uint32) this, SERIAL_DISCONNECTED);
}
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */


