4 * serial interface threads for gnokii on win32
5 * Based on the MS sample program 'tty.c'
7 * Roger Willcocks 16 Sept 99
10 * cl -Zi -DVERSION=\"win32\" \
11 * gnokii.c winserial.c fbus-6110.c getopt.c gsm-api.c fbus-6110-auth.c \
12 * fbus-6110-ringtones.c gsm-networks.c cfgreader.c
15 * Revision 1.1.1.1 2001/11/25 21:59:24 short
16 * :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
18 * Revision 1.7 2001/03/13 01:23:19 pkot
19 * Windows updates (Manfred Jonsson)
21 * Revision 1.6 2000/12/19 16:18:19 pkot
22 * configure script updates and added shared function for configfile reading
29 #define USECOMM // yes, we need the COMM API
37 #include <win32/winserial.h>
39 #define sleep(x) Sleep((x) * 1000)
40 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
42 #define BAUDRATE CBR_115200
44 DWORD FAR PASCAL CommWatchProc(LPSTR lpData);
45 DWORD FAR PASCAL KeepAliveProc(LPSTR lpData);
46 BOOL SetupConnection();
49 //---------------------------------------------------------------------------
50 // BOOL OpenConnection(char *szPort)
53 // Opens communication port.
54 // It also sets the CommState and notifies the window via
55 // the fConnected flag.
58 // - szPort - name of com port eg "com1"
60 //---------------------------------------------------------------------------
62 OVERLAPPED osWrite, osRead;
63 HANDLE hPhone, hThread, hKAThread;
65 DWORD ThreadID, KAThreadID;
67 BOOL OpenConnection(char *szPort, sigcallback fn, keepalive ka)
70 HANDLE hCommWatchThread;
72 COMMTIMEOUTS CommTimeOuts;
75 CreateFile(szPort, GENERIC_READ | GENERIC_WRITE,
76 0, // exclusive access
77 NULL, // no security attrs
79 FILE_ATTRIBUTE_NORMAL |
80 FILE_FLAG_OVERLAPPED, // overlapped I/O
81 NULL)) == (HANDLE) -1)
84 // get any early notifications
86 SetCommMask(hPhone, EV_RXCHAR);
88 // setup device buffers
90 SetupComm(hPhone, 4096, 4096);
92 // purge any information in the buffer
94 PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
95 PURGE_TXCLEAR | PURGE_RXCLEAR);
97 // set up for overlapped I/O
99 CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
100 CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
101 CommTimeOuts.ReadTotalTimeoutConstant = 1000;
103 // CBR_9600 is approximately 1byte/ms. For our purposes, allow
104 // double the expected time per character for a fudge factor.
105 CommTimeOuts.WriteTotalTimeoutMultiplier = 2*CBR_9600/BAUDRATE;
107 CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
109 CommTimeOuts.WriteTotalTimeoutConstant = 0;
110 SetCommTimeouts(hPhone, &CommTimeOuts);
113 fRetVal = SetupConnection();
118 // Create a secondary thread to issue keepAlive packets
120 hKAThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
122 (LPTHREAD_START_ROUTINE) KeepAliveProc,
132 // Create a secondary thread
133 // to watch for an event.
135 hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
137 (LPTHREAD_START_ROUTINE) CommWatchProc,
140 if (!hCommWatchThread)
147 ThreadID = dwThreadID;
148 hThread = hCommWatchThread;
152 EscapeCommFunction(hPhone, SETDTR);
162 } // end of OpenConnection()
164 //---------------------------------------------------------------------------
165 // BOOL SetupConnection()
168 // This routines sets up the DCB based on settings in the
169 // TTY info structure and performs a SetCommState().
173 //---------------------------------------------------------------------------
175 BOOL SetupConnection()
180 dcb.DCBlength = sizeof(DCB);
182 GetCommState(hPhone, &dcb);
184 dcb.BaudRate = BAUDRATE;
186 dcb.Parity = NOPARITY;
187 dcb.StopBits = ONESTOPBIT;
191 dcb.fOutxDsrFlow = 0;
192 dcb.fDtrControl = DTR_CONTROL_ENABLE;
196 dcb.fOutxCtsFlow = 0;
197 dcb.fRtsControl = RTS_CONTROL_DISABLE;
199 // no software flow control
201 dcb.fInX = dcb.fOutX = 0;
203 fRetVal = SetCommState(hPhone, &dcb);
206 fRetVal = GetLastError();
210 } // end of SetupConnection()
212 //---------------------------------------------------------------------------
213 // BOOL CloseConnection()
216 // Closes the connection to the port. Resets the connect flag
220 //---------------------------------------------------------------------------
222 BOOL CloseConnection()
226 // block until keepalive thread terminates (should wake it really)
228 WaitForSingleObject( hKAThread, INFINITE );
229 #ifdef _NEVER_SAY_NEVER_AGAIN_
230 while (KAThreadID != 0)
234 // disable event notification and wait for thread
237 SetCommMask(hPhone, 0);
239 // block until thread has been halted
241 WaitForSingleObject( hThread, INFINITE );
242 #ifdef _NEVER_SAY_NEVER_AGAIN_
249 EscapeCommFunction(hPhone, CLRDTR);
251 // purge any outstanding reads/writes and close device handle
253 PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
254 PURGE_TXCLEAR | PURGE_RXCLEAR);
259 } // end of CloseConnection()
261 //---------------------------------------------------------------------------
262 // int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
265 // Reads a block from the COM port and stuffs it into
266 // the provided buffer.
271 // block used for storage
274 // max length of block to read
276 //---------------------------------------------------------------------------
278 int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
286 // only try to read number of bytes in queue
287 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
288 dwLength = min((DWORD) nMaxLength, ComStat.cbInQue);
291 fReadStat = ReadFile(hPhone, lpszBlock,
292 dwLength, &dwLength, &osRead);
294 if (GetLastError() == ERROR_IO_PENDING) {
295 fprintf(stderr, "\n\rIO Pending");
296 // We have to wait for read to complete.
297 // This function will timeout according to the
298 // CommTimeOuts.ReadTotalTimeoutConstant variable
299 // Every time it times out, check for port errors
300 while(!GetOverlappedResult(hPhone,
301 &osRead, &dwLength, TRUE)) {
302 dwError = GetLastError();
303 if(dwError == ERROR_IO_INCOMPLETE)
304 // normal result if not finished
307 // an error occurred, try to recover
308 fprintf(stderr, "<CE-%u>", dwError);
309 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
310 if (dwErrorFlags > 0)
311 fprintf(stderr, "<CE-%u>", dwErrorFlags);
319 // some other error occurred
321 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
322 if (dwErrorFlags > 0)
323 fprintf(stderr, "<CE-%u>", dwErrorFlags);
330 } // end of ReadCommBlock()
332 //---------------------------------------------------------------------------
333 // BOOL WriteCommBlock(BYTE *pByte, DWORD dwBytesToWrite)
336 // Writes a block of data to the COM port specified in the associated
337 // TTY info structure.
342 // pointer to data to write to port
344 //---------------------------------------------------------------------------
346 BOOL WriteCommBlock(LPSTR lpByte , DWORD dwBytesToWrite)
350 DWORD dwBytesWritten;
356 fWriteStat = WriteFile(hPhone, lpByte, dwBytesToWrite,
357 &dwBytesWritten, &osWrite);
359 // Note that normally the code will not execute the following
360 // because the driver caches write operations. Small I/O requests
361 // (up to several thousand bytes) will normally be accepted
362 // immediately and WriteFile will return true even though an
363 // overlapped operation was specified
366 if(GetLastError() == ERROR_IO_PENDING) {
367 // We should wait for the completion of the write operation
368 // so we know if it worked or not
370 // This is only one way to do this. It might be beneficial to
371 // place the write operation in a separate thread
372 // so that blocking on completion will not negatively
373 // affect the responsiveness of the UI
375 // If the write takes too long to complete, this
376 // function will timeout according to the
377 // CommTimeOuts.WriteTotalTimeoutMultiplier variable.
378 // This code logs the timeout but does not retry
381 while(!GetOverlappedResult(hPhone,
382 &osWrite, &dwBytesWritten, TRUE)) {
383 dwError = GetLastError();
384 if(dwError == ERROR_IO_INCOMPLETE)
386 // normal result if not finished
387 dwBytesSent += dwBytesWritten;
391 // an error occurred, try to recover
392 fprintf(stderr, "<CE-%u>", dwError);
393 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
394 if (dwErrorFlags > 0)
395 fprintf(stderr, "<CE-%u>", dwErrorFlags);
400 dwBytesSent += dwBytesWritten;
402 if(dwBytesSent != dwBytesToWrite)
403 fprintf(stderr, "\nProbable Write Timeout: Total of %ld bytes sent (%ld)", dwBytesSent, dwBytesToWrite);
405 fprintf(stderr, "\n%ld bytes written", dwBytesSent);
409 // some other error occurred
410 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
411 if (dwErrorFlags > 0)
412 fprintf(stderr, "<CE-%u>", dwErrorFlags);
418 } // end of WriteCommBlock()
421 //************************************************************************
422 // DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
425 // A secondary thread that will watch for COMM events.
429 // 32-bit pointer argument
431 //************************************************************************
433 DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
438 BYTE abIn[ MAXBLOCK + 1];
440 sigcallback fn = (sigcallback)lpData;
442 memset(&os, 0, sizeof(OVERLAPPED));
444 // create I/O event used for overlapped read
446 os.hEvent = CreateEvent(NULL, // no security
447 TRUE, // explicit reset req
448 FALSE, // initial event reset
450 if (os.hEvent == NULL)
453 if (!SetCommMask(hPhone, EV_RXCHAR))
456 while (isConnected) {
459 WaitCommEvent(hPhone, &dwEvtMask, NULL);
461 if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR) {
463 if ((nLength = ReadCommBlock((LPSTR) abIn, MAXBLOCK))) {
466 for (i = 0; i < nLength; i++)
474 // get rid of event handle
476 CloseHandle(os.hEvent);
478 // clear information in structure (kind of a "we're done flag")
485 } // end of CommWatchProc()
488 DWORD FAR PASCAL KeepAliveProc(LPSTR lpData)
490 keepalive ka = (keepalive)lpData;
497 //---------------------------------------------------------------------------
498 // void device_changespeed()
501 // This routines sets up the DCB based on settings in the
502 // TTY info structure and performs a SetCommState().
506 //---------------------------------------------------------------------------
508 void device_changespeed(int speed)
513 dcb.DCBlength = sizeof(DCB);
515 GetCommState(hPhone, &dcb);
517 dcb.BaudRate = speed;
518 fRetVal = SetCommState(hPhone, &dcb);
521 fRetVal = GetLastError();
525 } // end of device_changespeed(int speed)
527 //---------------------------------------------------------------------------
528 // void device_setdtrrts()
531 // This routines sets up the DCB based on settings in the
532 // TTY info structure and performs a SetCommState().
536 //---------------------------------------------------------------------------
538 void device_setdtrrts(int dtr, int rts)
540 /* FIXME Oops not implemented */