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 
xtapi_init(char * appname,int * numdevs)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 
xtapi_shutdown()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 
xtapi_enumdevices(TapiDevice * dev,int num)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 
xtapi_lock(TapiDevice * dev)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 
xtapi_unlock(TapiDevice * dev)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 
xtapi_device_dialout(char * phonenum)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 
xtapi_device_dialin()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 
xtapi_device_answer()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 
xtapi_device_poll_callstate(uint * state)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 
xtapi_device_create_comm_object(COMM_OBJ * commobj)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 
xtapi_device_hangup()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 */
xtapi_device_wait_for_reply(LONG reqID)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 
xtapi_err(LONG err)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 
tapi_callback(DWORD hDevice,DWORD dwMsg,DWORD dwCallbackInst,DWORD dw1,DWORD dw2,DWORD dw3)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 
tapi_getdevcaps(uint dev)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 
tapi_line_getaddrstatus(HLINE hLine,DWORD dwID)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 
tapi_line_getaddrcaps(uint dev,DWORD addrID)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 
tapi_line_getcallstatus(HCALL hCall)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 
tapi_line_getcallinfo(HCALL hCall)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 }
1034