X-Git-Url: https://git.jankratochvil.net/?a=blobdiff_plain;f=common%2Fdevices%2Fwinserial.c;fp=common%2Fdevices%2Fwinserial.c;h=488468b5fcf84dc1e799b3157986a3e041681c81;hb=5d49fe8eea4e11457b4f9904eb0d5db435679841;hp=0000000000000000000000000000000000000000;hpb=a51604573a1dfaaad4304ffbf918e91617acf468;p=gnokii.git diff --git a/common/devices/winserial.c b/common/devices/winserial.c new file mode 100644 index 0000000..488468b --- /dev/null +++ b/common/devices/winserial.c @@ -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 +#include +#include +#include +#include + +#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, "", dwError); + ClearCommError(hPhone, &dwErrorFlags, &ComStat); + if (dwErrorFlags > 0) + fprintf(stderr, "", dwErrorFlags); + break; + } + + } + + } + else { + // some other error occurred + dwLength = 0; + ClearCommError(hPhone, &dwErrorFlags, &ComStat); + if (dwErrorFlags > 0) + fprintf(stderr, "", 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, "", dwError); + ClearCommError(hPhone, &dwErrorFlags, &ComStat); + if (dwErrorFlags > 0) + fprintf(stderr, "", 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, "", 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 */