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