Cygwin support, using SDL.
[btb/d2x.git] / unused / win95 / xtapi.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #pragma off (unreferenced)
16 static char rcsid[] = "$Id: xtapi.c,v 1.1.1.1 2001-01-19 03:30:15 bradleyb Exp $";
17 #pragma on (unreferenced)
18
19
20 #define _WIN32
21 #define WIN95
22 #define WIN32_LEAN_AND_MEAN
23
24 #include <windows.h>
25 #include <windowsx.h>
26 #include <tapi.h>
27
28 #include "pstypes.h"
29 #include "mono.h"
30 #include "error.h"
31 #include "winapp.h"
32 #include "xtapi.h"
33
34
35 /*      The TAPI layer will interface between Descent 2 and Win32 TAPI.
36         
37         The application will go about using XTAPI when wanting to dial out
38         to another player (modem) or when listening for a ring in.
39
40         First an application will initialize the library.
41         Then one needs to find the required device id by enumerating
42         the devices and then selecting a device id.
43         
44         Then we open this device by calling xtapi_lock.  The application
45         can only use this device between lock and unlock.  Also all functions
46         with the device extension will act upon this locked device.
47         When done with a device call xtapi_unlock.
48
49         To dial-out.  Call xtapi_device_dialout.  
50
51 */
52
53 #define TAPI_VERSION 0x00010004
54
55
56
57 /*
58  *      Data
59 */
60
61 struct tagTapiObj {
62         HLINEAPP hObj;
63         HWND hWnd;
64         DWORD num_devs;
65 } TapiObj = { NULL, NULL, 0 };
66
67 struct tagTapiDev {
68         BOOL valid;
69         DWORD id;
70         DWORD apiver;
71         DWORD type;
72         HLINE hLine;
73         HCALL hCall;
74         DWORD call_state;
75         BOOL connected;
76         BOOL lineReplyReceived;
77         BOOL callStateReceived;
78         DWORD asyncRequestedID;
79         LONG lineReplyResult;
80
81 } TapiDev = { FALSE, 0, 0, 0, NULL, NULL, 0, FALSE, FALSE, FALSE, 0, 0 };       
82
83
84
85 /* 
86  * Local function prototypes 
87 */
88
89 void CALLBACK                   tapi_callback(DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInst, 
90                                                                                                 DWORD dw1, 
91                                                                                                 DWORD dw2, 
92                                                                                         DWORD dw3);
93
94 LINEDEVCAPS*                    tapi_getdevcaps(uint dev);
95 LINEADDRESSSTATUS*      tapi_line_getaddrstatus(HLINE hLine, DWORD dwID);
96 LINEADDRESSCAPS*                tapi_line_getaddrcaps(uint dev, DWORD addrID);
97 LINECALLSTATUS*         tapi_line_getcallstatus(HCALL hCall);
98 LINECALLINFO*                   tapi_line_getcallinfo(HCALL hCall);
99
100 int                                             xtapi_err(LONG val);
101 LONG                                            xtapi_device_wait_for_reply(LONG reqID); 
102
103
104
105 /*
106  * Functions
107 */
108
109 /*      init
110                 initializes the TAPI interface
111
112         returns:
113                 XTAPI_NODEVICES if no devices found
114                 XTAPI_BUSY if we are calling this function again for some reason
115                                           (non-reentrant)
116                 XTAPI_APPCONFLICT TAPI is being used somewhere else, and we can't gain
117                                                                   control
118                 XTAPI_INCOMPATIBLE_VERSION this interface is old or not supported
119                                                                                    under current Win32.
120 */
121
122 int xtapi_init(char *appname, int *numdevs)
123 {
124         LONG retval;
125         BOOL reinit;
126
127         if (TapiObj.hObj) return XTAPI_SUCCESS;
128         
129         TapiObj.hWnd = GetLibraryWindow();
130
131         reinit = TRUE;                                                          // Allow for reinitialization
132
133         while (1)
134         {
135                 retval = lineInitialize(&TapiObj.hObj, 
136                                                 GetWindowInstance(TapiObj.hWnd),
137                                                 tapi_callback, 
138                                                 appname, 
139                                                 &TapiObj.num_devs);
140                 
141                 if (!retval) {
142                         break;
143                 }
144                 else if (retval == LINEERR_REINIT) {
145                         if (reinit)  {
146                                 timer_delay(0x50000);           // delay 5 seconds 
147                                 reinit = FALSE;
148                         }
149                         else {
150                                 return XTAPI_APPCONFLICT;       // another app is using tapi resources we need
151                         }
152                 }
153                 else if (retval == LINEERR_NODEVICE) {
154                         return XTAPI_NODEVICES;                 // should tell user to install a modem
155                 }
156                 else return XTAPI_GENERAL_ERR;
157         }
158
159         *numdevs = (int)TapiObj.num_devs;
160         
161         TapiDev.valid = FALSE;
162
163         return XTAPI_SUCCESS;
164 }
165
166
167 /* shutdown
168                 performs a shutdown of the TAPI system
169 */
170
171 int xtapi_shutdown()
172 {
173         LONG retval;
174         
175         if (!TapiObj.hObj) return 0;
176
177 //      Close any device that may be open at this point!
178
179 //      shutdown TAPI
180         retval = lineShutdown(TapiObj.hObj);
181         if (retval!=0)  return xtapi_err(retval);
182
183         TapiObj.hObj = NULL;
184         TapiObj.num_devs = 0;
185         TapiObj.hWnd = NULL;    
186
187         return 0;
188 }
189
190
191 /* enumdevices
192                 enumerates the devices usable by TAPI.  good for
193                 letting the user select the device and TAPI to take over
194 */
195
196 int xtapi_enumdevices(TapiDevice *dev, int num)
197 {
198         LONG retval;
199         int i;
200
201         Assert(TapiObj.hObj != NULL);
202         Assert(num <= TapiObj.num_devs);
203                                          
204         for (i = 0; i < num; i++) 
205         {
206                 LINEEXTENSIONID extid;
207                 LINEDEVCAPS *caps;
208         
209                 caps = tapi_getdevcaps(i);
210                 if (caps == NULL) return XTAPI_OUT_OF_MEMORY;
211
212                 if ((caps->dwBearerModes & LINEBEARERMODE_VOICE) &&
213                          (caps->dwMediaModes & LINEMEDIAMODE_DATAMODEM)) {
214                         dev[i].type = XTAPI_MODEM_DEVICE;
215                 }
216                 else dev[i].type = 0;
217
218                 retval = lineNegotiateAPIVersion(TapiObj.hObj, 
219                                                                 i,      TAPI_VERSION, TAPI_VERSION,
220                                                                 (DWORD *)&dev[i].apiver, &extid);
221
222                 if (retval == LINEERR_INCOMPATIBLEAPIVERSION)
223                         return XTAPI_INCOMPATIBLE_VERSION;
224                 else if (retval != 0)
225                         return xtapi_err(retval);
226
227                 dev[i].id = i;
228                 dev[i].min_baud = (uint)caps->dwMaxRate;
229                 dev[i].max_baud = (uint)caps->dwMaxRate;
230
231                 free(caps);                     
232         }
233                 
234         return XTAPI_SUCCESS;
235 }
236
237
238 /*      lock
239                 this function sets the specified device as the current device to be
240                 used by the xtapi_device system
241 */
242
243 int xtapi_lock(TapiDevice *dev)
244 {
245         if (TapiDev.valid) return XTAPI_BUSY;
246         
247         TapiDev.id = (DWORD)dev->id;
248         TapiDev.apiver = (DWORD)dev->apiver;
249         TapiDev.type = (DWORD)dev->type;
250         TapiDev.hLine = NULL;
251         TapiDev.hCall = NULL;
252         TapiDev.connected = FALSE;
253         TapiDev.call_state = 0;
254         TapiDev.lineReplyReceived = FALSE;
255         TapiDev.callStateReceived = FALSE;
256         TapiDev.lineReplyResult = 0;
257         TapiDev.valid = TRUE;   
258         
259         return XTAPI_SUCCESS;
260 }
261
262
263 /*      unlock
264                 this functions just releases the device.  device functions won't work
265                 anymore until another lock
266 */
267
268 int xtapi_unlock(TapiDevice *dev)
269 {
270         if (!TapiDev.valid) return XTAPI_NOTLOCKED;
271
272         if (TapiDev.hCall || TapiDev.hLine) 
273                 xtapi_device_hangup();
274         
275         TapiDev.id = 0;
276         TapiDev.apiver = 0;
277         TapiDev.hLine = NULL;
278         TapiDev.hCall = NULL;
279         TapiDev.call_state = 0;
280         TapiDev.valid = FALSE;
281
282         return XTAPI_SUCCESS;
283 }
284
285
286 /* device_dial 
287                 this function will dialout a given number through the current
288                 device.
289 */
290
291 int xtapi_device_dialout(char *phonenum)
292 {
293         LINEDEVCAPS *ldevcaps=NULL;
294         LINECALLPARAMS *lcallparams=NULL;
295         LONG retval;
296         int xtapi_ret = XTAPI_SUCCESS;
297
298         if (TapiDev.hLine) return XTAPI_BUSY;
299
300 //      check if we can dial out
301         if (TapiDev.type != XTAPI_MODEM_DEVICE) {
302                 xtapi_ret = XTAPI_NOT_SUPPORTED;
303                 goto dialout_exit;
304         }
305         ldevcaps = tapi_getdevcaps(TapiDev.id);
306         if (!ldevcaps) return XTAPI_OUT_OF_MEMORY;
307         if (!(ldevcaps->dwLineFeatures & LINEFEATURE_MAKECALL)) {
308                 xtapi_ret = XTAPI_NOT_SUPPORTED;
309                 goto dialout_exit;
310         }
311
312 //      open the line!
313         retval = lineOpen(TapiObj.hObj, TapiDev.id, 
314                                                                 &TapiDev.hLine,
315                                                                 TapiDev.apiver, 0, 0, 
316                                                                 LINECALLPRIVILEGE_NONE,
317                                                                 LINEMEDIAMODE_DATAMODEM,
318                                                                 0);
319         if (retval != 0) {
320                 xtapi_ret = xtapi_err(retval);
321                 goto dialout_exit;
322         }
323
324         retval = lineSetStatusMessages(TapiDev.hLine, 
325                                                 LINEDEVSTATE_OTHER |
326                                                 LINEDEVSTATE_RINGING | LINEDEVSTATE_CONNECTED | 
327                                                 LINEDEVSTATE_DISCONNECTED | LINEDEVSTATE_OUTOFSERVICE |
328                                                 LINEDEVSTATE_MAINTENANCE |      LINEDEVSTATE_REINIT,
329                                                 LINEADDRESSSTATE_INUSEZERO |
330                                                 LINEADDRESSSTATE_INUSEONE |
331                                                 LINEADDRESSSTATE_INUSEMANY);
332
333         if (retval != 0) {
334                 xtapi_ret = xtapi_err(retval);
335                 goto dialout_exit;
336         }
337
338 //      Setup calling parameters
339         lcallparams = (LINECALLPARAMS *)malloc(sizeof(LINECALLPARAMS)+strlen(phonenum)+1);
340         if (!lcallparams) {
341                 xtapi_ret = XTAPI_OUT_OF_MEMORY;
342                 goto dialout_exit;
343         }
344         memset(lcallparams, 0, sizeof(LINECALLPARAMS)+strlen(phonenum)+1);                                                      
345         lcallparams->dwTotalSize = sizeof(LINECALLPARAMS)+strlen(phonenum)+1;
346         lcallparams->dwBearerMode = LINEBEARERMODE_VOICE;
347         lcallparams->dwMediaMode = LINEMEDIAMODE_DATAMODEM;
348         lcallparams->dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
349         lcallparams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;
350         lcallparams->dwAddressID = 0;
351         lcallparams->dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
352         lcallparams->dwDisplayableAddressSize = strlen(phonenum)+1;
353         strcpy((LPSTR)lcallparams + sizeof(LINECALLPARAMS), phonenum);
354         
355 //      Dial it!
356         mprintf((0, "XTAPI: dialing %s.\n", phonenum));
357         retval = xtapi_device_wait_for_reply(
358                 lineMakeCall(TapiDev.hLine, &TapiDev.hCall, NULL, 0, lcallparams)
359         );
360         if (retval < 0) {
361                 xtapi_ret = xtapi_err(retval);
362                 goto dialout_exit;
363         }
364
365         retval = xtapi_device_wait_for_reply(
366                 lineDial(TapiDev.hCall, phonenum, 0)
367         );
368         if (retval < 0) {
369                 xtapi_ret = xtapi_err(retval);
370                 goto dialout_exit;
371         }
372         
373 dialout_exit:
374         if (lcallparams) free(lcallparams);
375         if (ldevcaps) free(ldevcaps);
376         
377         return xtapi_ret;
378 }
379
380
381 /* device_dialin
382                 sets up modem to wait for a call.  All this function does
383                 is initialize the line.
384 */
385
386 int xtapi_device_dialin()
387 {
388         LONG retval;
389         int xtapi_ret = XTAPI_SUCCESS;
390
391         if (TapiDev.hLine) return XTAPI_BUSY;
392
393 //      check if we can dial out
394         if (TapiDev.type != XTAPI_MODEM_DEVICE) {
395                 xtapi_ret = XTAPI_NOT_SUPPORTED;
396                 goto dialin_exit;
397         }
398
399 //      open the line!
400         retval = lineOpen(TapiObj.hObj, TapiDev.id, 
401                                                                 &TapiDev.hLine,
402                                                                 TapiDev.apiver, 0, 0, 
403                                                                 LINECALLPRIVILEGE_OWNER,
404                                                                 LINEMEDIAMODE_DATAMODEM,
405                                                                 0);
406         if (retval != 0) {
407                 xtapi_ret = xtapi_err(retval);
408                 goto dialin_exit;
409         }
410
411 dialin_exit:
412         return xtapi_ret;
413 }
414
415
416 /* device_answer
417                 the line should be open, and all this function does is grab the call
418 */
419                 
420 int xtapi_device_answer()
421 {
422         LONG retval;
423         int xtapi_ret = XTAPI_SUCCESS;
424
425         if (!TapiDev.hCall) return XTAPI_NOTOPEN;
426
427         retval = xtapi_device_wait_for_reply(
428                         lineAnswer(TapiDev.hCall, NULL, 0)
429         );
430         
431         if (retval < 0) {
432                 xtapi_ret = xtapi_err(retval);
433         }
434
435         return xtapi_ret;
436 }
437
438
439 /* device_poll_callstate
440                 we can be informed of the current state of a call made after the
441                 reply from lineMakeCall
442 */
443
444 int xtapi_device_poll_callstate(uint *state)
445 {
446 //       perform translation from TAPI to XTAPI!
447
448         if (TapiDev.callStateReceived) {
449                 switch (TapiDev.call_state) 
450                 {
451                         case LINECALLSTATE_IDLE: *state = XTAPI_LINE_IDLE; break;
452                         case LINECALLSTATE_DIALTONE: *state = XTAPI_LINE_DIALTONE; break;
453                         case LINECALLSTATE_DIALING: *state = XTAPI_LINE_DIALING; break;
454                         case LINECALLSTATE_RINGBACK: *state = XTAPI_LINE_RINGBACK; break;
455                         case LINECALLSTATE_BUSY: *state = XTAPI_LINE_BUSY; break;
456                         case LINECALLSTATE_SPECIALINFO: *state = XTAPI_LINE_FEEDBACK; break;
457                         case LINECALLSTATE_CONNECTED: *state = XTAPI_LINE_CONNECTED; break;
458                         case LINECALLSTATE_DISCONNECTED: *state = XTAPI_LINE_DISCONNECTED; break;
459                         case LINECALLSTATE_PROCEEDING: *state = XTAPI_LINE_PROCEEDING; break;                   
460                         case LINECALLSTATE_OFFERING: *state = XTAPI_LINE_RINGING; break;
461                         default:
462                                 mprintf((0, "call_state: %x\n", TapiDev.call_state));
463                                 *state = XTAPI_LINE_UNDEFINED;
464                 }
465                 TapiDev.callStateReceived = FALSE;
466                 TapiDev.call_state = 0;
467         }
468         else *state = 0;
469
470         return XTAPI_SUCCESS;
471 }
472
473
474 /* device_create_comm_object
475                 once we are connected, we can create a COMM_OBJ to actually send
476                 and receive data through the modem.
477 */
478
479 #define ASCII_XON       0x11
480 #define ASCII_XOFF      0x13
481
482
483 int xtapi_device_create_comm_object(COMM_OBJ *commobj)
484 {
485         VARSTRING *varstr = NULL;
486         DWORD varstrsize;
487         HANDLE hCommHandle=NULL;
488         LINECALLINFO *lcinfo = NULL;
489         int retval;
490         int errval = XTAPI_SUCCESS;
491
492         Assert(TapiDev.connected);
493
494         varstrsize = sizeof(VARSTRING) + 1024;
495         
496         while (1)
497         {
498                 varstr = (VARSTRING *)realloc(varstr, varstrsize);
499                 if (!varstr) {
500                         errval = XTAPI_OUT_OF_MEMORY;
501                         goto device_create_comm_exit;
502                 }
503
504                 varstr->dwTotalSize = varstrsize;
505
506                 retval = lineGetID(0,0,TapiDev.hCall, LINECALLSELECT_CALL, varstr,
507                                                 "comm/datamodem");
508                 errval = xtapi_err(retval);
509                 
510                 if (varstr->dwNeededSize > varstr->dwTotalSize) {
511                         varstrsize = varstr->dwNeededSize;
512                 }
513                 else break;
514         }
515
516         if (errval != XTAPI_SUCCESS) return errval;
517
518         hCommHandle = *((LPHANDLE)((LPBYTE)varstr+varstr->dwStringOffset));
519
520         lcinfo = tapi_line_getcallinfo(TapiDev.hCall);
521         if (!lcinfo) {
522                 errval = XTAPI_OUT_OF_MEMORY;
523                 goto device_create_comm_exit;
524         }
525
526
527 //      Create the COMM compatible COMM_OBJ
528 //      Most COMM settings will be set by TAPI, so this is less intensive than the 
529 //      COMM open connection
530         {
531                 COMMTIMEOUTS ctimeouts;
532
533                 memset(commobj, 0, sizeof(COMM_OBJ));
534         
535                 if (GetFileType(hCommHandle) != FILE_TYPE_CHAR) {
536                         errval = XTAPI_GENERAL_ERR;
537                         goto device_create_comm_exit;
538                 }
539         
540                 GetCommState(hCommHandle, &commobj->dcb);
541                 GetCommTimeouts(hCommHandle, &ctimeouts);
542
543                 commobj->handle = hCommHandle;
544                 commobj->baud = lcinfo->dwRate;
545
546
547 //              commobj->dcb.BaudRate           = commobj->baud;
548 //              commobj->dcb.fBinary                    = 1;
549 //              commobj->dcb.fNull                      = 0;
550 //              commobj->dcb.ByteSize           = 8;
551 //              commobj->dcb.StopBits           = ONESTOPBIT;
552 //              commobj->dcb.fParity                    = FALSE;
553 //              commobj->dcb.Parity                     = NOPARITY;
554 //         commobj->dcb.XonChar                 = ASCII_XON;
555 //         commobj->dcb.XoffChar                = ASCII_XOFF;
556 //         commobj->dcb.XonLim                  = 1024;
557 //      commobj->dcb.XoffLim            = 1024;
558 //         commobj->dcb.EofChar                 = 0;
559 //         commobj->dcb.EvtChar                 = 0;
560 //              commobj->dcb.fOutxDsrFlow       = FALSE;
561 //              commobj->dcb.fOutxCtsFlow       = FALSE;                                        // rts/cts off
562
563 //              commobj->dcb.fDtrControl        = DTR_CONTROL_ENABLE;// dtr=on
564 //              commobj->dcb.fRtsControl        = RTS_CONTROL_ENABLE;
565
566       ctimeouts.ReadIntervalTimeout         = 250;
567       ctimeouts.ReadTotalTimeoutMultiplier  = 0;
568       ctimeouts.ReadTotalTimeoutConstant    = 0;
569       ctimeouts.WriteTotalTimeoutMultiplier = 0;
570       ctimeouts.WriteTotalTimeoutConstant   = 0;
571
572       commobj->dcb.fAbortOnError = FALSE;
573
574       SetCommTimeouts(hCommHandle, &ctimeouts);
575       SetCommState(hCommHandle, &commobj->dcb);
576         }
577
578         memset(&commobj->rov, 0, sizeof(OVERLAPPED));
579         memset(&commobj->wov, 0, sizeof(OVERLAPPED));
580         commobj->rov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
581         if (commobj->rov.hEvent == NULL) {
582                 errval = XTAPI_GENERAL_ERR;
583                 goto device_create_comm_exit;
584         }
585         commobj->wov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
586         if (commobj->wov.hEvent == NULL) {
587                 CloseHandle(commobj->rov.hEvent);
588                 return 0;
589         }
590
591         commobj->hThread = NULL;
592         commobj->threadID = (DWORD)(-1);
593         commobj->connect = TRUE;
594
595 device_create_comm_exit:
596         if (varstr) free(varstr);
597         if (lcinfo) free(lcinfo);
598
599         return errval;
600 }
601
602
603 /* device_hangup
604                 frees the call and line 
605
606         damn well better assume that the COMM_OBJ allocated (if at all) is 
607         closed via the comm_library.
608 */
609
610 int xtapi_device_hangup()
611 {
612         LONG retval;
613
614         Assert(TapiDev.valid);                                  // Device should be locked!
615
616 //      if      (!TapiDev.hCall) return XTAPI_SUCCESS;
617 //      if (!TapiDev.hLine) return XTAPI_SUCCESS;
618
619 //      drop any call in progress
620         if (TapiDev.hCall) {
621                 LINECALLSTATUS *lcallstat = NULL;
622                 MSG msg;
623                 DWORD call_state;               
624
625                 lcallstat = tapi_line_getcallstatus(TapiDev.hCall);
626                 if (!lcallstat) {
627                         return XTAPI_OUT_OF_MEMORY;
628                 }
629
630                 mprintf((0, "XTAPI: Got linestatus.\n"));
631         
632                 if (!(lcallstat->dwCallState & LINECALLSTATE_IDLE))  {
633                 // line not IDLE so drop it!
634                         retval = xtapi_device_wait_for_reply(
635                                 lineDrop(TapiDev.hCall, NULL, 0)
636                         );
637
638                         if (retval != XTAPI_SUCCESS) {
639                                 mprintf((1, "XTAPI: error when lineDrop.\n"));
640                         }
641
642                         mprintf((0, "XTAPI: dropped line.\n"));
643                                                                         
644                 // wait for IDLE
645                         mprintf((0, "XTAPI: Waiting for idle.\n"));
646                         while (1)
647                         {
648                                 xtapi_device_poll_callstate((uint*)&call_state);
649                                 if (call_state == XTAPI_LINE_IDLE) break;
650                                 if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
651                 TranslateMessage(&msg);
652                 DispatchMessage(&msg);
653                         }
654                         }
655                 }
656                 retval = lineDeallocateCall(TapiDev.hCall);
657
658                 mprintf((0, "XTAPI: deallocated call.\n"));
659
660                 if (retval != 0) {
661                         free(lcallstat);
662                         return XTAPI_GENERAL_ERR;
663                 }
664                 TapiDev.hCall = NULL;           
665                 
666                 if (lcallstat) free(lcallstat);
667         }
668
669 //      Free up line.
670         if (TapiDev.hLine) {
671                 retval = lineClose(TapiDev.hLine);
672                 if (retval != 0) {
673                         return XTAPI_GENERAL_ERR;
674                 }
675                 TapiDev.hLine = NULL;           
676         }
677
678         mprintf((0, "XTAPI: Closed line.\n"));
679
680         TapiDev.connected = FALSE;
681         
682         return XTAPI_SUCCESS;
683 }
684
685                 
686 /*      device_wait_for_reply
687                 once a function is called, we wait until we have been given a reply.
688 */
689 LONG xtapi_device_wait_for_reply(LONG reqID)
690 {
691         if (reqID > 0) {                                                // A valid ID.  so we shall wait and see
692                 MSG msg;
693                 
694                 TapiDev.lineReplyReceived = FALSE;
695                 TapiDev.asyncRequestedID = (DWORD)reqID;
696                 TapiDev.lineReplyResult = LINEERR_OPERATIONFAILED;
697
698                 while (!TapiDev.lineReplyReceived) 
699                 {
700                         if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
701                 TranslateMessage(&msg);
702                 DispatchMessage(&msg);
703                 }
704                         
705                 // Insert code to take care of shutting down while wait.
706                 }
707
708                 return (LONG)TapiDev.lineReplyResult;
709         }
710
711         return reqID;
712 }               
713
714
715 /*      err
716                 translate tapi to xtapi errors
717 */
718
719 int xtapi_err(LONG err)
720 {
721         mprintf((1, "TAPI err: %x\n", (DWORD)err));
722         switch (err)
723         {
724                 case 0:
725                         return XTAPI_SUCCESS;
726
727                 case LINEERR_ALLOCATED:
728                         return XTAPI_APPCONFLICT;
729         
730                 case LINEERR_NOMEM:
731                         return XTAPI_OUT_OF_MEMORY;
732                 
733                 case LINEERR_INCOMPATIBLEAPIVERSION:
734                         return XTAPI_INCOMPATIBLE_VERSION;
735                 
736                 case LINEERR_NODEVICE:
737                         return XTAPI_NODEVICES;
738
739                 case LINEERR_OPERATIONFAILED:
740                         return XTAPI_FAIL;
741         
742                 case LINEERR_OPERATIONUNAVAIL:
743                         return XTAPI_NOT_SUPPORTED;
744
745                 case LINEERR_RESOURCEUNAVAIL:
746                         return XTAPI_OUT_OF_RESOURCES;
747
748                 case LINEERR_REINIT:
749                         return XTAPI_BUSY;
750         }
751         return XTAPI_GENERAL_ERR;
752 }
753
754
755 /* callback
756                 this function is used by TAPI to inform us of when asynchronous
757                 requests are valid and of any changes to the current call_state
758 */
759
760 void CALLBACK tapi_callback(DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInst, 
761                                                                                                 DWORD dw1, 
762                                                                                                 DWORD dw2, 
763                                                                                         DWORD dw3)
764 {
765         switch(dwMsg)
766         {
767                 case LINE_REPLY:
768                         if (dw2 != 0) 
769                                 mprintf((1, "XTAPI: LINE_REPLY err: %x.\n", dw2));
770                         else 
771                                 mprintf((1, "XTAPI: LINE_REPLY received.\n"));
772                         if (TapiDev.asyncRequestedID == dw1) {
773                                 TapiDev.lineReplyReceived = TRUE;
774                                 TapiDev.lineReplyResult = (LONG)dw2;
775                         }
776                         break;
777                 
778                 case LINE_CALLSTATE:
779                         TapiDev.callStateReceived = TRUE;
780                         TapiDev.call_state = dw1;
781
782                         mprintf((0, "Call_State  = %x\n", dw1));                        
783                         switch(TapiDev.call_state)
784                         {
785                                 case LINECALLSTATE_CONNECTED:
786                                         if (TapiDev.connected) break;
787                                         TapiDev.connected = TRUE;
788                                         break;
789         
790                                 case LINECALLSTATE_OFFERING:
791                                         if (TapiDev.connected) {
792                                                 mprintf((1, "TAPI: offering after connected!\n"));
793                                                 break;
794                                         }
795                                         if (dw3 != LINECALLPRIVILEGE_OWNER) {
796                                                 mprintf((1, "TAPI: need owner privilege to accept call!.\n"));
797                                                 break;
798                                         }
799                                         TapiDev.hCall = (HCALL)hDevice;
800                                         break;
801                         }
802                         break;
803         }
804 }
805                                 
806
807
808 /* 
809  * TAPI functions
810 */
811
812 /* tapi_getdevcaps
813                 gets device caps for specified device and returns a valid structure
814 */
815
816 LINEDEVCAPS *tapi_getdevcaps(uint dev)
817 {
818         LINEDEVCAPS *pcaps=NULL;
819         LONG retval, size;
820         DWORD apiver;
821
822         size = sizeof(LINEDEVCAPS) + 256;
823         
824         while (1)
825         {
826                 LINEEXTENSIONID extid;
827
828                 retval = lineNegotiateAPIVersion(TapiObj.hObj, 
829                                                                 dev,    TAPI_VERSION, TAPI_VERSION,
830                                                                 &apiver, &extid);
831
832                 if (retval != 0)
833                         return NULL;
834
835                 pcaps = (LINEDEVCAPS *)realloc(pcaps, size);
836                 if (!pcaps) return NULL;
837                  
838                 memset(pcaps, 0, size);
839                 pcaps->dwTotalSize = size;
840
841                 retval = lineGetDevCaps(TapiObj.hObj, dev,
842                                                 apiver, 0, 
843                                                 pcaps);
844
845                 if (retval!=0) {
846                         free(pcaps);
847                         return NULL;
848                 }
849                 
850                 if (pcaps->dwNeededSize > pcaps->dwTotalSize) {
851                         size = pcaps->dwNeededSize;
852                         continue;
853                 }
854                 else break;
855         }
856
857         return pcaps;
858 }
859
860 /*      tapi_line_getaddrstatus
861                 retrieves the current status of a given address on the line.
862
863         returns:
864                 NULL if fail.   
865 */
866
867 LINEADDRESSSTATUS *tapi_line_getaddrstatus(HLINE hLine, DWORD dwID)
868 {
869         LINEADDRESSSTATUS *ad=NULL;
870         LONG retval;
871         int size;
872
873         size = sizeof(LINEADDRESSSTATUS) + 256;
874
875         while (1)
876         {
877                 ad = (LINEADDRESSSTATUS *)realloc(ad, size);
878                 if (!ad) return NULL;
879                  
880                 memset(ad, 0, size);
881                 ad->dwTotalSize = size;
882
883                 retval = lineGetAddressStatus(hLine, dwID, ad);
884
885                 if (retval!=0) {
886                         free(ad);
887                         return NULL;
888                 }
889                 
890                 if (ad->dwNeededSize > ad->dwTotalSize) {
891                         size = ad->dwNeededSize;
892                         continue;
893                 }
894                 else break;
895         }
896
897         return ad;
898 }
899         
900                 
901 /*      tapi_line_getaddrcaps
902                 retrieves the current caps of a given address on the line.
903
904         returns:
905                 NULL if fail.   
906 */
907
908 LINEADDRESSCAPS *tapi_line_getaddrcaps(uint dev, DWORD addrID)
909 {
910         LINEADDRESSCAPS *ad=NULL;
911         LONG retval;
912         int size;
913
914         size = sizeof(LINEADDRESSCAPS) + 256;
915
916         while (1)
917         {
918                 DWORD apiver;
919                 LINEEXTENSIONID extid;
920
921                 retval = lineNegotiateAPIVersion(TapiObj.hObj, 
922                                                                 dev,    TAPI_VERSION, TAPI_VERSION,
923                                                                 &apiver, &extid);
924
925                 if (retval != 0)
926                         return NULL;
927
928                 ad = (LINEADDRESSCAPS *)realloc(ad, size);
929                 if (!ad) return NULL;
930                  
931                 memset(ad, 0, size);
932                 ad->dwTotalSize = size;
933
934                 retval = lineGetAddressCaps(TapiObj.hObj, dev, 
935                                                                         addrID, apiver, 0,
936                                                                         ad);
937
938                 if (retval!=0) {
939                         free(ad);
940                         return NULL;
941                 }
942                 
943                 if (ad->dwNeededSize > ad->dwTotalSize) {
944                         size = ad->dwNeededSize;
945                         continue;
946                 }
947                 else break;
948         }
949
950         return ad;
951 }
952
953
954 /*      tapi_line_getcallstatus
955                 retrieves the current status of a given call on the line.
956
957         returns:
958                 NULL if fail.   
959 */
960
961 LINECALLSTATUS *tapi_line_getcallstatus(HCALL hCall)
962 {
963         LINECALLSTATUS *ad=NULL;
964         LONG retval;
965         int size;
966
967         size = sizeof(LINECALLSTATUS) + 256;
968
969         while (1)
970         {
971                 ad = (LINECALLSTATUS *)realloc(ad, size);
972                 if (!ad) return NULL;
973                  
974                 memset(ad, 0, size);
975                 ad->dwTotalSize = size;
976
977                 retval = lineGetCallStatus(hCall,ad); 
978
979                 if (retval!=0) {
980                         free(ad);
981                         return NULL;
982                 }
983                 
984                 if (ad->dwNeededSize > ad->dwTotalSize) {
985                         size = ad->dwNeededSize;
986                         continue;
987                 }
988                 else break;
989         }
990
991         return ad;
992 }
993
994
995 /*      tapi_line_getcallinfo
996                 retrieves the current status of a given call on the line.
997
998         returns:
999                 NULL if fail.   
1000 */
1001
1002 LINECALLINFO *tapi_line_getcallinfo(HCALL hCall)
1003 {
1004         LINECALLINFO *ad=NULL;
1005         LONG retval;
1006         int size;
1007
1008         size = sizeof(LINECALLINFO) + 256;
1009
1010         while (1)
1011         {
1012                 ad = (LINECALLINFO *)realloc(ad, size);
1013                 if (!ad) return NULL;
1014                  
1015                 memset(ad, 0, size);
1016                 ad->dwTotalSize = size;
1017
1018                 retval = lineGetCallInfo(hCall,ad); 
1019
1020                 if (retval!=0) {
1021                         free(ad);
1022                         return NULL;
1023                 }
1024                 
1025                 if (ad->dwNeededSize > ad->dwTotalSize) {
1026                         size = ad->dwNeededSize;
1027                         continue;
1028                 }
1029                 else break;
1030         }
1031
1032         return ad;
1033 }