/* * $Id$ * * serial interface threads for gnokii on win32 * Based on the MS sample program 'tty.c' * * Roger Willcocks 16 Sept 99 * * compile with: * cl -Zi -DVERSION=\"win32\" \ * 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 * * $Log$ * Revision 1.1.1.1 2001/11/25 21:59:24 short * :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001 * * Revision 1.7 2001/03/13 01:23:19 pkot * Windows updates (Manfred Jonsson) * * Revision 1.6 2000/12/19 16:18:19 pkot * configure script updates and added shared function for configfile reading * * */ #ifdef WIN32 #define USECOMM // yes, we need the COMM API #include #include #include #include #include #include #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 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) 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) { keepalive ka = (keepalive)lpData; while (isConnected) (*ka)(); KAThreadID = 0; return 0; } //--------------------------------------------------------------------------- // void device_changespeed() // // Description: // This routines sets up the DCB based on settings in the // TTY info structure and performs a SetCommState(). // // Parameters: // //--------------------------------------------------------------------------- void device_changespeed(int speed) { BOOL fRetVal; DCB dcb; dcb.DCBlength = sizeof(DCB); GetCommState(hPhone, &dcb); dcb.BaudRate = speed; fRetVal = SetCommState(hPhone, &dcb); if (fRetVal == 0) fRetVal = GetLastError(); return; } // end of device_changespeed(int speed) //--------------------------------------------------------------------------- // void device_setdtrrts() // // Description: // This routines sets up the DCB based on settings in the // TTY info structure and performs a SetCommState(). // // Parameters: // //--------------------------------------------------------------------------- void device_setdtrrts(int dtr, int rts) { /* FIXME Oops not implemented */ return; } #endif /* end */