http://marcin-wiacek.fkn.pl/english/zips/mygnokii.tar.gz
[gnokii.git] / common / devices / winserial.c
1 /*
2  * serial interface threads for gnokii on win32
3  * Based on the MS sample program 'tty.c'
4  *
5  * Roger Willcocks  16 Sept 99
6  *
7  * compile with:
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 
11  */
12
13 #ifdef WIN32
14
15 #define USECOMM      // yes, we need the COMM API
16
17 #include <windows.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <io.h>
21 #include <memory.h>
22
23 #include "devices/winserial.h"
24
25 #define sleep(x) Sleep((x) * 1000)
26 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
27
28 #define BAUDRATE CBR_115200
29
30 DWORD FAR PASCAL CommWatchProc(LPSTR lpData);
31 DWORD FAR PASCAL KeepAliveProc(LPSTR lpData);
32 BOOL SetupConnection();
33 #define MAXBLOCK 1024
34
35 //---------------------------------------------------------------------------
36 //  BOOL OpenConnection(char *szPort)
37 //
38 //  Description:
39 //     Opens communication port.
40 //     It also sets the CommState and notifies the window via
41 //     the fConnected flag.
42 //
43 //  Parameters:
44 //     - szPort - name of com port eg "com1"
45 //
46 //---------------------------------------------------------------------------
47
48 OVERLAPPED osWrite, osRead;
49 HANDLE hPhone, hThread, hKAThread;
50 BOOL   isConnected;
51 DWORD  ThreadID, KAThreadID;
52
53 BOOL OpenConnection(char *szPort, sigcallback fn, keepalive ka)
54 {
55     BOOL       fRetVal;
56     HANDLE        hCommWatchThread;
57     DWORD         dwThreadID;
58     COMMTIMEOUTS  CommTimeOuts;
59
60     if ((hPhone =
61          CreateFile(szPort, GENERIC_READ | GENERIC_WRITE,
62                     0,                    // exclusive access
63                     NULL,                 // no security attrs
64                     OPEN_EXISTING,
65                     FILE_ATTRIBUTE_NORMAL |
66                     FILE_FLAG_OVERLAPPED, // overlapped I/O
67                     NULL)) == (HANDLE) -1)
68         return (FALSE);
69     else {
70         // get any early notifications
71
72         SetCommMask(hPhone, EV_RXCHAR);
73
74         // setup device buffers
75
76         SetupComm(hPhone, 4096, 4096);
77
78         // purge any information in the buffer
79
80         PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
81                   PURGE_TXCLEAR | PURGE_RXCLEAR);
82
83         // set up for overlapped I/O
84
85         CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
86         CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
87         CommTimeOuts.ReadTotalTimeoutConstant = 1000;
88 #if 0
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;
92 #else
93         CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
94 #endif
95         CommTimeOuts.WriteTotalTimeoutConstant = 0;
96         SetCommTimeouts(hPhone, &CommTimeOuts);
97     }
98
99     fRetVal = SetupConnection();
100
101     if (fRetVal) {
102         isConnected = TRUE;
103
104         // Create a secondary thread to issue keepAlive packets
105
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,
110 //                              0,
111 //                              (LPTHREAD_START_ROUTINE) KeepAliveProc,
112 //                              (LPVOID) ka,
113 //                              0, &KAThreadID );
114 //      if (!hKAThread)
115 //      {
116 //          isConnected = FALSE;
117 //          CloseHandle(hPhone);
118 //          return FALSE;
119 //      }
120  
121         // Create a secondary thread
122         // to watch for an event.
123
124         hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
125                                   0,
126                                   (LPTHREAD_START_ROUTINE) CommWatchProc,
127                                   (LPVOID) fn,
128                                   0, &dwThreadID);
129         if (!hCommWatchThread)
130         {
131             isConnected = FALSE;
132             CloseHandle(hPhone);
133             fRetVal = FALSE;
134         }
135         else {
136             ThreadID = dwThreadID;
137             hThread  = hCommWatchThread;
138
139             // assert DTR
140
141             EscapeCommFunction(hPhone, SETDTR);
142         }
143     }
144     else {
145         isConnected = FALSE;
146         CloseHandle(hPhone);
147     }
148
149     return (fRetVal);
150
151 } // end of OpenConnection()
152
153 //---------------------------------------------------------------------------
154 //  BOOL SetupConnection()
155 //
156 //  Description:
157 //     This routines sets up the DCB based on settings in the
158 //     TTY info structure and performs a SetCommState().
159 //
160 //  Parameters:
161 //
162 //---------------------------------------------------------------------------
163
164 BOOL SetupConnection()
165 {
166     BOOL       fRetVal;
167     DCB        dcb;
168
169     dcb.DCBlength = sizeof(DCB);
170
171     GetCommState(hPhone, &dcb);
172
173    dcb.BaudRate = BAUDRATE;
174    dcb.ByteSize = 8;
175    dcb.Parity = NOPARITY;
176    dcb.StopBits = ONESTOPBIT;
177
178     // set DTS
179
180     dcb.fOutxDsrFlow = 0;
181     dcb.fDtrControl = DTR_CONTROL_ENABLE;
182
183     // clear RTS
184
185     dcb.fOutxCtsFlow = 0;
186     dcb.fRtsControl = RTS_CONTROL_DISABLE;
187
188     // no software flow control
189
190     dcb.fInX = dcb.fOutX = 0;
191
192     fRetVal = SetCommState(hPhone, &dcb);
193
194     if (fRetVal == 0)
195         fRetVal = GetLastError();
196
197     return (fRetVal);
198
199 } // end of SetupConnection()
200
201 //---------------------------------------------------------------------------
202 //  BOOL CloseConnection()
203 //
204 //  Description:
205 //     Closes the connection to the port.  Resets the connect flag
206 //
207 //  Parameters:
208 //
209 //---------------------------------------------------------------------------
210
211 BOOL CloseConnection()
212 {
213     isConnected = FALSE;
214
215     // block until keepalive thread terminates (should wake it really)
216
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)
223 //              Sleep(250);
224 #endif
225
226     // disable event notification and wait for thread
227     // to halt
228
229     SetCommMask(hPhone, 0);
230
231     // block until thread has been halted
232
233     WaitForSingleObject( hThread, INFINITE );
234 #ifdef _NEVER_SAY_NEVER_AGAIN_
235     while(ThreadID != 0)
236                 Sleep(250);
237 #endif
238
239     // drop DTR
240
241     EscapeCommFunction(hPhone, CLRDTR);
242
243     // purge any outstanding reads/writes and close device handle
244
245     PurgeComm(hPhone, PURGE_TXABORT | PURGE_RXABORT |
246               PURGE_TXCLEAR | PURGE_RXCLEAR);
247
248     CloseHandle(hPhone);
249     return (TRUE);
250
251 } // end of CloseConnection()
252
253 //---------------------------------------------------------------------------
254 //  int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
255 //
256 //  Description:
257 //     Reads a block from the COM port and stuffs it into
258 //     the provided buffer.
259 //
260 //  Parameters:
261 //
262 //     LPSTR lpszBlock
263 //        block used for storage
264 //
265 //     int nMaxLength
266 //        max length of block to read
267 //
268 //---------------------------------------------------------------------------
269
270 int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
271 {
272     BOOL       fReadStat;
273     COMSTAT    ComStat;
274     DWORD      dwErrorFlags;
275     DWORD      dwLength;
276     DWORD      dwError;
277
278     // only try to read number of bytes in queue
279     ClearCommError(hPhone, &dwErrorFlags, &ComStat);
280     dwLength = min((DWORD) nMaxLength, ComStat.cbInQue);
281
282     if (dwLength > 0) {
283         fReadStat = ReadFile(hPhone, lpszBlock,
284                              dwLength, &dwLength, &osRead);
285         if (!fReadStat) {
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
297                         continue;
298                     else {
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);
304                         break;
305                     }
306
307                 }
308
309             }
310             else {
311                 // some other error occurred
312                 dwLength = 0;
313                 ClearCommError(hPhone, &dwErrorFlags, &ComStat);
314                 if (dwErrorFlags > 0)
315                     fprintf(stderr, "<CE-%u>", dwErrorFlags);
316             }
317         }
318     }
319
320     return (dwLength);
321
322 } // end of ReadCommBlock()
323
324 //---------------------------------------------------------------------------
325 //  BOOL WriteCommBlock(BYTE *pByte, DWORD dwBytesToWrite)
326 //
327 //  Description:
328 //     Writes a block of data to the COM port specified in the associated
329 //     TTY info structure.
330 //
331 //  Parameters:
332 //
333 //     BYTE *pByte
334 //        pointer to data to write to port
335 //
336 //---------------------------------------------------------------------------
337
338 BOOL WriteCommBlock(LPSTR lpByte , DWORD dwBytesToWrite)
339 {
340
341     BOOL        fWriteStat;
342     DWORD       dwBytesWritten;
343     DWORD       dwErrorFlags;
344     DWORD       dwError;
345     DWORD       dwBytesSent=0;
346     COMSTAT     ComStat;
347
348     fWriteStat = WriteFile(hPhone, lpByte, dwBytesToWrite,
349                            &dwBytesWritten, &osWrite);
350
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
356
357     if (!fWriteStat) {
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
361
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
366
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
371             // the write.
372
373             while(!GetOverlappedResult(hPhone,
374                                        &osWrite, &dwBytesWritten, TRUE)) {
375                 dwError = GetLastError();
376                 if(dwError == ERROR_IO_INCOMPLETE)
377                 {
378                     // normal result if not finished
379                     dwBytesSent += dwBytesWritten;
380                     continue;
381                 }
382                 else {
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);
388                     break;
389                 }
390             }
391
392             dwBytesSent += dwBytesWritten;
393 #if 0
394             if(dwBytesSent != dwBytesToWrite)
395                 fprintf(stderr, "\nProbable Write Timeout: Total of %ld bytes sent (%ld)", dwBytesSent, dwBytesToWrite);
396             else
397                 fprintf(stderr, "\n%ld bytes written", dwBytesSent);
398 #endif
399         }
400         else {
401             // some other error occurred
402             ClearCommError(hPhone, &dwErrorFlags, &ComStat);
403             if (dwErrorFlags > 0)
404                 fprintf(stderr, "<CE-%u>", dwErrorFlags);
405             return (FALSE);
406         }
407     }
408     return (TRUE);
409
410 } // end of WriteCommBlock()
411
412
413 //************************************************************************
414 //  DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
415 //
416 //  Description:
417 //     A secondary thread that will watch for COMM events.
418 //
419 //  Parameters:
420 //     LPSTR lpData
421 //        32-bit pointer argument
422 //
423 //************************************************************************
424
425 DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
426 {
427     DWORD       dwEvtMask;
428     OVERLAPPED  os;
429     int        nLength;
430     BYTE       abIn[ MAXBLOCK + 1];
431
432     sigcallback fn = (sigcallback)lpData;
433
434     memset(&os, 0, sizeof(OVERLAPPED));
435
436     // create I/O event used for overlapped read
437
438     os.hEvent = CreateEvent(NULL,    // no security
439                             TRUE,    // explicit reset req
440                             FALSE,   // initial event reset
441                             NULL); // no name
442     if (os.hEvent == NULL)
443         return (FALSE);
444
445     if (!SetCommMask(hPhone, EV_RXCHAR))
446         return (FALSE);
447
448     while (isConnected) {
449         dwEvtMask = 0;
450
451         WaitCommEvent(hPhone, &dwEvtMask, NULL);
452
453         if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR) {
454             do {
455                 if ((nLength = ReadCommBlock((LPSTR) abIn, MAXBLOCK))) {
456                     int i;
457
458                     for (i = 0; i < nLength; i++)
459                         (*fn)(abIn[i]);
460                 }
461             }
462             while (nLength > 0);
463         }
464     }
465
466     // get rid of event handle
467
468     CloseHandle(os.hEvent);
469
470     // clear information in structure (kind of a "we're done flag")
471
472     ThreadID = 0;
473     hThread = NULL;
474
475     return(TRUE);
476
477 } // end of CommWatchProc()
478
479
480 DWORD FAR PASCAL KeepAliveProc(LPSTR lpData)
481 {
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)
487 //      (*ka)();
488 //    KAThreadID = 0;
489     return 0;
490 }
491
492 #endif
493 /* end */