view gsm-fw/gpf/tst_drv/usart.c @ 474:6e6d4c1ec733

os_isr.c: os_CreateOSISR(): missed the assignment to .stack
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Fri, 27 Jun 2014 01:20:23 +0000
parents 5d1e22505446
children
line wrap: on
line source

/* 
+------------------------------------------------------------------------------
|  File:       usart.c
+------------------------------------------------------------------------------
|  Copyright 2002 Texas Instruments Berlin, AG 
|                 All rights reserved. 
| 
|                 This file is confidential and a trade secret of Texas 
|                 Instruments Berlin, AG 
|                 The receipt of or possession of this file does not convey 
|                 any rights to reproduce or disclose its contents or to 
|                 manufacture, use, or sell anything it may describe, in 
|                 whole, or in part, without the specific written consent of 
|                 Texas Instruments Berlin, AG. 
+----------------------------------------------------------------------------- 
|  Purpose :  This Modul defines functions for actual or simulated 
|             USART comunication between two PS-Frames.
|             Use US_set_mode() to select actual USART under windows95 or 
|             under windowsNT, or to select simulated USART under win95/NT
+----------------------------------------------------------------------------- 
*/ 



#ifndef __USART_C__
#define __USART_C__
#endif

/*==== INCLUDES ===================================================*/

 #include <windows.h>
#ifndef _VXWORKS_
  #include <stdarg.h>
#endif
#include <stdio.h>
#include "typedefs.h"
#include "usart.h"
#include "printtofile.h"

/*==== CONSTANTS ==================================================*/

#define COM_ERROR		(CE_FRAME | CE_IOE | CE_OVERRUN)
#define XON				0x11
#define XOFF			0x13

#define READER_THREAD_EXIT_CODE 4711
#define USART_BUFFER_SIZE       0x10000   /* 65536 */
#define FILE_MAX_CHUNK          0x0ffff
#define FILE_SLOW_DOWN          0x01

#ifdef _TOOLS_ 
  #define USART_SEND_TIMEOUT 60000
  #define USART_RCV_TIMEOUT 120000
#else  /* _TOOLS_ */
  #define USART_SEND_TIMEOUT INFINITE
  #define USART_RCV_TIMEOUT INFINITE
#endif /* _TOOLS_ */

/*==== TYPES ======================================================*/
typedef struct
{
  UBYTE  Connects;
  UBYTE  Type;
  USHORT CH1_numOfBytes;
  USHORT CH2_numOfBytes;
  UBYTE  CH1_CTS;
  UBYTE  CH2_CTS;
  UBYTE  CH1_data[USART_BUFFER_SIZE];
  UBYTE  CH2_data[USART_BUFFER_SIZE];
} T_USARTStream;

/*==== EXPORT =====================================================*/
/*==== PRIVATE ====================================================*/
/*==== VARIABLES ==================================================*/

static int       m_mode=UT_MODE_NT;
static long int  m_send_timeout=(long int)USART_SEND_TIMEOUT;
static long int  m_rcv_timeout =(long int)USART_RCV_TIMEOUT;
static int first_ut_init = 1;
static FILE*     m_file=NULL;

static OVERLAPPED gWriteOverLap;
static OVERLAPPED gReadOverLap;

static  int   ReaderThreadExitRequest = FALSE;


LOCAL   void  (*ReceiveCallback)(void) = NULL;
static  int   initialized = FALSE;

#ifdef COM_AUTOSEARCH
static  int   P  = 0;
#endif

#ifdef DEBUG_USART
static  int   usart_in, usart_out;
#endif

T_USARTStream *Stream;

HANDLE         SemCH1_full;
HANDLE         SemCH2_full;
HANDLE         SemCH1_empty;
HANDLE         SemCH2_empty;
HANDLE         USARTMemHandle;
HANDLE         *semRCVFull=0, *semRCVEmpty=0;
HANDLE         *semSNDFull=0, *semSNDEmpty=0;
HANDLE         ut_sema_handle;

UBYTE          *InBuffer, *OutBuffer, *readPointer;
USHORT         *InCounter, *OutCounter;
UBYTE          *CTS = NULL;


static HANDLE hComDev = INVALID_HANDLE_VALUE;
static HANDLE hThread = INVALID_HANDLE_VALUE;
static int mem_closed = TRUE;
static int cls_cnt = 0;
static int snd_cnt = 0;

/*==== FUNCTIONS ==================================================*/

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USART                  |
| STATE   : code                    ROUTINE : US_set_mode            |
+--------------------------------------------------------------------+

  PURPOSE : With this function you can select the UART mode 
                #define US_MODE_95         1
                #define US_MODE_NT         2
                #define US_MODE_SIM        3  
                #define US_MODE_FILE       4
  
*/
void US_set_mode(int mode)
{
  m_mode=mode;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USART                  |
| STATE   : code                    ROUTINE : US_set_mode            |
+--------------------------------------------------------------------+

  PURPOSE : With this function get the seleced UART mode  
                #define US_MODE_95         1
                #define US_MODE_NT         2
                #define US_MODE_SIM        3  
                #define US_MODE_FILE       4
*/
int US_get_mode()
{
  return m_mode;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USARTSIM               |
| STATE   : code                    ROUTINE : unlockUSARTMemory      |
+--------------------------------------------------------------------+

  PURPOSE : unlocks the previously locked shared memory area.
  
*/
LOCAL void markRCVBufferEmpty (void)
{
  ReleaseSemaphore (*semRCVEmpty,
                    1,
                    NULL);
}

LOCAL void markSNDBufferFull (void)
{
  ReleaseSemaphore (*semSNDFull,
                    1,
                    NULL);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USARTSIM               |
| STATE   : code                    ROUTINE : waitForSNDBufferEmpty  |
+--------------------------------------------------------------------+

  PURPOSE : waits for send buffer becoming empty

  RETURNS :  0 .. send buffer is empty
            -1 .. given up

*/
LOCAL int waitForSNDBufferEmpty (void)
{
  if (WaitForSingleObject (*semSNDEmpty, m_send_timeout) NEQ WAIT_OBJECT_0)
  {
    int err = GetLastError();
    PrintToFile("USART: error code %d\n", err);
    PrintToFile("USART: giving up sending with %d ms timeout :-(\n", m_send_timeout);
    return -1; /* give up */
  }
  return 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USARTSIM               |
| STATE   : code                    ROUTINE : createUSARTMemory      |
+--------------------------------------------------------------------+

  PURPOSE : Create two pipes for byte oriented data exchange
            between two win32 processes

*/

LOCAL void *createUSARTMemory (char *name, ULONG size)
{
  char newname[40];

  /*
   * create two Semaphores pairs to protect the send data to be
   * overwritten before they have read by the receiver
   */

  sprintf (newname, "%s_CH1empty", name);
  
  SemCH1_empty = CreateSemaphore (NULL,
                                  1,                               
                                  1,                           
                                  newname);
     
  if (SemCH1_empty EQ NULL)
    return NULL;

  sprintf (newname, "%s_CH1full", name);
  
  SemCH1_full = CreateSemaphore (NULL,
                                 0,                               
                                 1,                           
                                 newname);
     
  if (SemCH1_full EQ NULL)
    return NULL;

  sprintf (newname, "%s_CH2empty", name);
  
  SemCH2_empty = CreateSemaphore (NULL,
                                  1,                               
                                  1,                           
                                  newname);
     
  if (SemCH2_empty EQ NULL)
    return NULL;

  sprintf (newname, "%s_CH2full", name);
  
  SemCH2_full = CreateSemaphore (NULL,
                                 0,                               
                                 1,                           
                                 newname);
     
  if (SemCH2_full EQ NULL)
    return NULL;

  /*
   * create a shared memory area
   */
  sprintf (newname, "UT_Mem_%s", name);

  USARTMemHandle
      = CreateFileMapping (
                           (HANDLE) 0xffffffff,  /* memory-mapped     */
                            NULL,                /* no security       */
                            PAGE_READWRITE,      /* read/write access */
                           (DWORD) 0,
     /* memory size     */ (DWORD) size,
                           newname           /* name of sh. mem */
                          ); 
  
  if (USARTMemHandle EQ NULL)
    return NULL;
 
  /*
   * map the shared memory area into the address space of the process
   * and return the startaddress.
   */
  
  return MapViewOfFile (USARTMemHandle,
                        FILE_MAP_WRITE,
                        0,
                        0,
                        0);
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USARTSIM               |
| STATE   : code                    ROUTINE : openUSARTMemory        |
+--------------------------------------------------------------------+

  PURPOSE : open a shared memory area for character exchange
            between two WIN32 processes

*/

LOCAL void *openUSARTMemory (char *name, ULONG size)
{
  char newname[30];

  /*
   * open the Semaphores
   */

  sprintf (newname, "%s_CH1empty", name);

  SemCH1_empty = OpenSemaphore (SEMAPHORE_MODIFY_STATE
                                | SYNCHRONIZE,
                                FALSE,
                                newname);
     
  if (SemCH1_empty EQ NULL)
    return NULL;

  sprintf (newname, "%s_CH1full", name);

  SemCH1_full =  OpenSemaphore (SEMAPHORE_MODIFY_STATE
                                | SYNCHRONIZE,
                                FALSE,
                                newname);
     
  if (SemCH1_full EQ NULL)
    return NULL;

  sprintf (newname, "%s_CH2empty", name);

  SemCH2_empty = OpenSemaphore (SEMAPHORE_MODIFY_STATE
                                | SYNCHRONIZE,
                                FALSE,
                                newname);
     
  if (SemCH2_empty EQ NULL)
    return NULL;

  sprintf (newname, "%s_CH2full", name);

  SemCH2_full =  OpenSemaphore (SEMAPHORE_MODIFY_STATE
                                | SYNCHRONIZE,
                                FALSE,
                                newname);
     
  if (SemCH2_full EQ NULL)
    return NULL;

  /*
   * open the shared memory area
   */
  
  sprintf (newname, "UT_Mem_%s", name);

  USARTMemHandle =
    OpenFileMapping (FILE_MAP_WRITE,
                     FALSE,
                     newname);  /* name of sh. mem */
                 
  
  if (USARTMemHandle EQ NULL)
    return NULL;
 
  /*
   * map the shared memory area into the address space of the process
   * and return the startaddress.
   */
  
  return MapViewOfFile (USARTMemHandle,
                        FILE_MAP_WRITE,
                        0,
                        0,
                        0);
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USART                  |
| STATE   : code                    ROUTINE : waitForRCVBufferFull   |
+--------------------------------------------------------------------+

  PURPOSE : This function waits until an incoming character
            is signaled with the EV_RXCHAR Event 
  
*/

LOCAL void waitForRCVBufferFull (void)
{
  switch (m_mode) {
    case US_MODE_95: {
      static COMSTAT stComStat;
      static DWORD   dwErrors;
      static DWORD   EvtMask = 0;
      BOOL    validReceive = FALSE;

      do
      {
        SetCommMask (hComDev, EV_RXCHAR) ;
        WaitCommEvent (hComDev, &EvtMask, NULL);
        ClearCommError (hComDev, &dwErrors, &stComStat);
  
        if (dwErrors & COM_ERROR)
          PurgeComm (hComDev, PURGE_RXCLEAR|PURGE_RXABORT);
        else
          validReceive = TRUE;

      } while (!validReceive);
      break;
    }
    case US_MODE_NT: {
      static COMSTAT stComStat;
      static DWORD   dwErrors;
      static DWORD   EvtMask = 0;
      BOOL    validReceive = FALSE;

      do
      {
        SetCommMask (hComDev, EV_RXCHAR) ;
        WaitCommEvent (hComDev, &EvtMask, NULL);
        ClearCommError (hComDev, &dwErrors, &stComStat);

        if (dwErrors & COM_ERROR)
          PurgeComm (hComDev, PURGE_RXCLEAR|PURGE_RXABORT);
        else
          validReceive = TRUE;

      } while (!validReceive && !ReaderThreadExitRequest);
      break;
    }
    case US_MODE_SIM: {
      if (WaitForSingleObject (*semRCVFull, m_rcv_timeout) NEQ WAIT_OBJECT_0)
      {
        PrintToFile("USART: no stack connected\n");
      }
      break;
    }
    default:
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USART                  |
| STATE   : code                    ROUTINE : _readerThread          |
+--------------------------------------------------------------------+

  PURPOSE : This function is the central signal handling function. It is
            installed as a thread and waits for the occurance of an
            event and then calls the installed callback function
            of the application.
  
*/

LOCAL void _readerThread (void)
{
  while (ReaderThreadExitRequest == FALSE)
  {
    waitForRCVBufferFull ();
    ReceiveCallback ();
  } 
  ReaderThreadExitRequest = FALSE;
  ExitThread(READER_THREAD_EXIT_CODE);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USART                  |
| STATE   : code                    ROUTINE : convertBaudrate        |
+--------------------------------------------------------------------+

  PURPOSE : This function convert the constants for the baudrates in
            usart.h into the equvalent constants for WIN32 comm.
        
*/
LOCAL DWORD convertBaudrate (DWORD br)
{   
  switch (br)
  {
    case US_BAUD_256000:
      return CBR_256000;

    case US_BAUD_128000:
      return CBR_128000;

    case US_BAUD_115200:
      return CBR_115200;

    case US_BAUD_57600:
      return CBR_57600;

    case US_BAUD_38400:
      return CBR_38400;
    
    case US_BAUD_19200:
      return CBR_19200;

    case US_BAUD_14400:
      return CBR_14400;

    case US_BAUD_9600:
      return CBR_9600;

    case US_BAUD_4800:
      return CBR_4800;

    case US_BAUD_2400:
      return CBR_2400;

    case US_BAUD_1200:
      return CBR_1200;

    case US_BAUD_600:
      return CBR_600;

    case US_BAUD_300:
      return CBR_300;

    default:
      /* no CBR_xxx constant found -> return value itsself */
      return br;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)           MODULE  : USART                  |
| STATE   : code                    ROUTINE : sioInit                |
+--------------------------------------------------------------------+

  PURPOSE : This function opens the given comm port and initiializes
            the DCB with baudrate and flowcontrol parameters.
        
*/

LOCAL BOOL sioInit (int  portNr,
                    unsigned int  baudrate,
                    unsigned int  bufSize,
                    char flowCtrl)
{
  char         szPort[10];
  DCB          stDCB;
  COMMTIMEOUTS stTimeout ;
  DWORD        dwErrorFlags;
  COMSTAT      stComStat;

  sprintf (szPort, "\\\\.\\COM%d", portNr) ;

  // ------------------------------------
  // open the communication device
  // ------------------------------------
  if (m_mode==UT_MODE_NT) {
    hComDev = CreateFile
            ( 
              szPort,
              GENERIC_READ | GENERIC_WRITE,
              0,                       /* exclusive access  */
              NULL,                    /* no security attrs */
              OPEN_EXISTING,
              FILE_FLAG_OVERLAPPED,
              NULL
            );
  } else {
    hComDev = CreateFile
            ( 
              szPort,
              GENERIC_READ | GENERIC_WRITE,
              0,                       // exclusive access
              NULL,                    // no security attrs
              OPEN_EXISTING,
              0,
              NULL
            );
  }

  if (hComDev EQ INVALID_HANDLE_VALUE)
    return FALSE;   // device not available

  if (m_mode==UT_MODE_NT) {
    gReadOverLap.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (!gReadOverLap.hEvent)
	    return ( FALSE );

    gReadOverLap.Offset = 0;
    gReadOverLap.OffsetHigh = 0;

    gWriteOverLap.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (!(gWriteOverLap.hEvent))
	    return ( FALSE );

    gWriteOverLap.Offset = 0;
    gWriteOverLap.OffsetHigh = 0;
  }

  // ------------------------------------
  // get any early notifications
  // ------------------------------------
  SetCommMask (hComDev, EV_RXCHAR);

  // ------------------------------------
  // setup device buffers
  // ------------------------------------
  SetupComm (hComDev,
             bufSize,
             bufSize
            );

  // ------------------------------------
  // purge any information in the buffer
  // ------------------------------------
  PurgeComm (hComDev,
             PURGE_TXABORT | PURGE_RXABORT |
             PURGE_TXCLEAR | PURGE_RXCLEAR
            );

  // ------------------------------------
  // setup up and enable communication
  // device. If not possible close
  // communication and abort function.
  // ------------------------------------
  if (!GetCommState (hComDev, &stDCB))
  {
    SetCommMask (hComDev, 0);
    CloseHandle (hComDev);
    return FALSE;
  }

  stDCB.DCBlength = sizeof (stDCB);  // sizeof(DCB) 

  switch (flowCtrl)
  {
    case 'N':
      stDCB.fOutxCtsFlow = FALSE;
      stDCB.fOutxDsrFlow = FALSE;
      stDCB.fDtrControl = DTR_CONTROL_DISABLE;
      stDCB.fRtsControl = RTS_CONTROL_DISABLE;
      break;
    case 'D':
      stDCB.fOutxCtsFlow = FALSE;
      stDCB.fOutxDsrFlow = TRUE;
      stDCB.fDtrControl = DTR_CONTROL_HANDSHAKE;
      stDCB.fRtsControl = RTS_CONTROL_DISABLE;
      stDCB.XonLim  = 0;
      stDCB.XoffLim = 50;
      break;
    case 'R':
      stDCB.fOutxCtsFlow = TRUE;
      stDCB.fOutxDsrFlow = FALSE;
      stDCB.fDtrControl = DTR_CONTROL_DISABLE;
      stDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
      stDCB.XonLim  = 0;
      stDCB.XoffLim = 50;
      break;
    case 'P':
      stDCB.fOutxCtsFlow = FALSE;
      stDCB.fOutxDsrFlow = FALSE;
      stDCB.fDtrControl = DTR_CONTROL_ENABLE;
      stDCB.fRtsControl = RTS_CONTROL_DISABLE;
      break;
    case 'V':
      if (m_mode==US_MODE_NT) {
        stDCB.fOutxCtsFlow = FALSE;
        stDCB.fOutxDsrFlow = FALSE;
        stDCB.fDtrControl = DTR_CONTROL_ENABLE;
        stDCB.fRtsControl = RTS_CONTROL_ENABLE;
        break;
      }
      /*lint -fallthrough*/
      /* go on if not US_MODE_NT */
    default:
      return FALSE;
  }
  fprintf (stdout,"flow control: %c ...", flowCtrl);

  stDCB.BaudRate          = baudrate;  // current baud rate 
  stDCB.fBinary           = TRUE;      // binary mode, no EOF check 
  stDCB.fParity           = FALSE;     // enable parity checking 
  stDCB.fDsrSensitivity   = FALSE;     // DSR sensitivity 
  stDCB.fTXContinueOnXoff = FALSE;     // XOFF continues Tx 
  stDCB.fOutX             = FALSE;     // XON/XOFF out flow control 
  stDCB.fInX              = FALSE;     // XON/XOFF in flow control 
  stDCB.fErrorChar        = FALSE;     // enable error replacement 
  stDCB.fNull             = FALSE;     // enable null stripping 
  stDCB.fAbortOnError     = FALSE;     // abort reads/writes on error 
  stDCB.ByteSize          = 8;         // number of bits/byte, 4-8 
  stDCB.Parity            = NOPARITY;  // 0-4=no,odd,even,mark,space 
  stDCB.StopBits          = ONESTOPBIT;// 0,1,2 = 1, 1.5, 2 
  stDCB.XonChar           = 0;         // Tx and Rx XON character 
  stDCB.XoffChar          = 0;         // Tx and Rx XOFF character 
  stDCB.ErrorChar         = 0;         // error replacement character 
  stDCB.EofChar           = 0;         // end of input character 
  stDCB.EvtChar           = 0;         // received event character 

  if (!SetCommState (hComDev, &stDCB))
  {
    SetCommMask (hComDev, 0);
    CloseHandle (hComDev);
    return FALSE;
  }

  if (!GetCommTimeouts (hComDev, &stTimeout))
    return FALSE;

  stTimeout.WriteTotalTimeoutConstant   = 0xffff;
  stTimeout.WriteTotalTimeoutMultiplier = 0xffff;
  stTimeout.ReadTotalTimeoutConstant    = 0xffff;
  stTimeout.ReadIntervalTimeout         = 0;
  stTimeout.ReadTotalTimeoutMultiplier  = 0xffff;

  if (!SetCommTimeouts (hComDev, &stTimeout))
    return FALSE;

  PurgeComm (hComDev, PURGE_RXCLEAR
                     |PURGE_TXCLEAR
                     |PURGE_TXABORT
                     |PURGE_RXABORT);

  ClearCommError (hComDev, &dwErrorFlags, &stComStat);

  return TRUE;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : sioRead                    |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

LOCAL BOOL sioRead (BYTE  *bpRXBuffer, // RX Buffer
                    DWORD *pdwLength)  // IN:  Bytes to read
{
  DWORD   dwBytesToRead = *pdwLength ;
  DWORD   dwErrorFlags;
  COMSTAT stComStat;

  if (hComDev == INVALID_HANDLE_VALUE
    OR *pdwLength == 0) 
    return FALSE;  // device not available

  if (m_mode==US_MODE_NT) {
    if (!ReadFile (hComDev,
                 bpRXBuffer,
                 dwBytesToRead,
                 pdwLength,
                 &gReadOverLap))
    {
      // if there was a problem
		  if (GetLastError() == ERROR_IO_PENDING)
   	  {
	  	  /* asynchronous i/o is still in progress         */
		
   			/* do something else for a while                 */
	   		/* check on the results of the asynchronous read */
	    	if (GetOverlappedResult(gReadOverLap.hEvent, &gReadOverLap, 
	    		pdwLength, TRUE))
			    return TRUE;
      }

      ClearCommError(hComDev, &dwErrorFlags, &stComStat);
      return FALSE;
    } else {
      ClearCommError(hComDev, &dwErrorFlags, &stComStat);
    }
  } else {
    if (!ReadFile (hComDev,
                 bpRXBuffer,
                 dwBytesToRead,
                 pdwLength,
                 NULL))
    {
      // if there was a problem
      ClearCommError(hComDev, &dwErrorFlags, &stComStat);
      return FALSE;
    }
  }
#ifdef DEBUG_USART
  {
    char traceBuf[255];
    unsigned int  i;

    traceBuf[0] = '\0';

    if (bpRXBuffer[0] EQ 0xff)
    {
      Sleep(1);
    }

    for (i=0; i<*pdwLength; i++)
    {
      if (!isprint (bpRXBuffer[i]))
      {
        sprintf (traceBuf+strlen(traceBuf),
                 "[%02x]%c",
                 (USHORT) bpRXBuffer[i],
                 ((bpRXBuffer[i] EQ '\n') ? '\n' : ' ')
                 );
      }
      else
        sprintf (traceBuf+strlen(traceBuf),
                 "%c",
                 bpRXBuffer[i]);
      if (strlen (traceBuf) > 200)
      {
        write (usart_in, traceBuf, strlen (traceBuf));
        traceBuf[0] = '\0';
      }
    }
    /*
     * write the string to the tracefile
     */
    write (usart_in, traceBuf, strlen (traceBuf));
  }
#endif
  return TRUE;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : sioWrite                   |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

LOCAL BOOL sioWrite (BYTE  *bpTXBuffer, // TX   Buffer
                     DWORD *pdwLength)  // IN:  Bytes to read
{
  DWORD   dwBytesToSend = *pdwLength, toSend, realySend;
  int     ret;
  DWORD   dwErrorFlags;
  COMSTAT stComStat;
  DWORD   dwNumberOfBytesTransferred;   /* Windows 95:                                       */
                                        /* The number of transferred bytes returned by the   */
                                        /* GetOverlappedResult function is always zero, also */
                                        /* if there are bytes received by the communication  */ 
                                        /* partner via the serial line.                      */

  if (hComDev EQ INVALID_HANDLE_VALUE
    OR !bpTXBuffer
    OR *pdwLength ==0)
     return FALSE; // device not available

  realySend = 0;
  toSend    = dwBytesToSend;

  while (toSend > 0)
  {
    if (m_mode==US_MODE_NT) {
    	if (toSend > 20) {
	    	ret = WriteFile (hComDev,
                       bpTXBuffer+realySend,
                       20,
                       pdwLength,
                       &gWriteOverLap);
		    dwNumberOfBytesTransferred = 20;     
  	  } else {
  		  ret = WriteFile (hComDev,
                       bpTXBuffer+realySend,
                       toSend,
                       pdwLength,
                       &gWriteOverLap);
		    dwNumberOfBytesTransferred = toSend;
	    }
    	/* if there was a problem, or the async. operation's still pending ... */
	    if (!ret)
  	  {
  	  	/* deal with the error code */
	  	  if (GetLastError() == ERROR_IO_PENDING)
  	  	{
	  	  	/* asynchronous i/o is still in progress         */
		
  		  	/* do something else for a while                 */
	  		  /* check on the results of the asynchronous read */
  		  	while (!GetOverlappedResult(gWriteOverLap.hEvent, &gWriteOverLap, pdwLength, TRUE))
	    		{
			    	if(GetLastError() == ERROR_IO_INCOMPLETE) {
    					continue;
            } else {
				    	break ;
            }
      		}
		    } else {
			    ClearCommError(hComDev, &dwErrorFlags, &stComStat);
    			return FALSE;
		    }
      }
    } else {
      if (toSend > 20) {
        ret = WriteFile (hComDev,
                       bpTXBuffer+realySend,
                       20,
                       pdwLength,
                       NULL);
        dwNumberOfBytesTransferred = 20; /* US_MODE_NT */
      } else {
        ret = WriteFile (hComDev,
                       bpTXBuffer+realySend,
                       toSend,
                       pdwLength,
                       NULL);
  	    dwNumberOfBytesTransferred = toSend; /* US_MODE_NT */
      }
    }

#ifdef DEBUG_USART
    {
      char traceBuf[255];
      unsigned int  i;

      traceBuf[0] = '\0';

      for (i=0; i<*pdwLength; i++)
      {
        if (!isprint (bpTXBuffer[realySend+i]))
        {
          sprintf (traceBuf+strlen(traceBuf),
                   "[%02x]%c",
                   (USHORT) bpTXBuffer[realySend+i],
                   ((bpTXBuffer[realySend+i] EQ '\n') ? '\n': ' ')
                   );
        }
        else
          sprintf (traceBuf+strlen(traceBuf),
                   "%c",
                   bpTXBuffer[realySend+i]);
        if (strlen (traceBuf) > 200)
        {
          write (usart_out, traceBuf, strlen (traceBuf));
          traceBuf[0] = '\0';
        }
      }
      /*
       * write the string to the tracefile
       */
      write (usart_out, traceBuf, strlen (traceBuf));
    }
#endif

    switch (m_mode) {
      case US_MODE_NT:
       	realySend += dwNumberOfBytesTransferred;
      	toSend    -= dwNumberOfBytesTransferred;
        break;
      case US_MODE_95:
        realySend += *pdwLength;
        toSend    -= *pdwLength;
        break;
      default:
        break;
    }
  }

  *pdwLength = dwBytesToSend;

  return TRUE;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_Init                    |
+--------------------------------------------------------------------+

  PURPOSE : initializes the USART driver

  RETURNS : 0 ... initialization succeeded
           -1 ... error

*/


int UT_Init (unsigned int baudRate, int fifoSize, char flow_ctrl, void (func(void)), const char* fname )
{
  if (initialized == TRUE)
  {
		return 0;
  }

  switch (m_mode) 
  {
    case US_MODE_FILE:
      if (!fname || !strlen(fname) || (m_file=fopen(fname,"rb"))==NULL)
      {
        fprintf (stdout, "USART: failed to open %s :-(\n",fname);
        return -1;
      }
      fprintf (stdout, "USART: \"%s\" opened\n",fname);
      break;
    case US_MODE_95:
    case US_MODE_NT: {
      BOOL   ret;
      DWORD  dwThreadID;
#ifdef COM_AUTOSEARCH
      int    from, to;
      int portNr;
#else
      EXTERN int extPort;
#endif

      ReceiveCallback = func;

#ifdef COM_AUTOSEARCH
      if (P NEQ 0) {
        from = to = P;
      } else {
        from = 1;
        to = 4;
      }

      /*
       * try COMn: to COMm: where n is from and m is to
      */
      for (portNr = from; portNr <= to; portNr++)
      {
        fprintf (stdout,"USART: Trying COM%d ... ", portNr);

        if ((ret = sioInit (portNr,
                        convertBaudrate (baudRate),
                        10000 /*fifoSize*/,
                        flow_ctrl)) EQ FALSE) {
          fprintf (stdout, "fail\n");
        } else {
          fprintf (stdout, "success!\n");
          break;
        }
      }

#else
      fprintf (stdout, "USART: Trying COM%d ... ", extPort);

      if ((ret = sioInit (extPort,
                        convertBaudrate (baudRate),
                        10000 /*fifoSize*/,
                        flow_ctrl)) EQ FALSE) {
        fprintf (stdout, "fail\n");
      }
#endif

      if (ret)
      {
        fprintf (stdout, "success!\n");

        if (ReceiveCallback NEQ NULL)
        {
          hThread = CreateThread ((LPSECURITY_ATTRIBUTES) NULL,
                              0,
                              (LPTHREAD_START_ROUTINE) _readerThread,
                              (LPVOID) NULL,
                               0,
                              &dwThreadID
                             );
        }
        initialized = TRUE;
      }
      else {
        fprintf (stdout, "USART: COM-port not free or baudrate not supported !\n");
        return -1;
      }
      break;  
    }

    case US_MODE_SIM: {
      int    i;

      if ( first_ut_init )
      {
        if ( (ut_sema_handle = OpenSemaphore (SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, FALSE, "UT_SIM_SEMA")) == NULL )
        {
          ut_sema_handle = CreateSemaphore (NULL, 1, 1, "UT_SIM_SEMA");
        }
        first_ut_init = 0;
      }

      WaitForSingleObject (ut_sema_handle, INFINITE);

      if ((Stream = (T_USARTStream *) openUSARTMemory
                                      (
                                        (char*)"GSM",
                                        sizeof (T_USARTStream)
                                      )) EQ NULL)
      {
        if ((Stream = (T_USARTStream *) createUSARTMemory
                                      (
                                        (char*)"GSM",
                                        sizeof (T_USARTStream)
                                      )) EQ NULL)
        {
          PrintToFile ("USART: simulation could not create a shared memory area\n");
          return -1;
        }
        PrintToFile ("USART: shared memory area created\n");
  
        Stream->CH1_numOfBytes = 0;
        Stream->CH2_numOfBytes = 0;

        Stream->CH1_CTS = 0;
        Stream->CH2_CTS = 0;
  
        for (i=0; i<USART_BUFFER_SIZE; i++)
        {
          Stream->CH1_data[i] = 0;
          Stream->CH2_data[i] = 0;
        }

        Stream->Connects = 0;  /* init connection counter (!! CURRENTLY NOT USED !!) */
        Stream->Type=1;        /* signaling new type */ 
      }
      else
      {
        PrintToFile ("USART: shared memory area opened\n");
      }

      /* set pointers to semaphores and data buffers */
#ifdef _TOOLS_
      if (Stream->Type==0) // shared mem created by old stack
      {
        PrintToFile ("USART: connecting to old stack !\n");

        Stream->CH1_CTS = 1; // (baudRate NEQ -1); removed because baudrate never negative

        InBuffer      = Stream->CH1_data;
        OutBuffer     = Stream->CH2_data;
        InCounter     = &Stream->CH1_numOfBytes;
        OutCounter    = &Stream->CH2_numOfBytes;
        semRCVFull    = &SemCH1_full;
        semRCVEmpty   = &SemCH1_empty;
        semSNDFull    = &SemCH2_full;
        semSNDEmpty   = &SemCH2_empty;
        CTS           = &Stream->CH2_CTS;
      }
      else // shared mem created by us or new stack
      {
        Stream->CH2_CTS  = 1;  // (baudRate NEQ -1); removed because baudrate never negative 

        InBuffer      = Stream->CH2_data;
        OutBuffer     = Stream->CH1_data;
        InCounter     = &Stream->CH2_numOfBytes;
        OutCounter    = &Stream->CH1_numOfBytes;
        semRCVFull    = &SemCH2_full;
        semRCVEmpty   = &SemCH2_empty;
        semSNDFull    = &SemCH1_full;
        semSNDEmpty   = &SemCH1_empty;
        CTS           = &Stream->CH1_CTS;
      }
#else  /* _TOOLS_ */
      Stream->CH1_CTS = 1;  // (baudRate NEQ -1); removed because baudrate never negative 

      InBuffer      = Stream->CH1_data;
      OutBuffer     = Stream->CH2_data;
      InCounter     = &Stream->CH1_numOfBytes;
      OutCounter    = &Stream->CH2_numOfBytes;
      semRCVFull    = &SemCH1_full;
      semRCVEmpty   = &SemCH1_empty;
      semSNDFull    = &SemCH2_full;
      semSNDEmpty   = &SemCH2_empty;
      CTS           = &Stream->CH2_CTS;
#endif /* _TOOLS_ */

      readPointer = InBuffer;

      ReceiveCallback = func;

      Stream->Connects++;      /* mark connection (!! CURRENTLY NOT USED !!) */
      }
        
      ReleaseSemaphore (ut_sema_handle, 1, NULL);

      break;
    default:
      break;
  }
  initialized = TRUE;
  mem_closed = FALSE;

#ifdef DEBUG_USART
  /*
   * Open protocol file and initialize
   */

  usart_in  = open ("USART.IN", O_WRONLY| O_TEXT| O_TRUNC| O_CREAT, 0666);
  usart_out = open ("USART.OUT", O_WRONLY| O_TEXT| O_TRUNC| O_CREAT, 0666);
#endif

  return 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USARTSIM                   |
| STATE   : code                ROUTINE : UT_Close                   |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL BOOL UT_Close(void)
{

  if (initialized == FALSE)
		return FALSE;

  switch (m_mode) {
    case US_MODE_FILE:
      if (m_file)
      {
        fclose(m_file);
        m_file=NULL;
      }
      break;
    case US_MODE_95:
    case US_MODE_NT: {
    	DWORD ExitCode;
    
      if (ReceiveCallback != NULL) {
    	  /* Initialize stop _readerThread */
    	  ReaderThreadExitRequest = TRUE;
    	  while (ReaderThreadExitRequest == TRUE)
		      SetCommMask (hComDev, 0);
      }

    	/* Close Communication port. */
    	PurgeComm (hComDev,
			   PURGE_TXABORT | PURGE_RXABORT |
			   PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
      if (m_mode==UT_MODE_NT) {
      	CloseHandle (gReadOverLap.hEvent);
    	  gReadOverLap.hEvent = INVALID_HANDLE_VALUE ;
    	  CloseHandle (gWriteOverLap.hEvent);
    	  gWriteOverLap.hEvent = INVALID_HANDLE_VALUE ;
      }
    	CloseHandle (hComDev);
    	hComDev = INVALID_HANDLE_VALUE;
	
      if (ReceiveCallback != NULL) {
      	/* Stop _readerThread */
      	do {
		      GetExitCodeThread(hThread, (LPDWORD) &ExitCode);
    	  }
    	  while (ExitCode == STILL_ACTIVE);
      	CloseHandle (hThread);
      	hThread = INVALID_HANDLE_VALUE;
      }
    
      break;
    }
    case US_MODE_SIM: {
      PrintToFile("USART: shared memory closed (%d)\n",cls_cnt);
      mem_closed = TRUE;
     /* mark disconnection  */
      Stream->Connects=(Stream->Connects>1) ? 1 : 0; /* (!! CURRENTLY NOT USED !!) */

#ifdef _TOOLS_
      if (Stream->Type==0) /* shared mem created by old stack */
      {
        fprintf (stdout, "USART: disconnecting from old stack !\n");
        Stream->CH1_CTS = 0; 
      }
      else /* shared mem created by us or new stack */
      {
        Stream->CH2_CTS = 0; 
      }
#else /* _TOOLS_ */
      Stream->CH1_CTS = 0; 
#endif /* _TOOLS_ */

      CTS = NULL;
      /* close all handles */
      UnmapViewOfFile((void*)Stream);
      CloseHandle(USARTMemHandle);
      CloseHandle(SemCH1_full);
      CloseHandle(SemCH2_full);
      CloseHandle(SemCH1_empty);
      CloseHandle(SemCH2_empty);
      cls_cnt++;
      break;
    default:
      break;
    }
  }

#ifdef DEBUG_USART
 	/* close tracefiles for usart-in and out */
 	close(usart_in);
 	close(usart_out);
#endif

	/* Deinitialize */
	ReceiveCallback = NULL;
  initialized     = FALSE;

	return TRUE;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_InitBlk                 |
+--------------------------------------------------------------------+

  PURPOSE : Initialize the USART for reading blocks

*/

int UT_InitBlk ( unsigned int baudRate, int fifoSize, char flow_ctrl, void *hP)
{
  return UT_Init (baudRate, fifoSize, flow_ctrl, NULL, NULL);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_IsChar                  |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL int UT_IsChar (void)
{
  int ret;

  switch (m_mode) {
    case US_MODE_FILE:
      Sleep(FILE_SLOW_DOWN);
      while (feof(m_file))
      {
        Sleep(1000);
        fseek(m_file,0,SEEK_CUR);
      }
      ret=1;
      break;
    case US_MODE_95:
    case US_MODE_NT: {
      static COMSTAT stComStat;
      static DWORD   dwErrors;

      if (!initialized)
        return FALSE;

      waitForRCVBufferFull ();

      ClearCommError (hComDev, &dwErrors, &stComStat);

      ret= (stComStat.cbInQue > 0);
      break;
    }
    
    case US_MODE_SIM: {
      waitForRCVBufferFull ();
      ret = (*InCounter NEQ 0);

      if (ret EQ 0)
        readPointer = InBuffer;
      break;
    default: 
      ret = 0;
      break;
    }
  }
 
  return ret;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_ReadChar                |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL BYTE UT_ReadChar (void)
{
  BYTE ret=0;

  switch (m_mode) {
    case US_MODE_95:
    case US_MODE_NT: {

      BYTE  buffer[1];
      ULONG bytesRead;

      if (!initialized)
        return 0;

      sioRead (buffer, &bytesRead);

      if (!bytesRead)
        buffer[0] = 0xff;

      ret=buffer[0];
      break;
    }

    case US_MODE_SIM: {
      if (*InCounter NEQ 0)
      {
        ret = *readPointer++;

#ifdef DEBUG_USART
        {
          BYTE buf[20];
          sprintf (buf, "R[1 of %d]: ", *InCounter);
          write (usart_in,
            buf,
            strlen (buf));
          if (isprint (ret))
          {
            sprintf (buf, "%c\n", ret);
          }
          else
          {
            sprintf (buf, "(%02X)\n", ret);
          }
          write (usart_in,
            buf,
            strlen (buf));
		    }
#endif

        (*InCounter)--;

        if (*InCounter EQ 0)
        {
          readPointer = InBuffer;
          markRCVBufferEmpty ();
        }
      }
      break;
    default:
      break;
    }
  }
  return ret;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_ReadNChars              |
+--------------------------------------------------------------------+

  PURPOSE : Called from the HISR - Reads a block of characters
            Parameters : buffer for holding received characters,
                         max. size of buffer
            Returns the number of characters read

*/

GLOBAL ULONG UT_ReadNChars (int usart_id, BYTE *buffer, ULONG bufferSize)
{
  ULONG   bytes;

  switch (m_mode) {
    case US_MODE_FILE:
      if (bufferSize>FILE_MAX_CHUNK)
      {
        bufferSize=FILE_MAX_CHUNK;
      }
      bytes=fread(buffer,1,bufferSize,m_file);
      break;
    case US_MODE_95:
    case US_MODE_NT: {

      COMSTAT stComStat;
      DWORD   dwErrors;

      if (!initialized)
        return 0L;

      ClearCommError (hComDev, &dwErrors, &stComStat);
  
      bytes = MINIMUM (stComStat.cbInQue, bufferSize);

      if (bytes EQ 0)
        return 0L;

      sioRead (buffer, &bytes);
      break;
    }

    case US_MODE_SIM: {
      if ((bytes = MINIMUM (*InCounter, bufferSize)) NEQ 0)
      {
#ifdef DEBUG_USART
        unsigned int i;
        char buf[50];
#endif
    
        memcpy (buffer, readPointer, bytes);

#ifdef DEBUG_USART
        sprintf (buf, "R[%d of %d]: ", bytes, *InCounter);
        write (usart_in, buf, strlen (buf));

        for (i=0; i<bytes; i++)
        {
          if (isprint (buffer[i]))
            sprintf (buf, "%c", buffer[i]);
          else
            sprintf (buf, "(%02X)", buffer[i]);
          write (usart_in,
             buf,
             strlen (buf));
        }
        write (usart_in, "\n", 1);
#endif

        (*InCounter) -= (USHORT)bytes;

        if (*InCounter EQ 0)
        {
          readPointer = InBuffer;
          markRCVBufferEmpty ();
        }
        else {
          readPointer += bytes;
        }
      }
      else {
        markRCVBufferEmpty ();
      }
      break;
    default: bytes = 0;
      break;
    }
  }

  return bytes;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_WriteChar               |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL void UT_WriteChar (int usart_id, char ch)
{
  switch (m_mode) {
    case US_MODE_95:
    case US_MODE_NT: {
      BYTE  buffer[1];
      ULONG bytesWritten = 1;

      if (!initialized)
        return;

      buffer[0] = (BYTE)ch;

      sioWrite (buffer, &bytesWritten);
      break;
    }

    case US_MODE_SIM: {
#ifdef DEBUG_USART
      char buf[50];
#endif

      if ( CTS == NULL || !*CTS)  /* no testtools connected */
      {
        return;
      }

      if (waitForSNDBufferEmpty () != 0)
      {
        markSNDBufferFull ();
        return;   /* we gave up sending to avoid dead lock */
      }
      
#ifdef DEBUG_USART
      sprintf (buf, "W[1]: %02X", ch);
      write (usart_out, buf, strlen (buf));
#endif

      *OutBuffer  = (UBYTE)ch;
      *OutCounter = 1;
   
      markSNDBufferFull ();
      break;
    default:
      break;
    }
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_WriteString             |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL void UT_WriteString (int usart_id, char *s)
{
  switch (m_mode) {
    case US_MODE_95:
    case US_MODE_NT: {
      ULONG bytesWritten = strlen (s);

      if (!initialized)
        return;

      sioWrite ((BYTE *) s, &bytesWritten);
      break;
    }

    case US_MODE_SIM: {
      unsigned int numOfChars;
#ifdef DEBUG_USART
      int i;
      char buf[50];
#endif

      if ( CTS == NULL || !*CTS)  /* no testtools connected */
      {
        return;
      }

      if (waitForSNDBufferEmpty () != 0)
      {
        markSNDBufferFull ();
        return;   /* we gave up sending to avoid dead lock */
      }

      numOfChars = strlen (s);

      memcpy (OutBuffer, s, numOfChars);
      *OutCounter = numOfChars;

#ifdef DEBUG_USART
      sprintf (buf, "W[%d]:", numOfChars);
      write (usart_out, buf, strlen (buf));

      for (i=0; i<numOfChars; i++)
      {
        if (isprint (OutBuffer[i]))
          sprintf (buf, "%c", OutBuffer[i]);
        else
          sprintf (buf, "(%02X)", OutBuffer[i]);
        write (usart_out,
               buf,
               strlen (buf));

      }
      write (usart_out,"\n", 1);
#endif

      markSNDBufferFull ();
      break;
    default:
      break;
    }
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_WriteNChars             |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL void UT_WriteNChars (int usart_id, BYTE *s, unsigned int n)
{
  switch (m_mode) {
    case US_MODE_95:
    case US_MODE_NT: {
      ULONG bytesWritten = (ULONG) n;

      if (!initialized)
        return;

      if (!sioWrite ((BYTE *) s, &bytesWritten))
        fprintf (stderr, "USART: Error1\n");
      if (bytesWritten NEQ (ULONG) n)
        fprintf (stderr, "USART: Error2\n");
      break;
    }

    case US_MODE_SIM: {
#ifdef DEBUG_USART
      int i;
      char buf[50];
#endif
      
      if ( CTS == NULL || !*CTS)  /* no testtools connected */
      {
#ifdef DEBUG_USART
        printf("-");
#endif
        snd_cnt++;
        return;
      }

      if ( mem_closed == TRUE )
      {
        PrintToFile("USART:tried to write on closed memory (%d)\n",snd_cnt);
        return;
      }
      if (waitForSNDBufferEmpty () != 0)
      {
        markSNDBufferFull ();
        PrintToFile("USART: gave up sending\n");
        snd_cnt++;
        return;   /* we gave up sending to avoid dead lock */
      }
      memcpy (OutBuffer, s, n);
      *OutCounter = n;

#ifdef DEBUG_USART
      sprintf (buf, "W[%d]:", n);
      write (usart_out, buf, strlen (buf));

      for (i=0; i<n; i++)
      {
        if (isprint (OutBuffer[i]))
          sprintf (buf, "%c", OutBuffer[i]);
        else
          sprintf (buf, "(%02X)", OutBuffer[i]);
        write (usart_out,
               buf,
               strlen (buf));
      }
      write (usart_out,"\n", 1);
#endif

      if ( mem_closed == TRUE )
      {
        PrintToFile("USART: written on closed memory (%d)\n",snd_cnt);
        snd_cnt++;
        return;
      }
#ifdef DEBUG_USART
      printf("+");
#endif
      markSNDBufferFull ();
      snd_cnt++;
      break;
    default:
      break;
    }
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : USART                      |
| STATE   : code                ROUTINE : UT_SetFlowCtrl             |
+--------------------------------------------------------------------+

  PURPOSE : 

*/

GLOBAL void UT_SetFlowCtrl (char flowCtrl)
{
  switch (m_mode) {
    case US_MODE_95:
    case US_MODE_NT: {
      DCB stDCB;

      if (!GetCommState (hComDev, &stDCB))
        return;

      stDCB.DCBlength = sizeof (stDCB);  // sizeof(DCB) 

      switch (flowCtrl)
      {
        case 'N':
          stDCB.fOutxCtsFlow = FALSE;
          stDCB.fOutxDsrFlow = FALSE;
          stDCB.fDtrControl = DTR_CONTROL_DISABLE;
          stDCB.fRtsControl = RTS_CONTROL_DISABLE;
          break;
        case 'D':
          stDCB.fOutxCtsFlow = FALSE;
          stDCB.fOutxDsrFlow = TRUE;
          stDCB.fDtrControl = DTR_CONTROL_HANDSHAKE;
          stDCB.fRtsControl = RTS_CONTROL_DISABLE;
          stDCB.XonLim  = 0;
          stDCB.XoffLim = 50;
          break;
        case 'R':
          stDCB.fOutxCtsFlow = TRUE;
          stDCB.fOutxDsrFlow = FALSE;
          stDCB.fDtrControl = DTR_CONTROL_DISABLE;
          stDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
          stDCB.XonLim  = 0;
          stDCB.XoffLim = 50;
          break;
        case 'P':
          stDCB.fOutxCtsFlow = FALSE;
          stDCB.fOutxDsrFlow = FALSE;
          stDCB.fDtrControl = DTR_CONTROL_ENABLE;
          stDCB.fRtsControl = RTS_CONTROL_DISABLE;
          break;
        default:
          break;
      }

      SetCommState (hComDev, &stDCB);
      break;
    }

    case US_MODE_SIM:
      break;
    default:
      break;
  }
}