/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ #pragma off (unreferenced) static char rcsid[] = "$Id: xtapi.c 1.5 1996/10/04 17:08:45 samir Exp $"; #pragma on (unreferenced) #define _WIN32 #define WIN95 #define WIN32_LEAN_AND_MEAN #include #include #include #include "pstypes.h" #include "mono.h" #include "error.h" #include "winapp.h" #include "xtapi.h" /* The TAPI layer will interface between Descent 2 and Win32 TAPI. The application will go about using XTAPI when wanting to dial out to another player (modem) or when listening for a ring in. First an application will initialize the library. Then one needs to find the required device id by enumerating the devices and then selecting a device id. Then we open this device by calling xtapi_lock. The application can only use this device between lock and unlock. Also all functions with the device extension will act upon this locked device. When done with a device call xtapi_unlock. To dial-out. Call xtapi_device_dialout. */ #define TAPI_VERSION 0x00010004 /* * Data */ struct tagTapiObj { HLINEAPP hObj; HWND hWnd; DWORD num_devs; } TapiObj = { NULL, NULL, 0 }; struct tagTapiDev { BOOL valid; DWORD id; DWORD apiver; DWORD type; HLINE hLine; HCALL hCall; DWORD call_state; BOOL connected; BOOL lineReplyReceived; BOOL callStateReceived; DWORD asyncRequestedID; LONG lineReplyResult; } TapiDev = { FALSE, 0, 0, 0, NULL, NULL, 0, FALSE, FALSE, FALSE, 0, 0 }; /* * Local function prototypes */ void CALLBACK tapi_callback(DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInst, DWORD dw1, DWORD dw2, DWORD dw3); LINEDEVCAPS* tapi_getdevcaps(uint dev); LINEADDRESSSTATUS* tapi_line_getaddrstatus(HLINE hLine, DWORD dwID); LINEADDRESSCAPS* tapi_line_getaddrcaps(uint dev, DWORD addrID); LINECALLSTATUS* tapi_line_getcallstatus(HCALL hCall); LINECALLINFO* tapi_line_getcallinfo(HCALL hCall); int xtapi_err(LONG val); LONG xtapi_device_wait_for_reply(LONG reqID); /* * Functions */ /* init initializes the TAPI interface returns: XTAPI_NODEVICES if no devices found XTAPI_BUSY if we are calling this function again for some reason (non-reentrant) XTAPI_APPCONFLICT TAPI is being used somewhere else, and we can't gain control XTAPI_INCOMPATIBLE_VERSION this interface is old or not supported under current Win32. */ int xtapi_init(char *appname, int *numdevs) { LONG retval; BOOL reinit; if (TapiObj.hObj) return XTAPI_SUCCESS; TapiObj.hWnd = GetLibraryWindow(); reinit = TRUE; // Allow for reinitialization while (1) { retval = lineInitialize(&TapiObj.hObj, GetWindowInstance(TapiObj.hWnd), tapi_callback, appname, &TapiObj.num_devs); if (!retval) { break; } else if (retval == LINEERR_REINIT) { if (reinit) { timer_delay(0x50000); // delay 5 seconds reinit = FALSE; } else { return XTAPI_APPCONFLICT; // another app is using tapi resources we need } } else if (retval == LINEERR_NODEVICE) { return XTAPI_NODEVICES; // should tell user to install a modem } else return XTAPI_GENERAL_ERR; } *numdevs = (int)TapiObj.num_devs; TapiDev.valid = FALSE; return XTAPI_SUCCESS; } /* shutdown performs a shutdown of the TAPI system */ int xtapi_shutdown() { LONG retval; if (!TapiObj.hObj) return 0; // Close any device that may be open at this point! // shutdown TAPI retval = lineShutdown(TapiObj.hObj); if (retval!=0) return xtapi_err(retval); TapiObj.hObj = NULL; TapiObj.num_devs = 0; TapiObj.hWnd = NULL; return 0; } /* enumdevices enumerates the devices usable by TAPI. good for letting the user select the device and TAPI to take over */ int xtapi_enumdevices(TapiDevice *dev, int num) { LONG retval; int i; Assert(TapiObj.hObj != NULL); Assert(num <= TapiObj.num_devs); for (i = 0; i < num; i++) { LINEEXTENSIONID extid; LINEDEVCAPS *caps; caps = tapi_getdevcaps(i); if (caps == NULL) return XTAPI_OUT_OF_MEMORY; if ((caps->dwBearerModes & LINEBEARERMODE_VOICE) && (caps->dwMediaModes & LINEMEDIAMODE_DATAMODEM)) { dev[i].type = XTAPI_MODEM_DEVICE; } else dev[i].type = 0; retval = lineNegotiateAPIVersion(TapiObj.hObj, i, TAPI_VERSION, TAPI_VERSION, (DWORD *)&dev[i].apiver, &extid); if (retval == LINEERR_INCOMPATIBLEAPIVERSION) return XTAPI_INCOMPATIBLE_VERSION; else if (retval != 0) return xtapi_err(retval); dev[i].id = i; dev[i].min_baud = (uint)caps->dwMaxRate; dev[i].max_baud = (uint)caps->dwMaxRate; free(caps); } return XTAPI_SUCCESS; } /* lock this function sets the specified device as the current device to be used by the xtapi_device system */ int xtapi_lock(TapiDevice *dev) { if (TapiDev.valid) return XTAPI_BUSY; TapiDev.id = (DWORD)dev->id; TapiDev.apiver = (DWORD)dev->apiver; TapiDev.type = (DWORD)dev->type; TapiDev.hLine = NULL; TapiDev.hCall = NULL; TapiDev.connected = FALSE; TapiDev.call_state = 0; TapiDev.lineReplyReceived = FALSE; TapiDev.callStateReceived = FALSE; TapiDev.lineReplyResult = 0; TapiDev.valid = TRUE; return XTAPI_SUCCESS; } /* unlock this functions just releases the device. device functions won't work anymore until another lock */ int xtapi_unlock(TapiDevice *dev) { if (!TapiDev.valid) return XTAPI_NOTLOCKED; if (TapiDev.hCall || TapiDev.hLine) xtapi_device_hangup(); TapiDev.id = 0; TapiDev.apiver = 0; TapiDev.hLine = NULL; TapiDev.hCall = NULL; TapiDev.call_state = 0; TapiDev.valid = FALSE; return XTAPI_SUCCESS; } /* device_dial this function will dialout a given number through the current device. */ int xtapi_device_dialout(char *phonenum) { LINEDEVCAPS *ldevcaps=NULL; LINECALLPARAMS *lcallparams=NULL; LONG retval; int xtapi_ret = XTAPI_SUCCESS; if (TapiDev.hLine) return XTAPI_BUSY; // check if we can dial out if (TapiDev.type != XTAPI_MODEM_DEVICE) { xtapi_ret = XTAPI_NOT_SUPPORTED; goto dialout_exit; } ldevcaps = tapi_getdevcaps(TapiDev.id); if (!ldevcaps) return XTAPI_OUT_OF_MEMORY; if (!(ldevcaps->dwLineFeatures & LINEFEATURE_MAKECALL)) { xtapi_ret = XTAPI_NOT_SUPPORTED; goto dialout_exit; } // open the line! retval = lineOpen(TapiObj.hObj, TapiDev.id, &TapiDev.hLine, TapiDev.apiver, 0, 0, LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM, 0); if (retval != 0) { xtapi_ret = xtapi_err(retval); goto dialout_exit; } retval = lineSetStatusMessages(TapiDev.hLine, LINEDEVSTATE_OTHER | LINEDEVSTATE_RINGING | LINEDEVSTATE_CONNECTED | LINEDEVSTATE_DISCONNECTED | LINEDEVSTATE_OUTOFSERVICE | LINEDEVSTATE_MAINTENANCE | LINEDEVSTATE_REINIT, LINEADDRESSSTATE_INUSEZERO | LINEADDRESSSTATE_INUSEONE | LINEADDRESSSTATE_INUSEMANY); if (retval != 0) { xtapi_ret = xtapi_err(retval); goto dialout_exit; } // Setup calling parameters lcallparams = (LINECALLPARAMS *)malloc(sizeof(LINECALLPARAMS)+strlen(phonenum)+1); if (!lcallparams) { xtapi_ret = XTAPI_OUT_OF_MEMORY; goto dialout_exit; } memset(lcallparams, 0, sizeof(LINECALLPARAMS)+strlen(phonenum)+1); lcallparams->dwTotalSize = sizeof(LINECALLPARAMS)+strlen(phonenum)+1; lcallparams->dwBearerMode = LINEBEARERMODE_VOICE; lcallparams->dwMediaMode = LINEMEDIAMODE_DATAMODEM; lcallparams->dwCallParamFlags = LINECALLPARAMFLAGS_IDLE; lcallparams->dwAddressMode = LINEADDRESSMODE_ADDRESSID; lcallparams->dwAddressID = 0; lcallparams->dwDisplayableAddressOffset = sizeof(LINECALLPARAMS); lcallparams->dwDisplayableAddressSize = strlen(phonenum)+1; strcpy((LPSTR)lcallparams + sizeof(LINECALLPARAMS), phonenum); // Dial it! mprintf((0, "XTAPI: dialing %s.\n", phonenum)); retval = xtapi_device_wait_for_reply( lineMakeCall(TapiDev.hLine, &TapiDev.hCall, NULL, 0, lcallparams) ); if (retval < 0) { xtapi_ret = xtapi_err(retval); goto dialout_exit; } retval = xtapi_device_wait_for_reply( lineDial(TapiDev.hCall, phonenum, 0) ); if (retval < 0) { xtapi_ret = xtapi_err(retval); goto dialout_exit; } dialout_exit: if (lcallparams) free(lcallparams); if (ldevcaps) free(ldevcaps); return xtapi_ret; } /* device_dialin sets up modem to wait for a call. All this function does is initialize the line. */ int xtapi_device_dialin() { LONG retval; int xtapi_ret = XTAPI_SUCCESS; if (TapiDev.hLine) return XTAPI_BUSY; // check if we can dial out if (TapiDev.type != XTAPI_MODEM_DEVICE) { xtapi_ret = XTAPI_NOT_SUPPORTED; goto dialin_exit; } // open the line! retval = lineOpen(TapiObj.hObj, TapiDev.id, &TapiDev.hLine, TapiDev.apiver, 0, 0, LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_DATAMODEM, 0); if (retval != 0) { xtapi_ret = xtapi_err(retval); goto dialin_exit; } dialin_exit: return xtapi_ret; } /* device_answer the line should be open, and all this function does is grab the call */ int xtapi_device_answer() { LONG retval; int xtapi_ret = XTAPI_SUCCESS; if (!TapiDev.hCall) return XTAPI_NOTOPEN; retval = xtapi_device_wait_for_reply( lineAnswer(TapiDev.hCall, NULL, 0) ); if (retval < 0) { xtapi_ret = xtapi_err(retval); } return xtapi_ret; } /* device_poll_callstate we can be informed of the current state of a call made after the reply from lineMakeCall */ int xtapi_device_poll_callstate(uint *state) { // perform translation from TAPI to XTAPI! if (TapiDev.callStateReceived) { switch (TapiDev.call_state) { case LINECALLSTATE_IDLE: *state = XTAPI_LINE_IDLE; break; case LINECALLSTATE_DIALTONE: *state = XTAPI_LINE_DIALTONE; break; case LINECALLSTATE_DIALING: *state = XTAPI_LINE_DIALING; break; case LINECALLSTATE_RINGBACK: *state = XTAPI_LINE_RINGBACK; break; case LINECALLSTATE_BUSY: *state = XTAPI_LINE_BUSY; break; case LINECALLSTATE_SPECIALINFO: *state = XTAPI_LINE_FEEDBACK; break; case LINECALLSTATE_CONNECTED: *state = XTAPI_LINE_CONNECTED; break; case LINECALLSTATE_DISCONNECTED: *state = XTAPI_LINE_DISCONNECTED; break; case LINECALLSTATE_PROCEEDING: *state = XTAPI_LINE_PROCEEDING; break; case LINECALLSTATE_OFFERING: *state = XTAPI_LINE_RINGING; break; default: mprintf((0, "call_state: %x\n", TapiDev.call_state)); *state = XTAPI_LINE_UNDEFINED; } TapiDev.callStateReceived = FALSE; TapiDev.call_state = 0; } else *state = 0; return XTAPI_SUCCESS; } /* device_create_comm_object once we are connected, we can create a COMM_OBJ to actually send and receive data through the modem. */ #define ASCII_XON 0x11 #define ASCII_XOFF 0x13 int xtapi_device_create_comm_object(COMM_OBJ *commobj) { VARSTRING *varstr = NULL; DWORD varstrsize; HANDLE hCommHandle=NULL; LINECALLINFO *lcinfo = NULL; int retval; int errval = XTAPI_SUCCESS; Assert(TapiDev.connected); varstrsize = sizeof(VARSTRING) + 1024; while (1) { varstr = (VARSTRING *)realloc(varstr, varstrsize); if (!varstr) { errval = XTAPI_OUT_OF_MEMORY; goto device_create_comm_exit; } varstr->dwTotalSize = varstrsize; retval = lineGetID(0,0,TapiDev.hCall, LINECALLSELECT_CALL, varstr, "comm/datamodem"); errval = xtapi_err(retval); if (varstr->dwNeededSize > varstr->dwTotalSize) { varstrsize = varstr->dwNeededSize; } else break; } if (errval != XTAPI_SUCCESS) return errval; hCommHandle = *((LPHANDLE)((LPBYTE)varstr+varstr->dwStringOffset)); lcinfo = tapi_line_getcallinfo(TapiDev.hCall); if (!lcinfo) { errval = XTAPI_OUT_OF_MEMORY; goto device_create_comm_exit; } // Create the COMM compatible COMM_OBJ // Most COMM settings will be set by TAPI, so this is less intensive than the // COMM open connection { COMMTIMEOUTS ctimeouts; memset(commobj, 0, sizeof(COMM_OBJ)); if (GetFileType(hCommHandle) != FILE_TYPE_CHAR) { errval = XTAPI_GENERAL_ERR; goto device_create_comm_exit; } GetCommState(hCommHandle, &commobj->dcb); GetCommTimeouts(hCommHandle, &ctimeouts); commobj->handle = hCommHandle; commobj->baud = lcinfo->dwRate; // commobj->dcb.BaudRate = commobj->baud; // commobj->dcb.fBinary = 1; // commobj->dcb.fNull = 0; // commobj->dcb.ByteSize = 8; // commobj->dcb.StopBits = ONESTOPBIT; // commobj->dcb.fParity = FALSE; // commobj->dcb.Parity = NOPARITY; // commobj->dcb.XonChar = ASCII_XON; // commobj->dcb.XoffChar = ASCII_XOFF; // commobj->dcb.XonLim = 1024; // commobj->dcb.XoffLim = 1024; // commobj->dcb.EofChar = 0; // commobj->dcb.EvtChar = 0; // commobj->dcb.fOutxDsrFlow = FALSE; // commobj->dcb.fOutxCtsFlow = FALSE; // rts/cts off // commobj->dcb.fDtrControl = DTR_CONTROL_ENABLE;// dtr=on // commobj->dcb.fRtsControl = RTS_CONTROL_ENABLE; ctimeouts.ReadIntervalTimeout = 250; ctimeouts.ReadTotalTimeoutMultiplier = 0; ctimeouts.ReadTotalTimeoutConstant = 0; ctimeouts.WriteTotalTimeoutMultiplier = 0; ctimeouts.WriteTotalTimeoutConstant = 0; commobj->dcb.fAbortOnError = FALSE; SetCommTimeouts(hCommHandle, &ctimeouts); SetCommState(hCommHandle, &commobj->dcb); } memset(&commobj->rov, 0, sizeof(OVERLAPPED)); memset(&commobj->wov, 0, sizeof(OVERLAPPED)); commobj->rov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (commobj->rov.hEvent == NULL) { errval = XTAPI_GENERAL_ERR; goto device_create_comm_exit; } commobj->wov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (commobj->wov.hEvent == NULL) { CloseHandle(commobj->rov.hEvent); return 0; } commobj->hThread = NULL; commobj->threadID = (DWORD)(-1); commobj->connect = TRUE; device_create_comm_exit: if (varstr) free(varstr); if (lcinfo) free(lcinfo); return errval; } /* device_hangup frees the call and line damn well better assume that the COMM_OBJ allocated (if at all) is closed via the comm_library. */ int xtapi_device_hangup() { LONG retval; Assert(TapiDev.valid); // Device should be locked! // if (!TapiDev.hCall) return XTAPI_SUCCESS; // if (!TapiDev.hLine) return XTAPI_SUCCESS; // drop any call in progress if (TapiDev.hCall) { LINECALLSTATUS *lcallstat = NULL; MSG msg; DWORD call_state; lcallstat = tapi_line_getcallstatus(TapiDev.hCall); if (!lcallstat) { return XTAPI_OUT_OF_MEMORY; } mprintf((0, "XTAPI: Got linestatus.\n")); if (!(lcallstat->dwCallState & LINECALLSTATE_IDLE)) { // line not IDLE so drop it! retval = xtapi_device_wait_for_reply( lineDrop(TapiDev.hCall, NULL, 0) ); if (retval != XTAPI_SUCCESS) { mprintf((1, "XTAPI: error when lineDrop.\n")); } mprintf((0, "XTAPI: dropped line.\n")); // wait for IDLE mprintf((0, "XTAPI: Waiting for idle.\n")); while (1) { xtapi_device_poll_callstate((uint*)&call_state); if (call_state == XTAPI_LINE_IDLE) break; if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } retval = lineDeallocateCall(TapiDev.hCall); mprintf((0, "XTAPI: deallocated call.\n")); if (retval != 0) { free(lcallstat); return XTAPI_GENERAL_ERR; } TapiDev.hCall = NULL; if (lcallstat) free(lcallstat); } // Free up line. if (TapiDev.hLine) { retval = lineClose(TapiDev.hLine); if (retval != 0) { return XTAPI_GENERAL_ERR; } TapiDev.hLine = NULL; } mprintf((0, "XTAPI: Closed line.\n")); TapiDev.connected = FALSE; return XTAPI_SUCCESS; } /* device_wait_for_reply once a function is called, we wait until we have been given a reply. */ LONG xtapi_device_wait_for_reply(LONG reqID) { if (reqID > 0) { // A valid ID. so we shall wait and see MSG msg; TapiDev.lineReplyReceived = FALSE; TapiDev.asyncRequestedID = (DWORD)reqID; TapiDev.lineReplyResult = LINEERR_OPERATIONFAILED; while (!TapiDev.lineReplyReceived) { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Insert code to take care of shutting down while wait. } return (LONG)TapiDev.lineReplyResult; } return reqID; } /* err translate tapi to xtapi errors */ int xtapi_err(LONG err) { mprintf((1, "TAPI err: %x\n", (DWORD)err)); switch (err) { case 0: return XTAPI_SUCCESS; case LINEERR_ALLOCATED: return XTAPI_APPCONFLICT; case LINEERR_NOMEM: return XTAPI_OUT_OF_MEMORY; case LINEERR_INCOMPATIBLEAPIVERSION: return XTAPI_INCOMPATIBLE_VERSION; case LINEERR_NODEVICE: return XTAPI_NODEVICES; case LINEERR_OPERATIONFAILED: return XTAPI_FAIL; case LINEERR_OPERATIONUNAVAIL: return XTAPI_NOT_SUPPORTED; case LINEERR_RESOURCEUNAVAIL: return XTAPI_OUT_OF_RESOURCES; case LINEERR_REINIT: return XTAPI_BUSY; } return XTAPI_GENERAL_ERR; } /* callback this function is used by TAPI to inform us of when asynchronous requests are valid and of any changes to the current call_state */ void CALLBACK tapi_callback(DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInst, DWORD dw1, DWORD dw2, DWORD dw3) { switch(dwMsg) { case LINE_REPLY: if (dw2 != 0) mprintf((1, "XTAPI: LINE_REPLY err: %x.\n", dw2)); else mprintf((1, "XTAPI: LINE_REPLY received.\n")); if (TapiDev.asyncRequestedID == dw1) { TapiDev.lineReplyReceived = TRUE; TapiDev.lineReplyResult = (LONG)dw2; } break; case LINE_CALLSTATE: TapiDev.callStateReceived = TRUE; TapiDev.call_state = dw1; mprintf((0, "Call_State = %x\n", dw1)); switch(TapiDev.call_state) { case LINECALLSTATE_CONNECTED: if (TapiDev.connected) break; TapiDev.connected = TRUE; break; case LINECALLSTATE_OFFERING: if (TapiDev.connected) { mprintf((1, "TAPI: offering after connected!\n")); break; } if (dw3 != LINECALLPRIVILEGE_OWNER) { mprintf((1, "TAPI: need owner privilege to accept call!.\n")); break; } TapiDev.hCall = (HCALL)hDevice; break; } break; } } /* * TAPI functions */ /* tapi_getdevcaps gets device caps for specified device and returns a valid structure */ LINEDEVCAPS *tapi_getdevcaps(uint dev) { LINEDEVCAPS *pcaps=NULL; LONG retval, size; DWORD apiver; size = sizeof(LINEDEVCAPS) + 256; while (1) { LINEEXTENSIONID extid; retval = lineNegotiateAPIVersion(TapiObj.hObj, dev, TAPI_VERSION, TAPI_VERSION, &apiver, &extid); if (retval != 0) return NULL; pcaps = (LINEDEVCAPS *)realloc(pcaps, size); if (!pcaps) return NULL; memset(pcaps, 0, size); pcaps->dwTotalSize = size; retval = lineGetDevCaps(TapiObj.hObj, dev, apiver, 0, pcaps); if (retval!=0) { free(pcaps); return NULL; } if (pcaps->dwNeededSize > pcaps->dwTotalSize) { size = pcaps->dwNeededSize; continue; } else break; } return pcaps; } /* tapi_line_getaddrstatus retrieves the current status of a given address on the line. returns: NULL if fail. */ LINEADDRESSSTATUS *tapi_line_getaddrstatus(HLINE hLine, DWORD dwID) { LINEADDRESSSTATUS *ad=NULL; LONG retval; int size; size = sizeof(LINEADDRESSSTATUS) + 256; while (1) { ad = (LINEADDRESSSTATUS *)realloc(ad, size); if (!ad) return NULL; memset(ad, 0, size); ad->dwTotalSize = size; retval = lineGetAddressStatus(hLine, dwID, ad); if (retval!=0) { free(ad); return NULL; } if (ad->dwNeededSize > ad->dwTotalSize) { size = ad->dwNeededSize; continue; } else break; } return ad; } /* tapi_line_getaddrcaps retrieves the current caps of a given address on the line. returns: NULL if fail. */ LINEADDRESSCAPS *tapi_line_getaddrcaps(uint dev, DWORD addrID) { LINEADDRESSCAPS *ad=NULL; LONG retval; int size; size = sizeof(LINEADDRESSCAPS) + 256; while (1) { DWORD apiver; LINEEXTENSIONID extid; retval = lineNegotiateAPIVersion(TapiObj.hObj, dev, TAPI_VERSION, TAPI_VERSION, &apiver, &extid); if (retval != 0) return NULL; ad = (LINEADDRESSCAPS *)realloc(ad, size); if (!ad) return NULL; memset(ad, 0, size); ad->dwTotalSize = size; retval = lineGetAddressCaps(TapiObj.hObj, dev, addrID, apiver, 0, ad); if (retval!=0) { free(ad); return NULL; } if (ad->dwNeededSize > ad->dwTotalSize) { size = ad->dwNeededSize; continue; } else break; } return ad; } /* tapi_line_getcallstatus retrieves the current status of a given call on the line. returns: NULL if fail. */ LINECALLSTATUS *tapi_line_getcallstatus(HCALL hCall) { LINECALLSTATUS *ad=NULL; LONG retval; int size; size = sizeof(LINECALLSTATUS) + 256; while (1) { ad = (LINECALLSTATUS *)realloc(ad, size); if (!ad) return NULL; memset(ad, 0, size); ad->dwTotalSize = size; retval = lineGetCallStatus(hCall,ad); if (retval!=0) { free(ad); return NULL; } if (ad->dwNeededSize > ad->dwTotalSize) { size = ad->dwNeededSize; continue; } else break; } return ad; } /* tapi_line_getcallinfo retrieves the current status of a given call on the line. returns: NULL if fail. */ LINECALLINFO *tapi_line_getcallinfo(HCALL hCall) { LINECALLINFO *ad=NULL; LONG retval; int size; size = sizeof(LINECALLINFO) + 256; while (1) { ad = (LINECALLINFO *)realloc(ad, size); if (!ad) return NULL; memset(ad, 0, size); ad->dwTotalSize = size; retval = lineGetCallInfo(hCall,ad); if (retval!=0) { free(ad); return NULL; } if (ad->dwNeededSize > ad->dwTotalSize) { size = ad->dwNeededSize; continue; } else break; } return ad; }