2 * serial interface threads for gnokii on win32
3 * Based on the MS sample program 'tty.c'
5 * Roger Willcocks 16 Sept 99
8 * cl -Zi -DWIN32 -DVERSION=\"win32\" -DMODEL=\"6110\" -DPORT=\"COM1\" \
9 * gnokii.c winserial.c fbus-6110.c getopt.c gsm-api.c fbus-6110-auth.c \
10 * fbus-6110-ringtones.c gsm-networks.c cfgreader.c
15 #define USECOMM // yes, we need the COMM API
23 #include "devices/winserial.h"
25 #define sleep(x) Sleep((x) * 1000)
26 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
28 #define BAUDRATE CBR_115200
30 DWORD FAR PASCAL CommWatchProc(LPSTR lpData);
31 DWORD FAR PASCAL KeepAliveProc(LPSTR lpData);
32 BOOL SetupConnection();
35 //---------------------------------------------------------------------------
36 // BOOL OpenConnection(char *szPort)
39 // Opens communication port.
40 // It also sets the CommState and notifies the window via
41 // the fConnected flag.
44 // - szPort - name of com port eg "com1"
46 //---------------------------------------------------------------------------
48 OVERLAPPED osWrite, osRead;
49 HANDLE hPhone, hThread, hKAThread;
51 DWORD ThreadID, KAThreadID;
53 BOOL OpenConnection(char *szPort, sigcallback fn, keepalive ka)
56 HANDLE hCommWatchThread;
58 COMMTIMEOUTS CommTimeOuts;
61 CreateFile(szPort, GENERIC_READ | GENERIC_WRITE,
62 0, // exclusive access
63 NULL, // no security attrs
65 FILE_ATTRIBUTE_NORMAL |
66 FILE_FLAG_OVERLAPPED, // overlapped I/O
67 NULL)) == (HANDLE) -1)
70 // get any early notifications
72 SetCommMask(hPhone, EV_RXCHAR);
74 // setup device buffers
76 SetupComm(hPhone, 4096, 4096);
78 // purge any information in the buffer
80 PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
81 PURGE_TXCLEAR | PURGE_RXCLEAR);
83 // set up for overlapped I/O
85 CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
86 CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
87 CommTimeOuts.ReadTotalTimeoutConstant = 1000;
89 // CBR_9600 is approximately 1byte/ms. For our purposes, allow
90 // double the expected time per character for a fudge factor.
91 CommTimeOuts.WriteTotalTimeoutMultiplier = 2*CBR_9600/BAUDRATE;
93 CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
95 CommTimeOuts.WriteTotalTimeoutConstant = 0;
96 SetCommTimeouts(hPhone, &CommTimeOuts);
99 fRetVal = SetupConnection();
104 // Create a secondary thread to issue keepAlive packets
106 /* Marcin Wiacek. In this moment there is NO method of communication,
107 which require keepalive packets. I comment this thread, because it
108 makes only CPU time (100%) and makes it more hot */
109 // hKAThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
111 // (LPTHREAD_START_ROUTINE) KeepAliveProc,
116 // isConnected = FALSE;
117 // CloseHandle(hPhone);
121 // Create a secondary thread
122 // to watch for an event.
124 hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
126 (LPTHREAD_START_ROUTINE) CommWatchProc,
129 if (!hCommWatchThread)
136 ThreadID = dwThreadID;
137 hThread = hCommWatchThread;
141 EscapeCommFunction(hPhone, SETDTR);
151 } // end of OpenConnection()
153 //---------------------------------------------------------------------------
154 // BOOL SetupConnection()
157 // This routines sets up the DCB based on settings in the
158 // TTY info structure and performs a SetCommState().
162 //---------------------------------------------------------------------------
164 BOOL SetupConnection()
169 dcb.DCBlength = sizeof(DCB);
171 GetCommState(hPhone, &dcb);
173 dcb.BaudRate = BAUDRATE;
175 dcb.Parity = NOPARITY;
176 dcb.StopBits = ONESTOPBIT;
180 dcb.fOutxDsrFlow = 0;
181 dcb.fDtrControl = DTR_CONTROL_ENABLE;
185 dcb.fOutxCtsFlow = 0;
186 dcb.fRtsControl = RTS_CONTROL_DISABLE;
188 // no software flow control
190 dcb.fInX = dcb.fOutX = 0;
192 fRetVal = SetCommState(hPhone, &dcb);
195 fRetVal = GetLastError();
199 } // end of SetupConnection()
201 //---------------------------------------------------------------------------
202 // BOOL CloseConnection()
205 // Closes the connection to the port. Resets the connect flag
209 //---------------------------------------------------------------------------
211 BOOL CloseConnection()
215 // block until keepalive thread terminates (should wake it really)
217 /* Marcin Wiacek. In this moment there is NO method of communication,
218 which require keepalive packets. I comment this thread, because it
219 makes only CPU time (100%) and makes it more hot */
220 // WaitForSingleObject( hKAThread, INFINITE );
221 #ifdef _NEVER_SAY_NEVER_AGAIN_
222 // while (KAThreadID != 0)
226 // disable event notification and wait for thread
229 SetCommMask(hPhone, 0);
231 // block until thread has been halted
233 WaitForSingleObject( hThread, INFINITE );
234 #ifdef _NEVER_SAY_NEVER_AGAIN_
241 EscapeCommFunction(hPhone, CLRDTR);
243 // purge any outstanding reads/writes and close device handle
245 PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
246 PURGE_TXCLEAR | PURGE_RXCLEAR);
251 } // end of CloseConnection()
253 //---------------------------------------------------------------------------
254 // int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
257 // Reads a block from the COM port and stuffs it into
258 // the provided buffer.
263 // block used for storage
266 // max length of block to read
268 //---------------------------------------------------------------------------
270 int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
278 // only try to read number of bytes in queue
279 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
280 dwLength = min((DWORD) nMaxLength, ComStat.cbInQue);
283 fReadStat = ReadFile(hPhone, lpszBlock,
284 dwLength, &dwLength, &osRead);
286 if (GetLastError() == ERROR_IO_PENDING) {
287 fprintf(stderr, "\n\rIO Pending");
288 // We have to wait for read to complete.
289 // This function will timeout according to the
290 // CommTimeOuts.ReadTotalTimeoutConstant variable
291 // Every time it times out, check for port errors
292 while(!GetOverlappedResult(hPhone,
293 &osRead, &dwLength, TRUE)) {
294 dwError = GetLastError();
295 if(dwError == ERROR_IO_INCOMPLETE)
296 // normal result if not finished
299 // an error occurred, try to recover
300 fprintf(stderr, "<CE-%u>", dwError);
301 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
302 if (dwErrorFlags > 0)
303 fprintf(stderr, "<CE-%u>", dwErrorFlags);
311 // some other error occurred
313 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
314 if (dwErrorFlags > 0)
315 fprintf(stderr, "<CE-%u>", dwErrorFlags);
322 } // end of ReadCommBlock()
324 //---------------------------------------------------------------------------
325 // BOOL WriteCommBlock(BYTE *pByte, DWORD dwBytesToWrite)
328 // Writes a block of data to the COM port specified in the associated
329 // TTY info structure.
334 // pointer to data to write to port
336 //---------------------------------------------------------------------------
338 BOOL WriteCommBlock(LPSTR lpByte , DWORD dwBytesToWrite)
342 DWORD dwBytesWritten;
348 fWriteStat = WriteFile(hPhone, lpByte, dwBytesToWrite,
349 &dwBytesWritten, &osWrite);
351 // Note that normally the code will not execute the following
352 // because the driver caches write operations. Small I/O requests
353 // (up to several thousand bytes) will normally be accepted
354 // immediately and WriteFile will return true even though an
355 // overlapped operation was specified
358 if(GetLastError() == ERROR_IO_PENDING) {
359 // We should wait for the completion of the write operation
360 // so we know if it worked or not
362 // This is only one way to do this. It might be beneficial to
363 // place the write operation in a separate thread
364 // so that blocking on completion will not negatively
365 // affect the responsiveness of the UI
367 // If the write takes too long to complete, this
368 // function will timeout according to the
369 // CommTimeOuts.WriteTotalTimeoutMultiplier variable.
370 // This code logs the timeout but does not retry
373 while(!GetOverlappedResult(hPhone,
374 &osWrite, &dwBytesWritten, TRUE)) {
375 dwError = GetLastError();
376 if(dwError == ERROR_IO_INCOMPLETE)
378 // normal result if not finished
379 dwBytesSent += dwBytesWritten;
383 // an error occurred, try to recover
384 fprintf(stderr, "<CE-%u>", dwError);
385 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
386 if (dwErrorFlags > 0)
387 fprintf(stderr, "<CE-%u>", dwErrorFlags);
392 dwBytesSent += dwBytesWritten;
394 if(dwBytesSent != dwBytesToWrite)
395 fprintf(stderr, "\nProbable Write Timeout: Total of %ld bytes sent (%ld)", dwBytesSent, dwBytesToWrite);
397 fprintf(stderr, "\n%ld bytes written", dwBytesSent);
401 // some other error occurred
402 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
403 if (dwErrorFlags > 0)
404 fprintf(stderr, "<CE-%u>", dwErrorFlags);
410 } // end of WriteCommBlock()
413 //************************************************************************
414 // DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
417 // A secondary thread that will watch for COMM events.
421 // 32-bit pointer argument
423 //************************************************************************
425 DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
430 BYTE abIn[ MAXBLOCK + 1];
432 sigcallback fn = (sigcallback)lpData;
434 memset(&os, 0, sizeof(OVERLAPPED));
436 // create I/O event used for overlapped read
438 os.hEvent = CreateEvent(NULL, // no security
439 TRUE, // explicit reset req
440 FALSE, // initial event reset
442 if (os.hEvent == NULL)
445 if (!SetCommMask(hPhone, EV_RXCHAR))
448 while (isConnected) {
451 WaitCommEvent(hPhone, &dwEvtMask, NULL);
453 if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR) {
455 if ((nLength = ReadCommBlock((LPSTR) abIn, MAXBLOCK))) {
458 for (i = 0; i < nLength; i++)
466 // get rid of event handle
468 CloseHandle(os.hEvent);
470 // clear information in structure (kind of a "we're done flag")
477 } // end of CommWatchProc()
480 DWORD FAR PASCAL KeepAliveProc(LPSTR lpData)
482 /* Marcin Wiacek. In this moment there is NO method of communication,
483 which require keepalive packets. I comment this thread, because it
484 makes only CPU time (100%) and makes it more hot */
485 // keepalive ka = (keepalive)lpData;
486 // while (isConnected)