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