http://marcin-wiacek.fkn.pl/english/zips/mygnokii.tar.gz
[gnokii.git] / common / devices / winserial.c
diff --git a/common/devices/winserial.c b/common/devices/winserial.c
new file mode 100644 (file)
index 0000000..488468b
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * serial interface threads for gnokii on win32
+ * Based on the MS sample program 'tty.c'
+ *
+ * Roger Willcocks  16 Sept 99
+ *
+ * compile with:
+ * cl -Zi -DWIN32 -DVERSION=\"win32\" -DMODEL=\"6110\" -DPORT=\"COM1\" \
+ * gnokii.c winserial.c fbus-6110.c getopt.c gsm-api.c fbus-6110-auth.c \
+ * fbus-6110-ringtones.c gsm-networks.c cfgreader.c 
+ */
+
+#ifdef WIN32
+
+#define USECOMM      // yes, we need the COMM API
+
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+#include <io.h>
+#include <memory.h>
+
+#include "devices/winserial.h"
+
+#define sleep(x) Sleep((x) * 1000)
+#define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
+
+#define BAUDRATE CBR_115200
+
+DWORD FAR PASCAL CommWatchProc(LPSTR lpData);
+DWORD FAR PASCAL KeepAliveProc(LPSTR lpData);
+BOOL SetupConnection();
+#define MAXBLOCK 1024
+
+//---------------------------------------------------------------------------
+//  BOOL OpenConnection(char *szPort)
+//
+//  Description:
+//     Opens communication port.
+//     It also sets the CommState and notifies the window via
+//     the fConnected flag.
+//
+//  Parameters:
+//     - szPort - name of com port eg "com1"
+//
+//---------------------------------------------------------------------------
+
+OVERLAPPED osWrite, osRead;
+HANDLE hPhone, hThread, hKAThread;
+BOOL   isConnected;
+DWORD  ThreadID, KAThreadID;
+
+BOOL OpenConnection(char *szPort, sigcallback fn, keepalive ka)
+{
+    BOOL       fRetVal;
+    HANDLE        hCommWatchThread;
+    DWORD         dwThreadID;
+    COMMTIMEOUTS  CommTimeOuts;
+
+    if ((hPhone =
+        CreateFile(szPort, GENERIC_READ | GENERIC_WRITE,
+                   0,                    // exclusive access
+                   NULL,                 // no security attrs
+                   OPEN_EXISTING,
+                   FILE_ATTRIBUTE_NORMAL |
+                   FILE_FLAG_OVERLAPPED, // overlapped I/O
+                   NULL)) == (HANDLE) -1)
+       return (FALSE);
+    else {
+       // get any early notifications
+
+       SetCommMask(hPhone, EV_RXCHAR);
+
+       // setup device buffers
+
+       SetupComm(hPhone, 4096, 4096);
+
+       // purge any information in the buffer
+
+       PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
+                 PURGE_TXCLEAR | PURGE_RXCLEAR);
+
+       // set up for overlapped I/O
+
+       CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
+       CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
+       CommTimeOuts.ReadTotalTimeoutConstant = 1000;
+#if 0
+       // CBR_9600 is approximately 1byte/ms. For our purposes, allow
+       // double the expected time per character for a fudge factor.
+       CommTimeOuts.WriteTotalTimeoutMultiplier = 2*CBR_9600/BAUDRATE;
+#else
+       CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
+#endif
+       CommTimeOuts.WriteTotalTimeoutConstant = 0;
+       SetCommTimeouts(hPhone, &CommTimeOuts);
+    }
+
+    fRetVal = SetupConnection();
+
+    if (fRetVal) {
+       isConnected = TRUE;
+
+       // Create a secondary thread to issue keepAlive packets
+
+/* Marcin Wiacek. In this moment there is NO method of communication,
+   which require keepalive packets. I comment this thread, because it
+   makes only CPU time (100%) and makes it more hot */
+//     hKAThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
+//                             0,
+//                             (LPTHREAD_START_ROUTINE) KeepAliveProc,
+//                             (LPVOID) ka,
+//                             0, &KAThreadID );
+//     if (!hKAThread)
+//     {
+//         isConnected = FALSE;
+//         CloseHandle(hPhone);
+//         return FALSE;
+//     }
+       // Create a secondary thread
+       // to watch for an event.
+
+       hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
+                                 0,
+                                 (LPTHREAD_START_ROUTINE) CommWatchProc,
+                                 (LPVOID) fn,
+                                 0, &dwThreadID);
+       if (!hCommWatchThread)
+       {
+           isConnected = FALSE;
+           CloseHandle(hPhone);
+           fRetVal = FALSE;
+       }
+       else {
+           ThreadID = dwThreadID;
+           hThread  = hCommWatchThread;
+
+           // assert DTR
+
+           EscapeCommFunction(hPhone, SETDTR);
+       }
+    }
+    else {
+       isConnected = FALSE;
+       CloseHandle(hPhone);
+    }
+
+    return (fRetVal);
+
+} // end of OpenConnection()
+
+//---------------------------------------------------------------------------
+//  BOOL SetupConnection()
+//
+//  Description:
+//     This routines sets up the DCB based on settings in the
+//     TTY info structure and performs a SetCommState().
+//
+//  Parameters:
+//
+//---------------------------------------------------------------------------
+
+BOOL SetupConnection()
+{
+    BOOL       fRetVal;
+    DCB        dcb;
+
+    dcb.DCBlength = sizeof(DCB);
+
+    GetCommState(hPhone, &dcb);
+
+   dcb.BaudRate = BAUDRATE;
+   dcb.ByteSize = 8;
+   dcb.Parity = NOPARITY;
+   dcb.StopBits = ONESTOPBIT;
+
+    // set DTS
+
+    dcb.fOutxDsrFlow = 0;
+    dcb.fDtrControl = DTR_CONTROL_ENABLE;
+
+    // clear RTS
+
+    dcb.fOutxCtsFlow = 0;
+    dcb.fRtsControl = RTS_CONTROL_DISABLE;
+
+    // no software flow control
+
+    dcb.fInX = dcb.fOutX = 0;
+
+    fRetVal = SetCommState(hPhone, &dcb);
+
+    if (fRetVal == 0)
+       fRetVal = GetLastError();
+
+    return (fRetVal);
+
+} // end of SetupConnection()
+
+//---------------------------------------------------------------------------
+//  BOOL CloseConnection()
+//
+//  Description:
+//     Closes the connection to the port.  Resets the connect flag
+//
+//  Parameters:
+//
+//---------------------------------------------------------------------------
+
+BOOL CloseConnection()
+{
+    isConnected = FALSE;
+
+    // block until keepalive thread terminates (should wake it really)
+
+/* Marcin Wiacek. In this moment there is NO method of communication,
+   which require keepalive packets. I comment this thread, because it
+   makes only CPU time (100%) and makes it more hot */
+//    WaitForSingleObject( hKAThread, INFINITE );
+#ifdef _NEVER_SAY_NEVER_AGAIN_
+//    while (KAThreadID != 0)
+//             Sleep(250);
+#endif
+
+    // disable event notification and wait for thread
+    // to halt
+
+    SetCommMask(hPhone, 0);
+
+    // block until thread has been halted
+
+    WaitForSingleObject( hThread, INFINITE );
+#ifdef _NEVER_SAY_NEVER_AGAIN_
+    while(ThreadID != 0)
+               Sleep(250);
+#endif
+
+    // drop DTR
+
+    EscapeCommFunction(hPhone, CLRDTR);
+
+    // purge any outstanding reads/writes and close device handle
+
+    PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
+             PURGE_TXCLEAR | PURGE_RXCLEAR);
+
+    CloseHandle(hPhone);
+    return (TRUE);
+
+} // end of CloseConnection()
+
+//---------------------------------------------------------------------------
+//  int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
+//
+//  Description:
+//     Reads a block from the COM port and stuffs it into
+//     the provided buffer.
+//
+//  Parameters:
+//
+//     LPSTR lpszBlock
+//        block used for storage
+//
+//     int nMaxLength
+//        max length of block to read
+//
+//---------------------------------------------------------------------------
+
+int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
+{
+    BOOL       fReadStat;
+    COMSTAT    ComStat;
+    DWORD      dwErrorFlags;
+    DWORD      dwLength;
+    DWORD      dwError;
+
+    // only try to read number of bytes in queue
+    ClearCommError(hPhone, &dwErrorFlags, &ComStat);
+    dwLength = min((DWORD) nMaxLength, ComStat.cbInQue);
+
+    if (dwLength > 0) {
+       fReadStat = ReadFile(hPhone, lpszBlock,
+                            dwLength, &dwLength, &osRead);
+       if (!fReadStat) {
+           if (GetLastError() == ERROR_IO_PENDING) {
+               fprintf(stderr, "\n\rIO Pending");
+               // We have to wait for read to complete.
+               // This function will timeout according to the
+               // CommTimeOuts.ReadTotalTimeoutConstant variable
+               // Every time it times out, check for port errors
+               while(!GetOverlappedResult(hPhone,
+                                          &osRead, &dwLength, TRUE)) {
+                   dwError = GetLastError();
+                   if(dwError == ERROR_IO_INCOMPLETE)
+                       // normal result if not finished
+                       continue;
+                   else {
+                       // an error occurred, try to recover
+                       fprintf(stderr, "<CE-%u>", dwError);
+                       ClearCommError(hPhone, &dwErrorFlags, &ComStat);
+                       if (dwErrorFlags > 0)
+                           fprintf(stderr, "<CE-%u>", dwErrorFlags);
+                       break;
+                   }
+
+               }
+
+           }
+           else {
+               // some other error occurred
+               dwLength = 0;
+               ClearCommError(hPhone, &dwErrorFlags, &ComStat);
+               if (dwErrorFlags > 0)
+                   fprintf(stderr, "<CE-%u>", dwErrorFlags);
+           }
+       }
+    }
+
+    return (dwLength);
+
+} // end of ReadCommBlock()
+
+//---------------------------------------------------------------------------
+//  BOOL WriteCommBlock(BYTE *pByte, DWORD dwBytesToWrite)
+//
+//  Description:
+//     Writes a block of data to the COM port specified in the associated
+//     TTY info structure.
+//
+//  Parameters:
+//
+//     BYTE *pByte
+//        pointer to data to write to port
+//
+//---------------------------------------------------------------------------
+
+BOOL WriteCommBlock(LPSTR lpByte , DWORD dwBytesToWrite)
+{
+
+    BOOL        fWriteStat;
+    DWORD       dwBytesWritten;
+    DWORD       dwErrorFlags;
+    DWORD      dwError;
+    DWORD       dwBytesSent=0;
+    COMSTAT     ComStat;
+
+    fWriteStat = WriteFile(hPhone, lpByte, dwBytesToWrite,
+                           &dwBytesWritten, &osWrite);
+
+    // Note that normally the code will not execute the following
+    // because the driver caches write operations. Small I/O requests
+    // (up to several thousand bytes) will normally be accepted
+    // immediately and WriteFile will return true even though an
+    // overlapped operation was specified
+
+    if (!fWriteStat) {
+       if(GetLastError() == ERROR_IO_PENDING) {
+           // We should wait for the completion of the write operation
+           // so we know if it worked or not
+
+           // This is only one way to do this. It might be beneficial to
+           // place the write operation in a separate thread
+           // so that blocking on completion will not negatively
+           // affect the responsiveness of the UI
+
+           // If the write takes too long to complete, this
+           // function will timeout according to the
+           // CommTimeOuts.WriteTotalTimeoutMultiplier variable.
+           // This code logs the timeout but does not retry
+           // the write.
+
+           while(!GetOverlappedResult(hPhone,
+                                      &osWrite, &dwBytesWritten, TRUE)) {
+               dwError = GetLastError();
+               if(dwError == ERROR_IO_INCOMPLETE)
+               {
+                   // normal result if not finished
+                   dwBytesSent += dwBytesWritten;
+                   continue;
+               }
+               else {
+                   // an error occurred, try to recover
+                   fprintf(stderr, "<CE-%u>", dwError);
+                   ClearCommError(hPhone, &dwErrorFlags, &ComStat);
+                   if (dwErrorFlags > 0)
+                       fprintf(stderr, "<CE-%u>", dwErrorFlags);
+                   break;
+               }
+           }
+
+           dwBytesSent += dwBytesWritten;
+#if 0
+           if(dwBytesSent != dwBytesToWrite)
+               fprintf(stderr, "\nProbable Write Timeout: Total of %ld bytes sent (%ld)", dwBytesSent, dwBytesToWrite);
+           else
+               fprintf(stderr, "\n%ld bytes written", dwBytesSent);
+#endif
+       }
+       else {
+           // some other error occurred
+           ClearCommError(hPhone, &dwErrorFlags, &ComStat);
+           if (dwErrorFlags > 0)
+               fprintf(stderr, "<CE-%u>", dwErrorFlags);
+           return (FALSE);
+       }
+    }
+    return (TRUE);
+
+} // end of WriteCommBlock()
+
+
+//************************************************************************
+//  DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
+//
+//  Description:
+//     A secondary thread that will watch for COMM events.
+//
+//  Parameters:
+//     LPSTR lpData
+//        32-bit pointer argument
+//
+//************************************************************************
+
+DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
+{
+    DWORD       dwEvtMask;
+    OVERLAPPED  os;
+    int        nLength;
+    BYTE       abIn[ MAXBLOCK + 1];
+
+    sigcallback fn = (sigcallback)lpData;
+
+    memset(&os, 0, sizeof(OVERLAPPED));
+
+    // create I/O event used for overlapped read
+
+    os.hEvent = CreateEvent(NULL,    // no security
+                            TRUE,    // explicit reset req
+                            FALSE,   // initial event reset
+                            NULL); // no name
+    if (os.hEvent == NULL)
+       return (FALSE);
+
+    if (!SetCommMask(hPhone, EV_RXCHAR))
+       return (FALSE);
+
+    while (isConnected) {
+       dwEvtMask = 0;
+
+       WaitCommEvent(hPhone, &dwEvtMask, NULL);
+
+       if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR) {
+           do {
+               if ((nLength = ReadCommBlock((LPSTR) abIn, MAXBLOCK))) {
+                   int i;
+
+                   for (i = 0; i < nLength; i++)
+                       (*fn)(abIn[i]);
+               }
+           }
+           while (nLength > 0);
+       }
+    }
+
+    // get rid of event handle
+
+    CloseHandle(os.hEvent);
+
+    // clear information in structure (kind of a "we're done flag")
+
+    ThreadID = 0;
+    hThread = NULL;
+
+    return(TRUE);
+
+} // end of CommWatchProc()
+
+
+DWORD FAR PASCAL KeepAliveProc(LPSTR lpData)
+{
+/* Marcin Wiacek. In this moment there is NO method of communication,
+   which require keepalive packets. I comment this thread, because it
+   makes only CPU time (100%) and makes it more hot */
+//    keepalive ka = (keepalive)lpData;
+//    while (isConnected)
+//     (*ka)();
+//    KAThreadID = 0;
+    return 0;
+}
+
+#endif
+/* end */