· 7 years ago · Sep 03, 2018, 09:00 PM
1/**********@@@SOFT@@@WARE@@@COPY@@@RIGHT@@@**********************************
2* DIALOGIC CONFIDENTIAL
3*
4* Copyright (C) 1990-2007 Dialogic Corporation. All Rights Reserved.
5* The source code contained or described herein and all documents related
6* to the source code ("Material") are owned by Dialogic Corporation or its
7* suppliers or licensors. Title to the Material remains with Dialogic Corporation
8* or its suppliers and licensors. The Material contains trade secrets and
9* proprietary and confidential information of Dialogic or its suppliers and
10* licensors. The Material is protected by worldwide copyright and trade secret
11* laws and treaty provisions. No part of the Material may be used, copied,
12* reproduced, modified, published, uploaded, posted, transmitted, distributed,
13* or disclosed in any way without Dialogic's prior express written permission.
14*
15* No license under any patent, copyright, trade secret or other intellectual
16* property right is granted to or conferred upon you by disclosure or delivery
17* of the Materials, either expressly, by implication, inducement, estoppel or
18* otherwise. Any license under such intellectual property rights must be
19* express and approved by Dialogic in writing.
20*
21***********************************@@@SOFT@@@WARE@@@COPY@@@RIGHT@@@**********/
22/*****************************************************************************
23 *****************************************************************************
24* C Source: gc_basic_call_model.c
25* Description: This is a demonstration program for a basic call model which performs,
26* Basic make call and DropCall from Outbound end.It also shows the case
27* of simultaneous disconnect.
28* which executes the program continuously until it encounters signal ctrl+c.
29* General Notes: 1. The tab setting used for this file is 3.
30* 2. Pagination is done with Courier New, 10 point, 64 lines per page
31* 3. For purposes of the demo, the outbound side waits
32* 1-2 seconds in the connected state, then calls gc_DropCall()
33* 4. PDK as shipped has an issue where occasionally the first makecall may
34* fail with an invalid state error. The demo program has a recovery
35* mechanism built in. When connected to a switch, this should not be
36* a problem.
37* 5. gc_demo_open_isdn_channel may need to be modified for your environment,
38* especially if you are using T1.
39*
40*
41***************************************************************************/
42
43#ifndef lint
44static char *_csrc = "@(#) %filespec: gc_basic_call_model.c-28 % (%full_filespec: gc_basic_call_model.c-28:csrc:gc#1 %)";
45#endif
46
47#if defined HRC_TARGET
48#define IPV6_CC 1
49#endif
50
51/* OS Header Files */
52
53#include <stdio.h>
54#include <stdlib.h>
55#include <ctype.h>
56#include <string.h>
57#include <time.h>
58#include <signal.h>
59#include <sys/timeb.h>
60
61/* OS Socket API Headers */
62#ifdef _WIN32
63#include <winsock2.h>
64#include <ws2tcpip.h>
65#else
66#include <unistd.h>
67#include <netdb.h>
68#include <sys/socket.h>
69#include <arpa/inet.h>
70#endif
71
72/* Dialogic Header Files */
73#include <gcip.h>
74#include <gclib.h>
75#include <gcisdn.h>
76#include <srllib.h>
77#if defined HRC_TARGET
78#include <hrclib.h>
79#endif
80/* Macro Definitions */
81
82
83#define MAXCHAN 120 /* Maximum number of channels that can be opened - 2 E1 spans */
84#define MAX_DEVNAME 64 /* Maximum length of device name */
85#define MAX_CODECNAME 20 /* Maximum length of codec name */
86#define MAX_STRING_SIZE 1500 /* Maximum length of string that to write on log file */
87 /* Keep large */
88#define MAX_CALLS 2 /* Maximum number of calls per channel */
89#define MIN_CONNECTED_TIME 1 /* Minimum connected time, artifact of test program */
90 /* and most likely will not be needed for a production program */
91
92#define MIN_MAKECALL_RETRY_TIME 5 /* min time to wait before retrying makecall under */
93 /* error conditions */
94#define MAXCODECS 10 /* Maximum number of codecs per VoIP channel */
95#define MAX_VADSTR 10 /* Maximum length of VAD string, e.g. "ON", "OFF", "N/A" */
96
97
98/* Logging defines */
99
100#define NON_GC_APIERR 0 /* Had error on non-GC API function */
101#define EVENT 1 /* An event */
102#define GC_APICALL 2 /* GC API funtion call */
103#define GC_APIERR 3 /* Had error on GC API function */
104#define STATE 4 /* STATE message */
105#define GC_RESULT_INFO 5 /* want GC result information */
106#define MISC 6 /* None of the above */
107#define MISC_WITH_NL 7 /* None of the above with new line codes */
108#define TITLE 8
109
110/* Inbound or Outbound Direction of device */
111
112#define DIR_IN 1
113#define DIR_OUT 2
114
115/* Miscellaneous declarations */
116
117#define YES 1
118#define NO 0
119
120#define ALL_DEVICES -1 /* This value is used to write the */
121 /* miscellaneous statements into all log files */
122#define OUT_OF_RANGE -1 /* callindex is out of range */
123
124/*
125 * Technology Types
126 */
127#define E1ORT1_CAS 1
128#define ISDN 2 /* includes both E1 and T1 ISDN */
129#define ANALOG 3
130#define SS7 4
131#define H323 5
132#define SIP 6
133
134/* Global variables */
135static char logfile[] = "gc_basic_call_model"; /* log filename without the .log */
136static char cfgfile[] = "gc_basic_call_model.cfg"; /* config filename */
137
138static int num_devices; /* Number of devices loaded from cfg file */
139static int interrupted = NO; /* Flag for user interrupted signals */
140static int reset_linedevs_left_to_complete = 0;
141
142/* Data structure which stores call information for each line device */
143typedef struct {
144 CRN crn; /* GlobalCall call handle */
145 int call_state; /* state of first layer state machine */
146 int dropcall_active; /* Flag for simultaneous disconnect check */
147} CALL;
148
149/* TX and RX codecs for each line device although only used for H.323 or SIP */
150typedef struct {
151 char txcodec[MAX_CODECNAME]; /* TX codec type (H.323 or SIP only) */
152 int txrate; /* TX codec rate (H.323 or SIP only) */
153 char txVAD[MAX_VADSTR]; /* TX VAD enabled/disabled (H.323 or SIP only) */
154 char rxcodec[MAX_CODECNAME]; /* RX codec type (H.323 or SIP only) */
155 int rxrate; /* RX codec rate (H.323 or SIP only) */
156 char rxVAD[MAX_VADSTR]; /* RX VAD enabled/disabled (H.323 or SIP only) */
157} CODEC;
158
159/* Data structure which stores all information for each line device */
160static struct channel {
161 LINEDEV ldev; /* GlobalCall line device handle */
162 LINEDEV mediah; /* Media handle - only for H323 */
163 CALL call[MAX_CALLS];
164 int index; /* Device index as loaded from cfg file */
165 int direction; /* Inbound(WaitCall will be issued)
166 or outbound(MakeCall will be issued) */
167 int blocked; /* YES or NO */
168 char netdevice[MAX_DEVNAME]; /* Network device name */
169 char protname[MAX_DEVNAME]; /* Protocol name */
170 char voicedevice[MAX_DEVNAME]; /* Voice device name */
171 char devname[MAX_DEVNAME]; /* Argument to gc_OpenEx() function */
172 char destination_num[MAX_ADDRESS_LEN]; /* Phone */
173 int num_codecs; /* The No. of codecs specified (VoIP device only) */
174 CODEC codec[MAXCODECS]; /* Codecs (VoIP device only) */
175 int numb_calls; /* The No of calls on the device */
176 int waitcall_active; /* Flag for waitcall function for issuing only once*/
177 int makecall_active;
178 int resetlinedev_active; /* Necessary since must ignore call related GC */
179 /* events with gc_ResetLineDev() active */
180 int resetlinedev_required_after_unblocked; /* for ISDN */
181 /* and some error conditions */
182
183 int makecall_timeout; /* necessary since not all technologies support */
184 /* async makecall timeout and they might be */
185 /* different for different technologies */
186 GC_MAKECALL_BLK makecallblk; /* Variable for MAKECALL block */
187 FILE *log_fp; /* logfile Pointer */
188 int techtype; /* technology type: E1ORT1_CAS, ISDN, ANALOG, */
189
190 time_t makecall_time; /* time before which makecall is not allowed */
191 /* this is to add robustness to the app */
192 /* if makecall fails which it might under some */
193 /* circumstances, but will work if try later */
194 /* if this value is 0, then OK to do now */
195
196 /* this variable is in so know when to drop a call */
197 /* it is an artifact of the demo program and */
198 /* and should not be used in production level */
199 /* programs */
200 time_t dropcall_time; /* time to drop the call on the outbound side */
201 /* format is result of time() function call */
202 /* 0 <=> no dropcall required */
203 time_t intercall_time; /* Time when the call was released. The next call */
204 /* would be made after intercall_time + */
205 /* intercall_delay seconds. */
206 int intercall_delay; /* Inter-call delay in milliseconds */
207} port[MAXCHAN];
208
209/* codec translation table */
210typedef struct {
211 char* codecname;
212 int codecvalue;
213 int isVADsupported;
214} CODECXLAT;
215
216static const CODECXLAT codecxlat[] = {
217 {"G.711Alaw", GCCAP_AUDIO_g711Alaw64k, NO},
218 {"G.711ulaw", GCCAP_AUDIO_g711Ulaw64k, NO},
219 {"G.723_5.3k", GCCAP_AUDIO_g7231_5_3k, YES},
220 {"G.723_6.3k", GCCAP_AUDIO_g7231_6_3k, YES},
221 {"G.726_16k", GCCAP_AUDIO_g726_16k, YES},
222 {"G.726_24k", GCCAP_AUDIO_g726_24k, YES},
223 {"G.726_32k", GCCAP_AUDIO_g726_32k, YES},
224 {"G.726_40k", GCCAP_AUDIO_g726_40k, YES},
225 {"G.729", GCCAP_AUDIO_g729 , NO},
226 {"G.729A", GCCAP_AUDIO_g729AnnexA, NO},
227 {"G.729B", GCCAP_AUDIO_g729wAnnexB, YES},
228 {"G.729AB", GCCAP_AUDIO_g729AnnexAwAnnexB, YES},
229 {"GSM_FULL", GCCAP_AUDIO_gsmFullRate, YES},
230 {"T.38UDP", GCCAP_DATA_t38UDPFax, NO},
231 {"DONT_CARE", GCCAP_dontCare, NO}
232};
233
234static int in_calls, out_calls; /* Counters for Inbound calls and Outbound calls */
235static int gc_started = NO; /* Flag is set if gc_Start() is Success */
236time_t start_time; /* To note down the start time for the test */
237
238#ifdef IPV6_CC
239#ifndef WIN32
240#define stricmp strcasecmp
241#endif
242unsigned char sip_address_type = 0; /* IP address mode for SIP call control */
243unsigned char sdp_address_type = 0; /* IP address mode for SDP */
244int useloopback = 0;
245int islinklocal = 0;
246struct sockaddr_in *sin_p;
247struct sockaddr_in6 *sin6_p;
248struct addrinfo *addrinfo_p;
249struct addrinfo *addrinfo6_p;
250struct sockaddr_in localsin;
251struct sockaddr_in6 localsin6;
252char localIPv4address[MAX_ADDRESS_LEN];
253char localIPv6address[MAX_ADDRESS_LEN];
254char localname[64];
255#endif
256
257/* Function prototype(s) */
258
259static void init_srl_mode(void); /* inits SRL mode */
260static void load_config_file(void); /* Loads Configuration File */
261static void drop_outbound_calls_if_required(void); /* used by demo to make repeated calls */
262static void make_calls_if_required(void); /* used by demo to handle makecall error conditions */
263static void printandlog(int index, int log_type, /* Prints and logs */
264 METAEVENT *metavent, char *msg_str, int callindex);
265static void gc_demo_makecall(int index); /* Function for gc_MakeCall() */
266static void exitdemo(int exitlevel); /* Exit routine */
267static void initiate_exit(void);
268static void intr_hdlr(void); /* For handling user interrupted signals */
269static void set_calling_num(struct channel *pline, char *phone_num);
270static void enable_alarm_notification(struct channel *pline);/* enable alarm notification */
271static void gc_demo_open_isdn_channel(int index); /* Technology specific initialization for ISDN */
272static void gc_demo_open_E1orT1_channel(int index);/* Technology specific initialization for PDK */
273static void gc_demo_open_ss7_channel(int index); /* Technology specific initialization for SS7 */
274static void gc_demo_open_analog_channel(int index);/* Technology specific initialization for Analog */
275static void gc_demo_open_H323_channel(int index); /* Technology specific initialization for H323 */
276static void gc_demo_open_SIP_channel(int index); /* Technology specific initialization for SIP */
277static void gc_set_channel_codecs(int index); /* VoIP specific initialization of codecs */
278static void open_all_devices(void); /* Function invokes Technology specific initilization
279 functions to open all devices */
280static void open_device(int index); /* Function invokes Technology specific initilization
281 functions to open specific device */
282static void inbound_application(struct channel *pline); /* PLACE HOLDER FOR YOUR APPLICATION CODE */
283static void outbound_application(struct channel *pline); /* PLACE HOLDER FOR YOUR APPLICATION CODE */
284static void print_alarm_info(METAEVENTP metaeventp, struct channel *pline);
285static void process_event(void); /* Function handles the GlobalCall Events */
286static void process_connected_event(struct channel *pline, METAEVENT *metaeventp);
287static void process_disconnected_event(struct channel *pline, METAEVENT *metaeventp);
288static void process_offered_event(METAEVENT *metaeventp, struct channel *pline);
289static void process_call_related_inbound_event(struct channel *pline, METAEVENT *metaeventp); /* Function handles the GlobalCall Events for inbound */
290static void process_call_related_outbound_event(struct channel *pline, METAEVENT *metaeventp); /* Function handles the GlobalCall Events for outbound */
291static int open_all_logfiles(void); /* Function to open the logfiles */
292static void append_call_state_name(char *buffer, int index, int callindex); /* append call state name to buffer */
293static void format_error_info(char *dest_buffer); /* formats error information */
294static void format_result_info(int index, METAEVENT *metaeventp, /* formats event result information */
295 char *dest_buffer);
296static int existCRN(CRN crn, struct channel *pline); /* Retrieve CRN if available */
297
298/********************************************************************************
299 * NAME: main ()
300 *DESCRIPTION: This is the entry point.
301 * It loads the configuration file.
302 * Take cares the Event Handling process.
303 * Starts GC Library.
304 * Opens Configured Devices.
305 * Waits for user interrupted signal (ctrl+c).
306 * Whenever an event is received on any of the opened
307 * devices, Depending upon the event received the proper
308 * action will be taken.
309 * INPUT: None
310 * RETURNS: 0 if success else -1
311 ********************************************************************************/
312int main(void)
313{
314 int i, ret;
315 char str[MAX_STRING_SIZE], str1[MAX_STRING_SIZE];
316 GC_CCLIB_STATUSALL cclib_status_all;
317 GC_START_STRUCT *gclib_startp = NULL;
318
319
320#ifdef IPV6_CC
321#ifdef WIN32
322 WSADATA wsaData;
323 int iRetval;
324 DWORD dwRetval;
325 DWORD ipbufferlength;
326#else
327 int dwRetval;
328#endif
329
330 char scope[10];
331 struct addrinfo hints;
332 struct addrinfo *it;
333 char *chp;
334 struct sockaddr_in6 *t_sin6;
335 GC_START_STRUCT gclib_start;
336 IPCCLIB_START_DATA cclibStartData;
337 IP_VIRTBOARD virtBoards[1];
338
339 CCLIB_START_STRUCT cclib_start[]={{"GC_H3R_LIB", &cclibStartData},
340 {"GC_IPM_LIB", NULL}};
341
342 localIPv4address[0]='\0';
343 localIPv6address[0]='\0';
344#endif
345
346
347 /* Note down the start time for the test */
348 time(&start_time);
349
350 /* Give control to Event Handler */
351#ifdef _WIN32
352 signal(SIGINT, (void (__cdecl*)(int)) intr_hdlr);
353 signal(SIGTERM, (void (__cdecl*)(int)) intr_hdlr);
354#else
355 signal(SIGHUP, (void (*)()) intr_hdlr);
356 signal(SIGQUIT, (void (*)()) intr_hdlr);
357 signal(SIGINT, (void (*)()) intr_hdlr);
358 signal(SIGTERM, (void (*)()) intr_hdlr);
359#endif
360
361 printandlog(ALL_DEVICES, TITLE, NULL, "********** GC DEMO - BASIC CALL MODEL ***********\n", 0);
362 printandlog(ALL_DEVICES, TITLE, NULL, "DIALOGIC CONFIDENTIAL\n\n"
363 "Copyright (C) 1990-2007 Dialogic Corporation. All Rights Reserved.\n"
364 "The source code contained or described herein and all documents related\n"
365 "to the source code (\"Material\") are owned by Dialogic Corporation or its\n"
366 "suppliers or licensors. Title to the Material remains with Dialogic Corporation\n"
367 "or its suppliers and licensors. The Material contains trade secrets and\n"
368 "proprietary and confidential information of Dialogic or its suppliers and\n"
369 "licensors. The Material is protected by worldwide copyright and trade secret\n"
370 "laws and treaty provisions. No part of the Material may be used, copied,\n"
371 "reproduced, modified, published, uploaded, posted, transmitted, distributed,\n"
372 "or disclosed in any way without Dialogic's prior express written permission.\n\n"
373 "No license under any patent, copyright, trade secret or other intellectual\n"
374 "property right is granted to or conferred upon you by disclosure or delivery\n"
375 "of the Materials, either expressly, by implication, inducement, estoppel or\n"
376 "otherwise. Any license under such intellectual property rights must be\n"
377 "express and approved by Dialogic in writing.\n\n", 0);
378
379 init_srl_mode(); /* set SRL mode to ASYNC, polled */
380
381 /* Load Configuration file */
382 load_config_file();
383
384 /* Function to open all the logfiles */
385 open_all_logfiles();
386 if (num_devices == 0) {
387 printf("No devices enabled - please modify gc_basic_call_model.cfg to enable desired technology\n");
388 exitdemo(1);
389 }
390
391#ifdef IPV6_CC
392
393 if (sip_address_type)
394 {
395#ifdef WIN32
396 iRetval = WSAStartup(MAKEWORD(2, 2), &wsaData);
397
398 if (iRetval)
399 {
400 printandlog(ALL_DEVICES, GC_APIERR, NULL, "WSAStartup() failed\n", 0);
401 exitdemo(1);
402 }
403#endif
404
405 if(!useloopback)
406 {
407 /* retrieve local host name and address */
408 if (gethostname(localname, MAX_STRING_SIZE) != 0) {
409 printandlog(ALL_DEVICES, GC_APIERR, NULL, "Failed to retrieve Host Name", 0);
410 exitdemo(1);
411 }
412 }
413
414 memset(&hints,0,sizeof(hints));
415 hints.ai_family = AF_UNSPEC;
416 hints.ai_socktype = 0;
417 hints.ai_protocol = 0;
418
419
420 dwRetval = getaddrinfo(localname, NULL, &hints, &addrinfo_p);
421 if ( dwRetval != 0 ) {
422 sprintf(str, "getaddrinfo (%s) failed with error: %d\n", localname, dwRetval);
423 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
424 exitdemo(1);
425 }
426 for (it = addrinfo_p; it != NULL; it = it->ai_next)
427 {
428 switch (it->ai_family)
429 {
430 case AF_UNSPEC:
431 sprintf(str, "Family: Unspecified");
432 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
433 break;
434 case AF_INET:
435 if (!strlen(localIPv4address))
436 {
437 localsin = *(struct sockaddr_in *) it->ai_addr; // *sockaddr_ipv4;
438 sprintf(localIPv4address, "%s",inet_ntoa(localsin.sin_addr));
439 sprintf(str, "Family: AF_INET, IPv4 address = %s",localIPv4address);
440 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
441 }
442 break;
443 case AF_INET6:
444 t_sin6 = (struct sockaddr_in6 *) it->ai_addr;
445 if (!strlen(localIPv6address))
446 {
447 localsin6 = *(struct sockaddr_in6 *) it->ai_addr;
448#ifdef WIN32
449 ipbufferlength = sizeof(localIPv6address);
450 iRetval = WSAAddressToString((LPSOCKADDR)&localsin6, (DWORD) it->ai_addrlen, NULL,
451 localIPv6address, &ipbufferlength );
452 if (iRetval)
453 {
454 sprintf(str, "WSAAddressToString failed with %u", WSAGetLastError() );
455 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
456 exitdemo(1);
457 }
458#else
459 inet_ntop(AF_INET6, &localsin6.sin6_addr, localIPv6address, sizeof(localIPv6address));
460#endif
461 if(strstr(localIPv6address, "fe80::") && !islinklocal)
462 {
463 localIPv6address[0]='\0';
464 }
465 else if(strstr(localIPv6address, "::1"))
466 {
467 /* SIP stack does not like the "::1" address in the From header. */
468 sprintf(localIPv6address, "0::1");
469 }
470 chp = strstr(localIPv6address, "%");
471 if(chp)
472 *chp = 0;
473 if (strlen(localIPv6address))
474 {
475 sprintf(str, "Family: AF_INET6, IPv6 address = %s, scope Id = %d", localIPv6address, localsin6.sin6_scope_id);
476 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
477 }
478 }
479 break;
480 default:
481 sprintf(str, "Family: Other %i", it->ai_family);
482 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
483 break;
484 }
485 }
486
487 INIT_IPCCLIB_START_DATA(&cclibStartData, 1, virtBoards);
488 INIT_IP_VIRTBOARD(virtBoards);
489
490 if (sip_address_type == USE_IPv4)
491 {
492 virtBoards[0].localIP.ip_ver = IPVER4;
493 virtBoards[0].localIP.u_ipaddr.ipv4 = localsin.sin_addr.s_addr;
494 }
495 else if (sip_address_type == USE_IPv6)
496 {
497 virtBoards[0].E_SIP_IPv6 = ENUM_Enabled;
498 virtBoards[0].localIPv6.ip_ver = IPVER6;
499 memcpy(&virtBoards[0].localIPv6.u_ipaddr.ipv6, &localsin6.sin6_addr, sizeof(struct in6_addr));
500#ifdef WIN32
501 itoa(localsin6.sin6_scope_id, scope, 10);
502 virtBoards[0].localIPv6_iface_name = scope;
503#else
504 virtBoards[0].localIPv6_iface_name = "eth0";
505#endif
506 }
507 gclib_start.num_cclibs = 2;
508 gclib_start.cclib_list = cclib_start;
509 gclib_startp = &gclib_start;
510 }
511#endif
512
513 /* Start GlobalCall */
514 if (gc_Start(gclib_startp) != GC_SUCCESS) {
515 sprintf(str, "gc_Start(gclib_start) Failed");
516 printandlog(ALL_DEVICES, GC_APIERR, NULL, str, 0);
517 exitdemo(1);
518 }
519
520 /* Set the flag gc_started to YES */
521 gc_started = YES;
522 sprintf(str, "gc_Start(startp = NULL) Success");
523 printandlog(ALL_DEVICES, GC_APICALL, NULL, str, 0);
524
525
526 /* this code is usefule for installation problem debugging */
527 /* you may not wish to use this code in production systems */
528 if (gc_CCLibStatusEx("GC_ALL_LIB", &cclib_status_all) != GC_SUCCESS) {
529 sprintf(str, "gc_CCLibStatusEx(GC_ALL_LIB, &cclib_status_all) Failed");
530 printandlog(ALL_DEVICES, GC_APIERR, NULL, str, 0);
531 exitdemo(1);
532 }
533 strcpy(str, " Call Control Library Status:\n");
534 for (i = 0; i < GC_TOTAL_CCLIBS; i++) {
535 switch (cclib_status_all.cclib_state[i].state) {
536 case GC_CCLIB_CONFIGURED:
537 sprintf(str1, " %s - configured\n", cclib_status_all.cclib_state[i].name);
538 break;
539
540 case GC_CCLIB_AVAILABLE:
541 sprintf(str1, " %s - available\n", cclib_status_all.cclib_state[i].name);
542 break;
543
544 case GC_CCLIB_FAILED:
545 sprintf(str1, " %s - is not available for use\n", cclib_status_all.cclib_state[i].name);
546 break;
547
548 default:
549 sprintf(str1, " %s - unknown CCLIB status\n", cclib_status_all.cclib_state[i].name);
550 break;
551
552
553 }
554 strcat(str, str1);
555 }
556 printandlog(ALL_DEVICES, MISC, NULL, str, 0);
557
558 /* open all the GC devices */
559 open_all_devices();
560
561 /*
562 -- Forever loop where the main work is done - wait for an event or user requested exit
563 */
564 for (;;) {
565 ret = sr_waitevt(500); /* 1/2 second */
566 if (ret != -1) { /* i.e. not timeout */
567 process_event();
568 }
569
570 if (interrupted == NO) { /* flag set in intr_hdlr() */
571 drop_outbound_calls_if_required();/* an artifact of the demo program */
572 /* where the outbound side disconnects 1-2 */
573 /* seconds after connected */
574
575 make_calls_if_required(); /* handles error conditions on makecall */
576 } else {
577 initiate_exit();
578 if (reset_linedevs_left_to_complete == 0) {
579 exitdemo(1);
580 }
581 }
582
583 }
584} /* End of Main */
585
586/****************************************************************
587* NAME: void init_srl_mode(void)
588* DESCRIPTION: Inits SR mode
589* RETURNS: None
590* CAUTIONS: NA
591****************************************************************/
592static void init_srl_mode(void)
593{
594/*
595SRL MODEL TYPE - Single Threaded Async, event handler will be called in the Main thread
596*/
597#ifdef _WIN32
598 int mode = SR_STASYNC | SR_POLLMODE;
599#else
600 int mode = SR_POLLMODE;
601#endif
602
603
604 /* Set SRL mode */
605#ifdef _WIN32
606 if (sr_setparm(SRL_DEVICE, SR_MODELTYPE, &mode) == -1) {
607 printandlog(ALL_DEVICES, NON_GC_APIERR, NULL, "Unable to set to SR_STASYNC | SR_POLLMODE", 0);
608 exitdemo(1);
609 }
610 printandlog(ALL_DEVICES, MISC, NULL, "SRL Model Set to SR_STASYNC | SR_POLLMODE", 0);
611#else
612 if (sr_setparm(SRL_DEVICE, SR_MODEID, &mode) == -1) {
613 printandlog(ALL_DEVICES, NON_GC_APIERR, NULL, "Unable to set mode ID to SR_POLLMODE ", 0);
614 exitdemo(1);
615 }
616
617 printandlog(ALL_DEVICES, MISC, NULL, "SRL mode ID set to SR_POLLMODE", 0);
618#endif
619}
620
621/****************************************************************
622* NAME: open_all_logfiles(void)
623* DESCRIPTION: Function to Open the Logfiles and put file pointers in file
624* pointer field of port array. Later depending upon the index the
625* particular file can be accessed to dump the data.
626* If the network device name is present, then the log file
627* uses the network device name
628* else it uses the voice device name
629* RETURNS: returns 1 if successful
630* CAUTIONS: Assumes device name includes network device name or a voice device name
631****************************************************************/
632static int open_all_logfiles(void)
633{
634 char temp_filename[MAX_DEVNAME], temp_str[MAX_STRING_SIZE];
635 char *i;
636 int index;
637
638 for (index = 0; index < num_devices; index++) {
639 /* build up temp_filename */
640 strcpy(temp_filename, logfile);
641
642 /* case 1: if dti name exists, use it */
643 if (strcmp(port[index].netdevice, "NONE") != 0) {
644 strcat(temp_filename, "_");
645 /* IP has different "dti" name than NON-IP */
646 if (port[index].netdevice[0] == 'i') {
647 sscanf(port[index].devname, ":N_ipt%[^:]", temp_str);
648 strcat(temp_filename, "ipt");
649 } else if (port[index].netdevice[1] == 'k') {
650 sscanf(port[index].devname, ":N_dk%[^:]", temp_str);
651 strcat(temp_filename, "dk");
652 } else {
653 sscanf(port[index].devname, ":N_dti%[^:]", temp_str);
654 strcat(temp_filename, "dti");
655 }
656 strcat(temp_filename, temp_str);
657 }
658 /* case 2: if voice name exists, use it */
659 else if (strcmp(port[index].voicedevice, "NONE") != 0) {
660 i = (char *)strstr(port[index].voicedevice, "B");
661 strcpy(temp_str, i);
662 strcat(temp_filename, "_");
663 strcat(temp_filename, temp_str);
664 } else {
665 printf("Missing both DTI and Voice resource in config file device name (%s)\n",
666 port[index].devname);
667 exitdemo(1);
668 }
669 strcat(temp_filename, ".log");
670
671 if ((port[index].log_fp = fopen(temp_filename, "w")) == NULL) {
672 printf("\nCannot open log file %s for write\n", temp_filename);
673 perror("Reason is: ");
674 exitdemo(1);
675 }
676 printf("%s successfully opened\n", temp_filename);
677
678 } /* End of for loop */
679 return 1;
680}
681
682/****************************************************************
683* NAME: inbound_application(struct channel *pline)
684* DESCRIPTION: Place holder for your inbound application code in the connected state
685* INPUT: pline - pointer to port data structure
686* RETURNS: NA
687* CAUTIONS: none
688****************************************************************/
689static void inbound_application(struct channel *pline)
690{
691 /* Place holder - your app code goes here */
692}
693/****************************************************************
694* NAME: outbound_application(struct channel *pline)
695* DESCRIPTION: Place holder for your outbound application code in the connected state
696* INPUT: pline - pointer to port data structure
697* RETURNS: NA
698* CAUTIONS: none
699****************************************************************/
700static void outbound_application(struct channel *pline)
701{
702 char str[MAX_STRING_SIZE];
703
704 /*************************************************************************/
705 /* YOUR APP WOULD PUT ITS WORK HERE */
706 /* however for purposes of the demo, stay in the connected state */
707 /* at least one second. This is because not all technologies and all */
708 /* protocols support dropcall immediately after connected in the default */
709 /* shipped configuration */
710 /*************************************************************************/
711 time(&pline->dropcall_time);
712 pline->dropcall_time += MIN_CONNECTED_TIME + 1; /* add 1 because the granularity is 1 sec */
713 sprintf(str, "gc_DropCall() will be issued in %d-%d seconds", MIN_CONNECTED_TIME, MIN_CONNECTED_TIME + 1);
714 printandlog(pline->index, MISC, NULL, str, 0);
715}
716
717/****************************************************************
718* NAME: drop_outbound_calls_if_required(void)
719* DESCRIPTION: This drops outbound side calls if required
720* For purposes of the demo, a call stays in the connected
721* state state for a second or two, then is dropped by the outbound side
722* Your application does not need this code
723* and hence it is not optimized
724* RETURNS: NA
725* CAUTIONS: none
726****************************************************************/
727static void drop_outbound_calls_if_required(void)
728{
729 int index;
730 struct channel *pline;
731 time_t current_time; /* in seconds */
732 char str[MAX_STRING_SIZE];
733 int callindex;
734
735 /* get the current time in seconds */
736 time(¤t_time);
737
738 /* check all devices for an "expired" drop call time */
739 /* assumes caller of gc_ResetLinedev() set dropcall_time = 0 */
740 for (index = 0; index < num_devices; index++) {
741 pline = &port[index];
742 /* check if time tripped */
743 if (pline->dropcall_time && (pline->dropcall_time <= current_time)) {
744 /* drop call is no longer required */
745 pline->dropcall_time = 0;
746
747 /* Retrieve CRN - assumes only 1 call is active */
748 callindex = existCRN(-1, pline);
749 if (callindex == OUT_OF_RANGE) {
750 /* CRN not found, so log error and return from function. */
751 sprintf(str, "CRN not found");
752 printandlog(pline->index, MISC_WITH_NL, NULL, str, 0);
753 return;
754 }
755
756 if (gc_DropCall(pline->call[callindex].crn, GC_NORMAL_CLEARING, EV_ASYNC) != GC_SUCCESS) {
757 sprintf(str, "gc_DropCall(crn=0x%lx, cause=GC_NORMAL_CLEARING, mode=EV_ASYNC) Failed", pline->call[callindex].crn);
758 printandlog(pline->index, GC_APIERR, NULL, str, 0);
759 printandlog(pline->index, STATE, NULL, " ", callindex);
760 exitdemo(1);
761 }
762 printandlog(index, MISC_WITH_NL, NULL, "********* Dropping outbound call from drop_outbound_calls_if_required() *********", 0);
763 sprintf(str, "gc_DropCall(crn=0x%lx, cause=GC_NORMAL_CLEARING, mode=EV_ASYNC) Success", pline->call[callindex].crn);
764 printandlog(pline->index, GC_APICALL, NULL, str, 0);
765
766 /* Set the dropcall flag to YES on the related channel */
767 pline->call[callindex].dropcall_active = YES;
768 }
769 }
770}
771
772
773/****************************************************************
774* NAME: make_calls_if_required(void)
775* DESCRIPTION: This procedure sees if there are any calls that need to be made
776* handles some makecall error conditions
777* RETURNS: NA
778* CAUTIONS: none
779****************************************************************/
780static void make_calls_if_required(void)
781
782{
783 int index;
784 struct channel *pline;
785 time_t current_time; /* in seconds */
786
787 time(¤t_time); /* get the current time in seconds */
788
789 /* check all devices for an "expired" make call time */
790 for (index = 0; index < num_devices; index++) {
791 pline = &port[index];
792 if (pline->makecall_time && (pline->makecall_time <= current_time)) { /* check if time tripped */
793 printandlog(index, MISC, NULL, "makecall timer tripped - will retry the makecall", 0);
794 pline->makecall_time = 0; /* makecall request will now be met */
795 gc_demo_makecall(index); /* attempt to make the call again */
796 }
797 if (pline->intercall_time && (pline->intercall_time <= current_time)) { /* check if time tripped */
798 printandlog(index, MISC, NULL, "Inter-Call timer tripped - New call will now be attempted", 0);
799 pline->intercall_time = 0; /* makecall request will now be met */
800 gc_demo_makecall(index); /* attempt to make the call again */
801 }
802
803 }
804}
805
806/****************************************************************
807* NAME: process_event(void)
808* DESCRIPTION: Function to handle the GlobalCall Events
809* RETURNS: NA
810* CAUTIONS: none
811****************************************************************/
812static void process_event(void)
813{
814 METAEVENT metaevent;
815 struct channel *pline;
816 char str[MAX_STRING_SIZE];
817 int index; /* Device index */
818 int evttype;
819 GC_INFO t_info; /* Structure which stores information about GC related errors */
820 int callindex; /* CAUTION: once initialized, do not change */
821 /* the value in this subroutine! */
822 int temp_callindex; /* use the temporary callindex instead */
823
824 /* Populate the metaEvent structure */
825 if(gc_GetMetaEvent(&metaevent) != GC_SUCCESS)
826 {
827 /* serious problem - should never fail */
828 printandlog(ALL_DEVICES, GC_APIERR, NULL, "gc_GetMetaEvent() failed", 0);
829 exitdemo(1);
830 }
831
832 /* process GlobalCall events */
833 if ((metaevent.flags & GCME_GC_EVENT) == 0) {
834 /*********************************************************************/
835 /* Your application might have non-GC events , e.g. voice or fax */
836 /* events that need to be processed. That would be done here. */
837 /* However for the purposes of the demo, non_GC events are only */
838 /* logged. Most likely you will need to do some sort of a */
839 /* search of the port array to find the associated data structure */
840 /* for this event. Since the event is not a GC event, */
841 /* gc_GetUsrAttr() cannot help find the associated GC data structure */
842 /*********************************************************************/
843#if defined HRC_TARGET
844 if (metaevent.evttype == HRCEV_LINK_UP) {
845 printandlog(ALL_DEVICES, MISC, NULL, "********* Received HRCEV_LINK_UP *********", 0);
846 sprintf(str,"Connection to Media Server established");
847 printandlog(ALL_DEVICES,EVENT,NULL,str,0);
848 return;
849 }
850 else if (metaevent.evttype == HRCEV_LINK_DOWN) {
851 printandlog(ALL_DEVICES, MISC, NULL, "********* Received HRCEV_LINK_DOWN *********", 0);
852 sprintf(str,"Lost Connection to Media Server\n\n");
853 printandlog(ALL_DEVICES,EVENT,NULL,str,0);
854 exitdemo(1);
855 }
856 else
857 {
858#endif
859 sprintf(str, "Received a non-GC Event 0x%lx\n", metaevent.evttype);
860 printandlog(ALL_DEVICES, MISC, NULL, str, 0);
861#if defined HRC_TARGET
862 }
863#endif
864 return; /* RETURN POINT */
865 }
866 /**************************************/
867 /* when here => processing a GC event */
868 /**************************************/
869 /* initialize pline to port[index],
870 The pointer to this structure was stored in GC
871 during gc_OpenEx() */
872 pline = (struct channel *) metaevent.usrattr;
873
874 /* Retrieve device index */
875 index = pline->index;
876
877 evttype = metaevent.evttype;
878
879 /* skip checks if no crn for this event */
880
881 if ((metaevent.crn != 0) && (pline->resetlinedev_active == NO)) {
882 /* Assumption: Only GCEV_DETECTED/GCEV_OFFERED will require the CRN
883 to be inserted into the array. */
884 callindex = existCRN(metaevent.crn, pline);
885 if (callindex == OUT_OF_RANGE) {
886 /* CRN was not found, so insert CRN into the array.
887 * Should only occurs when GCEV_DETECTED or GCEV_OFFERED event is received.
888 * Note: GCEV_DETECTED is not enabled, but this code will support it if
889 * if you enable this event */
890 if ((evttype == GCEV_DETECTED) || (evttype == GCEV_OFFERED)) {
891 for (callindex = 0; callindex < MAX_CALLS; callindex++) {
892 if (pline->call[callindex].crn == 0) {
893 /* crn is not found, insert into crn array. */
894 pline->call[callindex].crn = metaevent.crn;
895 break;
896 }
897 }
898 } else {
899 /* Error case: should not happen */
900 sprintf(str, " Received an event(0x%x) with an invalid CRN(0x%lx)", evttype, metaevent.crn);
901 printandlog(index, MISC_WITH_NL, NULL, str, 0);
902 return;
903 }
904 }
905 } else {
906 callindex = 0; /* some printing routines use callindex */
907 }
908 /**********************************************************************/
909 /* CAUTION: at this point callindex is initialized, do not change it! */
910 /**********************************************************************/
911
912 printandlog(index, MISC_WITH_NL, NULL, "********* Received a GC event *********", 0);
913 printandlog(index, EVENT, NULL, GCEV_MSG(evttype), 0);
914 printandlog(index, STATE, NULL, " is the current GC call state ", callindex);
915
916 /* 1st handle the generic events */
917 switch (evttype)
918 {
919 case GCEV_OPENEX:
920 printandlog(index, MISC, NULL, "GCEV_OPENEX received", 0);
921 /* Although this demo currently only opens H.323 and SIP devices asynchronously */
922 if ((pline->techtype == H323) || (pline->techtype == SIP)) {
923
924 /* Enable alarm notification for H.323 or SIP devices which were opened ASYNC */
925 enable_alarm_notification(pline);
926
927 /* Set DTMF mode to inband for SIP as default of Alphanumeric mode
928 * valid only for H.323 devices
929 */
930 if (pline->techtype == SIP) {
931 GC_PARM_BLK *parmblkp = NULL;
932
933 gc_util_insert_parm_val(&parmblkp, IPSET_DTMF, IPPARM_SUPPORT_DTMF_BITMASK,
934 sizeof(char), IP_DTMF_TYPE_INBAND_RTP);
935
936 if (gc_SetUserInfo(GCTGT_GCLIB_CHAN, port[index].ldev, parmblkp, GC_ALLCALLS) != GC_SUCCESS) {
937 sprintf(str, "gc_SetUserInfo(linedev=%ld) Failed configuring DTMF mode", port[index].ldev);
938 printandlog(index, GC_APIERR, NULL, str, 0);
939 exitdemo(1);
940 }
941
942 sprintf(str, "gc_SetUserInfo(linedev=%ld) Success - DTMF mode is inband", port[index].ldev);
943 printandlog(index, GC_APICALL, NULL, str, 0);
944 gc_util_delete_parm_blk(parmblkp);
945
946#ifdef IPV6_CC
947 if (sdp_address_type)
948 {
949 parmblkp = NULL;
950 gc_util_insert_parm_val(&parmblkp, IPSET_SDP, IPPARM_SDP_IP_TYPE,
951 sizeof(char), sdp_address_type);
952
953 if (gc_SetUserInfo(GCTGT_GCLIB_CHAN, port[index].ldev, parmblkp, GC_ALLCALLS) != GC_SUCCESS) {
954 sprintf(str, "gc_SetUserInfo(linedev=%ld) Failed configuring SDP Type", port[index].ldev);
955 printandlog(index, GC_APIERR, NULL, str, 0);
956 exitdemo(1);
957 }
958
959 sprintf(str, "gc_SetUserInfo(linedev=%ld) Success - SDP Type is %d", port[index].ldev, sdp_address_type);
960 printandlog(index, GC_APICALL, NULL, str, 0);
961 gc_util_delete_parm_blk(parmblkp);
962 }
963#endif
964
965 }
966 }
967 break;
968
969 /* This demo only opens H.323 and SIP devices asynchronously */
970 case GCEV_OPENEX_FAIL:
971 printandlog(index, MISC, NULL, "GCEV_OPENEX_FAIL received", 0);
972 printandlog(index, GC_RESULT_INFO, &metaevent, "Reason for GCEV_OPENEX_FAIL ", 0);
973 exitdemo(1);
974 break;
975
976
977 case GCEV_ALARM:
978 print_alarm_info(&metaevent, pline);
979 break;
980
981 case GCEV_FATALERROR:
982 printandlog(index, GC_RESULT_INFO, &metaevent, "Reason for fatalerror ", 0);
983 printandlog(index, STATE, NULL, " ", callindex);
984
985 if (gc_ResultInfo(&metaevent, &t_info) < 0) {
986 sprintf(str, "gc_ResultInfo() call failed");
987 printandlog(index, GC_APIERR, NULL, str, 0);
988 exitdemo(1);
989 }
990
991 if (t_info.gcValue == GCRV_RESETABLE_FATALERROR) {
992 pline->dropcall_time = 0; /* in case on */
993 pline->resetlinedev_active = YES; /* reset linedevice is on as a background process */
994 /* do nothing else= wait till the GCEV_RESETLINEDEV completion event arrives */
995 } else if (t_info.gcValue == GCRV_RECOVERABLE_FATALERROR) {
996 /* do a close and open on recoverable, fatal errors */
997 printandlog(index, MISC, NULL, " Recoverable fatal error occurred - closing device and re-opening it", 0);
998 if (gc_Close(port[index].ldev) < 0) {
999 printandlog(index, GC_APIERR, NULL, "gc_Close() failed", 0);
1000 exitdemo(1);
1001 }
1002 open_device(index);
1003
1004 } else {
1005 printandlog(index, MISC, NULL, " Non-recoverable fatal error occurred", 0);
1006 exitdemo(1);
1007 }
1008 break;
1009
1010 case GCEV_BLOCKED:
1011 pline->blocked = YES; /* Nothing to do when a blocked event occurs */
1012 break;
1013
1014 case GCEV_UNBLOCKED:
1015 /* blocked/unblocked do not cause state transitions */
1016 pline->blocked = NO;
1017 if (pline->resetlinedev_required_after_unblocked == YES) {
1018 pline->resetlinedev_required_after_unblocked = NO;
1019
1020 if (gc_ResetLineDev(port[index].ldev, EV_ASYNC) != GC_SUCCESS) {
1021 sprintf(str, "gc_ResetLineDev(linedev=%ld, mode=EV_ASYNC) Failed", port[index].ldev);
1022 printandlog(index, GC_APIERR, NULL, str, 0);
1023 exitdemo(1);
1024 }
1025 pline->resetlinedev_active = YES;
1026 sprintf(str, "gc_ResetLineDev(linedev=%ld, mode=EV_ASYNC) Success", port[index].ldev);
1027 printandlog(index, GC_APICALL, NULL, str, 0);
1028 /* nothing more can do until the completion event arrives */
1029 break;
1030 }
1031
1032 if (pline->waitcall_active == YES) {
1033 /* waitcall already active - do not call it again */
1034 break;
1035 }
1036 if (pline->resetlinedev_active == YES) {
1037 /* The line device is being reset */
1038 break;
1039 }
1040 /* FALL THROUGH ON PURPOSE */
1041
1042 case GCEV_RESETLINEDEV:
1043 if (evttype == GCEV_RESETLINEDEV) {
1044 /* Reset call related flags and variables */
1045 pline->waitcall_active = NO;
1046 pline->makecall_active = NO;
1047 pline->resetlinedev_active = NO;
1048 for (temp_callindex = 0; temp_callindex < MAX_CALLS; temp_callindex++) {
1049 /* reset internal variables to that of beginning */
1050 pline->call[temp_callindex].call_state = GCST_NULL;
1051 pline->call[temp_callindex].crn = 0;
1052 pline->call[temp_callindex].dropcall_active = NO;
1053 }
1054 }
1055
1056 if ((evttype == GCEV_RESETLINEDEV)
1057 && (reset_linedevs_left_to_complete != 0)) { /* tests if processing shutdown request */
1058 reset_linedevs_left_to_complete--;
1059 sprintf(str, "One less GCEV_RESETLINEDEV to wait for, %d left",
1060 reset_linedevs_left_to_complete);
1061 printandlog(ALL_DEVICES, MISC, NULL, str, 0);
1062
1063 }
1064
1065 /* must further process the event if unblocked */
1066 if ((interrupted == NO) && (pline->blocked == NO)) {
1067 if (pline->direction == DIR_IN) {
1068 /* Note: by definition, waitcall is not active at this point */
1069 if (pline->blocked == NO) {
1070 if (gc_WaitCall(pline->ldev, NULL, NULL, -1, EV_ASYNC) != GC_SUCCESS) {
1071 sprintf(str, "gc_WaitCall(linedev=%ld, crnp=NULL, waittime=0, mode=EV_ASYNC) Failed", pline->ldev);
1072 printandlog(index, GC_APIERR, NULL, str, 0);
1073 exitdemo(1);
1074 }
1075 pline->waitcall_active = YES;
1076 sprintf(str, "gc_WaitCall(linedev=%ld, crnp=NULL, waittime=0, mode=EV_ASYNC) Success", pline->ldev);
1077 printandlog(index, GC_APICALL, NULL, str, 0);
1078 }
1079 } else {
1080 /* Make another call if possible */
1081 gc_demo_makecall(index);
1082 }
1083 }
1084 break;
1085
1086 case GCEV_TASKFAIL:
1087 printandlog(index, GC_RESULT_INFO, &metaevent, "Reason for taskfail ", 0);
1088 printandlog(index, STATE, NULL, " ", callindex);
1089
1090 /* best can do is a reset linedevice */
1091 /* if reset lindevice is already active, then retry it */
1092 if (gc_ResetLineDev(port[index].ldev, EV_ASYNC) == GC_SUCCESS) {
1093 pline->dropcall_time = 0; /* in case on */
1094 pline->call[callindex].dropcall_active = NO; /* in case on */
1095 pline->makecall_active = NO; /* in case on */
1096 pline->resetlinedev_active = YES;
1097 sprintf(str, "gc_ResetLineDev(linedev=%ld, mode=EV_ASYNC) Success", port[index].ldev);
1098 printandlog(index, GC_APICALL, NULL, str, 0);
1099 } else {
1100 sprintf(str, "gc_ResetLineDev(linedev=%ld, mode=EV_ASYNC) Failed", port[index].ldev);
1101 printandlog(index, GC_APIERR, NULL, str, 0);
1102 if (pline->blocked == NO) { /* If no alarms are active */
1103 exitdemo(1); /* Then should never get here */
1104 }
1105 /* when here, resetlinedev failed and in the blocked state */
1106 /* most likely due to an alarm being active */
1107 if (pline->call[callindex].crn) { /* is there an active call? */
1108 if (pline->call[callindex].dropcall_active == NO) { /* drop and release the call if can */
1109 if (gc_DropCall(pline->call[callindex].crn, GC_NORMAL_CLEARING, EV_ASYNC) == GC_SUCCESS)
1110 {
1111 sprintf(str, "gc_DropCall(crn=0x%lx, cause=GC_NORMAL_CLEARING, mode=EV_ASYNC) Success", pline->call[callindex].crn);
1112 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1113 pline->call[callindex].dropcall_active = YES;
1114 } else {
1115 sprintf(str, "gc_DropCall(crn=0x%lx, cause=GC_NORMAL_CLEARING, mode=EV_ASYNC) Failed", pline->call[callindex].crn);
1116 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1117 printandlog(pline->index, STATE, NULL, " ", callindex);
1118 }
1119 }
1120 }
1121 pline->resetlinedev_required_after_unblocked = YES; /* try again later */
1122 }
1123 break;
1124
1125 case GCEV_DISCONNECTED:
1126 if (pline->resetlinedev_active == NO) {
1127 process_disconnected_event(pline, &metaevent); /* process the disconnected event */
1128 }
1129 break;
1130
1131 default:
1132 if (pline->resetlinedev_active == NO) { /* ignore all call related events with reset linedevice active */
1133 if (pline->direction == DIR_IN) {
1134 process_call_related_inbound_event(pline, &metaevent);
1135 } else {
1136 process_call_related_outbound_event(pline, &metaevent);
1137 }
1138 }
1139 break;
1140 }
1141
1142 printandlog(index, STATE, NULL, " is the new GC call state after processing the event", callindex);
1143}
1144
1145/****************************************************************
1146* NAME: void print_alarm_info(METAEVENTP metaeventp, struct channel *pline)
1147* DESCRIPTION: Prings alarm information
1148* INPUTS: metaeventp - pointer to the alarm event
1149* pline - pointer to the channel data structure
1150* RETURNS: NA
1151* CAUTIONS: Assumes already known to be an alarm event
1152****************************************************************/
1153static void print_alarm_info(METAEVENTP metaeventp, struct channel *pline)
1154{
1155 long alarm_number;
1156 char *alarm_name;
1157 unsigned long alarm_source_objectID;
1158 char *alarm_source_object_name;
1159 char str[MAX_STRING_SIZE];
1160
1161 if (gc_AlarmNumber(metaeventp, &alarm_number) != GC_SUCCESS)
1162 {
1163 sprintf(str, "gc_AlarmNumber(...) FAILED");
1164 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1165 printandlog(pline->index, STATE, NULL, " ", 0);
1166 exitdemo(1);
1167 }
1168 if (gc_AlarmName(metaeventp, &alarm_name) != GC_SUCCESS)
1169 {
1170 sprintf(str, "gc_AlarmName(...) FAILED");
1171 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1172 printandlog(pline->index, STATE, NULL, " ", 0);
1173 exitdemo(1);
1174 }
1175
1176 if (gc_AlarmSourceObjectID(metaeventp, &alarm_source_objectID) != GC_SUCCESS)
1177 {
1178 sprintf(str, "gc_AlarmSourceObjectID(...) FAILED");
1179 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1180 printandlog(pline->index, STATE, NULL, " ", 0);
1181 exitdemo(1);
1182 }
1183
1184
1185 if (gc_AlarmSourceObjectName(metaeventp, &alarm_source_object_name) != GC_SUCCESS)
1186 {
1187 sprintf(str, "gc_AlarmSourceObjectName(...) FAILED");
1188 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1189 printandlog(pline->index, STATE, NULL, " ", 0);
1190 exitdemo(1);
1191 }
1192
1193 sprintf(str, "Alarm %s (0x%lx) occurred on Alarm Source Object %s (%d)",
1194 alarm_name, alarm_number, alarm_source_object_name, (int) alarm_source_objectID);
1195
1196 printandlog(pline->index, MISC, NULL, str, 0);
1197}
1198
1199
1200
1201/****************************************************************
1202* NAME: process_disconnected_event(struct channel *pline, METAEVENT *metaeventp)
1203* DESCRIPTION: Function to process a disconnected event
1204* INPUTS: pline - pointer to entry in port table
1205* metaeventp - pointer to the current metaevent
1206* RETURNS: NA
1207* CAUTIONS: Assumes event logging already done
1208****************************************************************/
1209static void process_disconnected_event(struct channel *pline, METAEVENT *metaeventp)
1210{
1211 char str[MAX_STRING_SIZE];
1212 int callindex;
1213
1214 /* Retrieve CRN */
1215 callindex = existCRN(metaeventp->crn, pline);
1216 if (callindex == OUT_OF_RANGE) {
1217 /* CRN not found, so log error and return from function. */
1218 sprintf(str, "CRN not found");
1219 printandlog(pline->index, MISC_WITH_NL, NULL, str, 0);
1220 return;
1221 }
1222
1223 switch (pline->call[callindex].call_state)
1224 {
1225 case GCST_IDLE:
1226 /*************************************************************/
1227 /* this represents a simultaneous disconnect */
1228 /* do nothing as gc_DropCall() must have already been issued */
1229 /* to reach this point! */
1230 /*************************************************************/
1231 break;
1232
1233 case GCST_NULL:
1234 /* In case have a disconnect before dialing is reached */
1235 if (pline->makecall_active == NO) {
1236 /* this represents an error */
1237 printandlog(pline->index, MISC, NULL, "Received GCEV_DISCONNECTED in NULL call state", 0);
1238
1239 /* however, there is a PT - 24402 - which indicates that the inbound side */
1240 /* may under alarm conditions receive a disconnected event */
1241 /* even though the call has not been offered */
1242 /* the other work-around is to enable the detected state, but */
1243 /* for purposes of showing a second work-around, we will "create" the call */
1244 /* and then drop and release it */
1245 pline->call[callindex].crn = metaeventp->crn; /* "create the call */
1246 /* the call state will be init in default */
1247
1248 /* break; - when PT24402 is fixed, put this back in and pull the call "creation" */
1249 }
1250 /* fall through on purpose - need to do a dropcall and release call since there is a call active */
1251 /* may have reached this condition if there is a problem before dialing */
1252 /* or the inbound side has not enabled detection of events and the disconnected */
1253 /* event arrives (usually due to alarm conditions) without the app previously */
1254 /* being informed the call exists */
1255
1256 default:
1257 /* GCEV_DISCONNECTED is allowed in all other call states */
1258 /* If dropcall is already active, don't reissue it */
1259 /* this represents a simultaneous disconnect */
1260 if(pline->call[callindex].dropcall_active == NO)
1261 {
1262 if (gc_DropCall(pline->call[callindex].crn, GC_NORMAL_CLEARING, EV_ASYNC) != GC_SUCCESS)
1263 {
1264 sprintf(str, "gc_DropCall(crn=0x%lx, cause=GC_NORMAL_CLEARING, mode=EV_ASYNC) Failed", pline->call[callindex].crn);
1265 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1266 printandlog(pline->index, STATE, NULL, " ", callindex);
1267 exitdemo(1);
1268 }
1269 sprintf(str, "gc_DropCall(crn=0x%lx, cause=GC_NORMAL_CLEARING, mode=EV_ASYNC) Success", pline->call[callindex].crn);
1270 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1271 pline->call[callindex].dropcall_active = YES;
1272 }
1273 pline->call[callindex].call_state = GCST_DISCONNECTED;
1274 pline->dropcall_time = 0; /* dropcall is no longer required */
1275 break;
1276 }
1277}
1278
1279/****************************************************************
1280* NAME: process_call_related_inbound_event(struct channel *pline, METAEVENT *metaeventp)
1281* DESCRIPTION: Function to process inbound GlobalCall event
1282* INPUTS: pline - pointer to entry in port table
1283* metaeventp - pointer to the metaevent structure
1284* RETURNS: NA
1285* CAUTIONS: Tightly coupled with process_event() for pre-processing
1286* Assumes event logging already done
1287* Disconnected event already handled
1288****************************************************************/
1289static void process_call_related_inbound_event(struct channel *pline, METAEVENT *metaeventp)
1290{
1291 int evttype, index;
1292 char str[MAX_STRING_SIZE];
1293 int unexpected_event = 0; /*assume not till proven otherwise */
1294 /* May be overridden for GCEV_UNBLOCKED */
1295 int callindex;
1296
1297 evttype = metaeventp->evttype;
1298 index = pline->index;
1299
1300 /* Retrieve CRN */
1301 callindex = existCRN(metaeventp->crn, pline);
1302 if (callindex == OUT_OF_RANGE) {
1303 /* CRN not found, so log error and return from function. */
1304 sprintf(str, "CRN not found");
1305 printandlog(pline->index, MISC_WITH_NL, NULL, str, 0);
1306 return;
1307 }
1308
1309 /* Implement the state machine */
1310 switch (pline->call[callindex].call_state)
1311 {
1312 /************************************/
1313 /* first come the call setup states */
1314 /************************************/
1315 case GCST_NULL:
1316 {
1317 switch (evttype)
1318 {
1319 case GCEV_OFFERED:
1320 process_offered_event(metaeventp, pline);
1321 break;
1322
1323 default:
1324 unexpected_event = 1;
1325 break;
1326 }
1327 break;
1328 /* end of case GCST_NULL */
1329 }
1330
1331 case GCST_OFFERED:
1332 {
1333 if (evttype == GCEV_ACCEPT) {
1334 pline->call[callindex].call_state = GCST_ACCEPTED;
1335
1336
1337 /* Microsoft Phoenix Version 5.0 Soft SIP phone requires SDP field in OK event so we */
1338 /* specify coders prior to calling gc_AnswerCall( ) to ensure OK event has SDP field.*/
1339 /* Though done specifically for Phoenix interopability, this will not affect */
1340 /* interoperability with other SIP clients. */
1341 if(pline->techtype == SIP) {
1342 gc_set_channel_codecs(pline->index);
1343 }
1344
1345 /* Note: if both answercall and acceptcall use 0 rings */
1346 /* then the outbound side may not get an alerting event */
1347 /* this behavior is technology/protocol dependent */
1348 if (gc_AnswerCall(pline->call[callindex].crn, 0, EV_ASYNC) != GC_SUCCESS) {
1349 sprintf(str, "gc_AnswerCall(crn=0x%lx, # of rings=0, mode=EV_ASYNC) Failed", pline->call[callindex].crn);
1350 printandlog(index, GC_APIERR, NULL, str, 0);
1351 exitdemo(1);
1352 }
1353 sprintf(str, "gc_AnswerCall(crn=0x%lx, mode=EV_ASYNC) Success", pline->call[callindex].crn);
1354 printandlog(index, GC_APICALL, NULL, str, 0);
1355 } else {
1356 unexpected_event = 1;
1357 }
1358 break;
1359 }
1360
1361 case GCST_ACCEPTED:
1362 {
1363 if (evttype == GCEV_ANSWERED) {
1364 /* Do nothing but change the state to connected. The call will be cleared from
1365 outbound side. Just wait for Disconnect */
1366 /* Your application specific code will go here */
1367 pline->call[callindex].call_state = GCST_CONNECTED;
1368
1369 /* Increment the total calls on this device */
1370 pline->numb_calls++;
1371 /* Increment the total inbound calls on all devices */
1372 in_calls++;
1373 inbound_application(pline); /* YOUR APPLICATION CODE IS CALLED HERE */
1374 } else {
1375 unexpected_event = 1;
1376 }
1377 break;
1378 }
1379
1380 case GCST_CONNECTED:
1381 {
1382 /*************************************************/
1383 /* Depending upon your application, you may or */
1384 /* may not have application specific event */
1385 /* handling here for when in the connected state */
1386 /* You could also have your app specific code */
1387 /* in process_event() */
1388 /*************************************************/
1389 unexpected_event = 1; /* recall that GCEV_DISCONNECTED is handled elsewhere */
1390 break;
1391 }
1392
1393 /*************************************/
1394 /* now come the call teardown states */
1395 /*************************************/
1396 case GCST_DISCONNECTED:
1397 {
1398 /* If the GCEV_OFFERED event is observed in this state,
1399 then add the code that in located in GCST_IDLE state
1400 to resolved the issue. */
1401 if (evttype == GCEV_DROPCALL) {
1402 pline->call[callindex].dropcall_active = NO;
1403
1404 /* Call ReleaseCall */
1405 pline->call[callindex].call_state = GCST_IDLE;
1406
1407 if (gc_ReleaseCallEx(pline->call[callindex].crn, EV_ASYNC) != GC_SUCCESS) {
1408 sprintf(str, "gc_ReleaseCallEx(crn=0x%lx, EV_ASYNC) Failed", pline->call[callindex].crn);
1409 printandlog(index, GC_APIERR, NULL, str, 0);
1410 exitdemo(1);
1411 }
1412 sprintf(str, "gc_ReleaseCallEx(crn=0x%lx, EV_ASYNC) Success", pline->call[callindex].crn);
1413 printandlog(index, GC_APICALL, NULL, str, 0);
1414 fflush(stdout);
1415 } else {
1416 unexpected_event = 1;
1417 }
1418 break;
1419 }
1420
1421 case GCST_IDLE:
1422 {
1423 /* In most cases, only GCEV_RELEASECALL will be seen in this state,
1424 but GCEV_OFFERED can be received after gc_ReleaseCallEx() and
1425 before GCEV_RELEASECALL. */
1426 if (evttype == GCEV_RELEASECALL) {
1427 pline->call[callindex].call_state = GCST_NULL;
1428 pline->call[callindex].crn = 0;
1429 } else if (evttype == GCEV_OFFERED) {
1430 process_offered_event(metaeventp, pline);
1431 } else {
1432 unexpected_event = 1;
1433 }
1434 break;
1435 }
1436
1437 default:
1438 {
1439 /* Should never get here */
1440 printandlog(index, MISC, NULL, "Invalid state in process_call_related_inbound_event()", 0);
1441 printandlog(index, STATE, NULL, " ", callindex);
1442 break;
1443 }
1444 }
1445
1446 if (unexpected_event && (evttype != GCEV_UNBLOCKED)) {
1447 printandlog(index, GC_RESULT_INFO, metaeventp, "Event was unexpected in the current state", 0);
1448 printandlog(index, STATE, NULL, " ", callindex);
1449 }
1450}
1451
1452/****************************************************************
1453* NAME: process_offered_event(struct channel *pline)
1454* DESCRIPTION: Function to process an offered event while in the NULL state
1455* INPUTS: pline - pointer to entry in port table
1456* RETURNS: NA
1457* CAUTIONS: Assumes event logging already done
1458****************************************************************/
1459static void process_offered_event(METAEVENT *metaeventp, struct channel *pline)
1460{
1461 char dnis[GC_ADDRSIZE];
1462 char ani[GC_ADDRSIZE];
1463 char caller_name[GC_ADDRSIZE];
1464 char str[MAX_STRING_SIZE];
1465 int callindex;
1466
1467 /* Retrieve CRN */
1468 callindex = existCRN(metaeventp->crn, pline);
1469 if (callindex == OUT_OF_RANGE) {
1470 printandlog(pline->index, MISC, NULL, "metaeventp->crn unexpectedly not found for a GCEV_OFFERED event", 0);
1471 exitdemo(1);
1472 }
1473
1474 /* Save the CRN in port[index] and call Acceptcall */
1475 pline->call[callindex].call_state = GCST_OFFERED;
1476 pline->call[callindex].crn = metaeventp->crn;
1477
1478 /* show some technology specific functionality */
1479 /* get destination and origination if not ANALOG */
1480 if (pline->techtype != ANALOG) {
1481 /* first, get called party number */
1482 if (gc_GetCallInfo(pline->call[callindex].crn, DESTINATION_ADDRESS, dnis) == GC_SUCCESS) {
1483 sprintf(str, "gc_GetCallInfo(crn=0x%lx) Success - called party = %s", pline->call[callindex].crn, dnis);
1484 } else {
1485 sprintf(str, "gc_GetCallInfo(crn=0x%lx) Failure - called party not available ", pline->call[callindex].crn);
1486 }
1487 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1488 /* do application specific dnis event processing here */
1489 /* For simplicity's sake, max DDI logic is not shown here */
1490
1491 /* next get the calling party here */
1492 if (gc_GetCallInfo(pline->call[callindex].crn, ORIGINATION_ADDRESS, ani) == GC_SUCCESS) {
1493 sprintf(str, "gc_GetCallInfo(crn=0x%lx) Success - calling party = %s", pline->call[callindex].crn, ani);
1494 } else {
1495 sprintf(str, "gc_GetCallInfo(crn=0x%lx) Failure - calling party not available", pline->call[callindex].crn);
1496 }
1497 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1498 } else {
1499 /* get calling party, if Analog */
1500 if (gc_GetCallInfo(pline->call[callindex].crn, ORIGINATION_ADDRESS, ani) == GC_SUCCESS) {
1501 /* get ANI */
1502 sprintf(str, "gc_GetCallInfo(crn=0x%lx) Success - calling party = %s", pline->call[callindex].crn, ani);
1503 } else {
1504 sprintf(str, "gc_GetCallInfo(crn=0x%lx) Failure - calling party not available", pline->call[callindex].crn);
1505 }
1506 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1507
1508 /* get the call info */
1509 if (gc_GetCallInfo(pline->call[callindex].crn, CALLNAME, caller_name) == GC_SUCCESS) {
1510 sprintf(str, "gc_GetCallInfo(crn=0x%lx, CALLNAME) Success, name = %s",
1511 pline->call[callindex].crn, caller_name);
1512 } else {
1513 sprintf(str, "gc_GetCallInfo(crn=0x%lx, CALLNAME) Failure, name not available",
1514 pline->call[callindex].crn);
1515 }
1516 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1517 }
1518
1519 /* Note: if # of rings is == 0 then the outbound side */
1520 /* may not get the alerting event, depending upon the protocol */
1521 /* if the number of rings is 1, there is an occasional problem in */
1522 /* in the outbound side's ability to recognize the tone */
1523 if (gc_AcceptCall(pline->call[callindex].crn, 3, EV_ASYNC) != GC_SUCCESS) {
1524 sprintf(str, "gc_AcceptCall(crn=0x%lx, # of rings=3, mode=EV_ASYNC) Failed", pline->call[callindex].crn);
1525 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1526 exitdemo(1);
1527 }
1528 sprintf(str, "gc_AcceptCall(crn=0x%lx, mode=EV_ASYNC) Success", pline->call[callindex].crn);
1529 printandlog(pline->index, GC_APICALL, NULL, str, 0);
1530}
1531
1532/****************************************************************
1533* NAME: process_call_related_outbound_event(struct channel *pline, METAEVENT *metaeventp)
1534* DESCRIPTION: Function to process outbound GlobalCall event
1535* INPUTS: pline - pointer to entry in port table
1536* metaeventp - pointer to the metaevent structure
1537* RETURNS: NA
1538* CAUTIONS: Tightly coupled with process_event() for pre-processing
1539* Assumes event logging already done
1540* Disconnected event already handled
1541****************************************************************/
1542static void process_call_related_outbound_event(struct channel *pline, METAEVENT *metaeventp)
1543{
1544 int evttype, index;
1545 int unexpected_event = 0; /* till proven otherwise */
1546 char str[MAX_STRING_SIZE];
1547 int callindex;
1548
1549 evttype = metaeventp->evttype;
1550 index = pline->index;
1551
1552 /* Retrieve CRN */
1553 callindex = existCRN(metaeventp->crn, pline);
1554 if (callindex == OUT_OF_RANGE) {
1555 /* CRN not found, so log error and return from function. */
1556 sprintf(str, "CRN not found");
1557 printandlog(pline->index, MISC_WITH_NL, NULL, str, 0);
1558 return;
1559 }
1560
1561 switch (pline->call[callindex].call_state)
1562 {
1563 /************************************/
1564 /* first come the call setup states */
1565 /************************************/
1566 case GCST_NULL:
1567 {
1568 switch (evttype)
1569 {
1570 case GCEV_ALERTING: /* just change the state to alerting - no other action is necessary */
1571 pline->call[callindex].call_state = GCST_ALERTING;
1572 break;
1573
1574 case GCEV_PROCEEDING: /* just change the state to proceeding - no other action is necessary */
1575 pline->call[callindex].call_state = GCST_PROCEEDING;
1576 break;
1577
1578 /* This case can happen in some technologies/protocols if the inbound side */
1579 /* answered the call without ringing (i.e. if acceptcall was issued, */
1580 /* then the rings parameter in both acceptcall and anwercall was 0 */
1581 /* if only answercall was issued, then the # of rings was 0 */
1582 /* The internal GC call state should be Dialing, but the dialing event */
1583 /* is disabled by default and we did not enable it */
1584 case GCEV_CONNECTED:
1585 process_connected_event(pline, metaeventp);
1586 break;
1587
1588 default:
1589 unexpected_event = 1;
1590 break;
1591 }
1592 break;
1593 }
1594
1595 case GCST_ALERTING:
1596 {
1597 if (evttype == GCEV_CONNECTED) {
1598 process_connected_event(pline, metaeventp);
1599 } else {
1600 unexpected_event = 1;
1601 }
1602 break;
1603 }
1604
1605 case GCST_PROCEEDING:
1606 {
1607 switch (evttype)
1608 {
1609 case GCEV_ALERTING: /* just change the state to alerting - no other action is necessary */
1610 pline->call[callindex].call_state = GCST_ALERTING;
1611 break;
1612
1613 case GCEV_CONNECTED:
1614 process_connected_event(pline, metaeventp);
1615 break;
1616
1617 default:
1618 unexpected_event = 1;
1619 break;
1620 }
1621 break;
1622 }
1623
1624 /*************************************/
1625 /* now come the call teardown states */
1626 /*************************************/
1627 case GCST_CONNECTED:
1628 {
1629 /*************************************************/
1630 /* Depending upon your application, you may or */
1631 /* may not have application specific event */
1632 /* handling here for when in the connected state */
1633 /* You could also have your app specific code */
1634 /* in process_event() */
1635 /*************************************************/
1636 if (evttype != GCEV_DROPCALL) { /* recall the outbound side issues a gc_DropCall() */
1637 unexpected_event = 1; /* for the purposes of this demo */
1638 break;
1639 }
1640 /* FALL THROUGH ON PURPOSE since this is one of the 2 ways of reaching Idle */
1641 /* the other being a disconnected event */
1642 }
1643
1644 case GCST_DISCONNECTED:
1645 {
1646 if (evttype == GCEV_DROPCALL) {
1647 pline->call[callindex].dropcall_active = NO;
1648
1649 /* Call ReleaseCall */
1650 pline->call[callindex].call_state = GCST_IDLE;
1651
1652 if (gc_ReleaseCallEx(pline->call[callindex].crn, EV_ASYNC) != GC_SUCCESS) {
1653 sprintf(str, "gc_ReleaseCallEx(crn=0x%lx, EV_ASYNC) Failed", pline->call[callindex].crn);
1654 printandlog(index, GC_APIERR, NULL, str, 0);
1655 exitdemo(1);
1656 }
1657 sprintf(str, "gc_ReleaseCallEx(crn=0x%lx, EV_ASYNC) Success", pline->call[callindex].crn);
1658 printandlog(index, GC_APICALL, NULL, str, 0);
1659 fflush(stdout);
1660 break;
1661 } else {
1662 unexpected_event = 1;
1663 }
1664 break;
1665 }
1666
1667 case GCST_IDLE:
1668 {
1669 if (evttype == GCEV_RELEASECALL) {
1670 pline->call[callindex].call_state = GCST_NULL;
1671 pline->call[callindex].crn = 0;
1672 pline->makecall_active = NO; /* makecall is no longer active */
1673 if(pline->intercall_delay) /* Set Inter-call Delay if specified */
1674 {
1675 time(&pline->intercall_time);
1676 pline->intercall_time += pline->intercall_delay;
1677 }
1678 gc_demo_makecall(index);
1679 } else {
1680 unexpected_event = 1;
1681 }
1682 break;
1683 }
1684
1685 default:
1686 {
1687 /* Should never get here */
1688 printandlog(index, MISC, NULL, "Invalid state in process_call_related_outbound_event()", 0);
1689 break;
1690 }
1691 }
1692
1693 if (unexpected_event && (evttype != GCEV_UNBLOCKED)) {
1694 printandlog(index, GC_RESULT_INFO, metaeventp, "Event was unexpected in the current state", 0);
1695 printandlog(index, EVENT, NULL, GCEV_MSG(evttype), 0);
1696 printandlog(index, STATE, NULL, " ", callindex);
1697 }
1698}
1699
1700/****************************************************************
1701* NAME: process_connected_event(struct channel *pline, METAEVENT *metaeventp)
1702* DESCRIPTION: Function to process a connected event
1703* INPUTS: pline - pointer to entry in port table
1704* RETURNS: NA
1705* CAUTIONS: Assumes event logging already done
1706* Recall that this is only done for the outbound side
1707* as the GCEV_CONNECTED event is an outbound side event only
1708****************************************************************/
1709static void process_connected_event(struct channel *pline, METAEVENT *metaeventp)
1710{
1711 char str[MAX_STRING_SIZE];
1712 char value; /* for connect type */
1713 int rc;
1714 int callindex;
1715
1716 /* Retrieve CRN */
1717 callindex = existCRN(metaeventp->crn, pline);
1718 if (callindex == OUT_OF_RANGE) {
1719 /* CRN not found, so log error and return from function. */
1720 sprintf(str, "CRN not found");
1721 printandlog(pline->index, MISC_WITH_NL, NULL, str, 0);
1722 return;
1723 }
1724
1725 /* Obtain connect type reason if not H323 nor SIP */
1726 if ((pline->techtype != H323) &&
1727 (pline->techtype != SIP) &&
1728 (pline->techtype != SS7)){
1729 /* print connect type */
1730 rc = gc_GetCallInfo(pline->call[callindex].crn, CONNECT_TYPE, &value);
1731 if (rc != GC_SUCCESS) {
1732 if (rc == EGC_UNSUPPORTED) {
1733 strcpy(str, "call connected - gc_GetCallInfo(CONNECT_TYPE) not supported");
1734 } else {
1735 strcpy(str, "call connected - gc_GetCallInfo(CONNECT_TYPE) error\n");
1736 sprintf(str, "gc_GetCallInfo(crn=0x%lx, CONNECT_TYPE, &value) failed", pline->call[callindex].crn);
1737 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1738 }
1739 } else {
1740 switch (value)
1741 {
1742 case GCCT_CAD :
1743 strcpy(str, "call connected - cadence break");
1744 break;
1745
1746 case GCCT_LPC :
1747 strcpy(str, "call connected - loop current drop");
1748 break;
1749
1750 case GCCT_PVD :
1751 strcpy(str, "call connected - voice detection");
1752 break;
1753
1754 case GCCT_PAMD :
1755 strcpy(str, "call connected - answering machine");
1756 break;
1757
1758 case GCCT_FAX :
1759 strcpy(str, "call connected - FAX machine");
1760 break;
1761
1762 case GCCT_NA :
1763 strcpy(str, "call connected - call progress not applicable");
1764 break;
1765
1766 default :
1767 strcpy(str, "call connected - unknown connect type");
1768 break;
1769 }
1770 }
1771 printandlog(pline->index, MISC, NULL, str, 0);
1772 }
1773
1774 pline->call[callindex].call_state = GCST_CONNECTED;
1775 pline->numb_calls++; /* Increment the total calls on this device */
1776 out_calls++; /* Increment the total outbound calls on all devices */
1777 outbound_application(pline); /* call your application specific code here */
1778}
1779
1780/****************************************************************
1781* NAME: enable_alarm_notification(struct channel *pline)
1782* DESCRIPTION: Enables all alarms notification for pline
1783* Also fills in pline->mediah
1784* INPUT: pline - pointer to channel data structure
1785* RETURNS: None - exits if error
1786* CAUTIONS: Does not sanity checking as to whether or not the technology supports
1787* alarms - assumes caller has done that already
1788****************************************************************/
1789static void enable_alarm_notification(struct channel *pline)
1790{
1791 char str[MAX_STRING_SIZE];
1792 int alarm_ldev; /* linedevice that alarms come on */
1793
1794
1795 alarm_ldev = pline->ldev; /* until proven otherwise */
1796 if (pline->techtype == H323) {
1797 /* Recall that the alarms for IP come on the media device, not the network device */
1798 if (gc_GetResourceH(pline->ldev, &alarm_ldev, GC_MEDIADEVICE) != GC_SUCCESS) {
1799 sprintf(str, "gc_GetResourceH(linedev=%ld, &alarm_ldev, GC_MEDIADEVICE) Failed", pline->ldev);
1800 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1801 exitdemo(1);
1802 }
1803 pline->mediah = alarm_ldev; /* save for later use */
1804 }
1805
1806 if (gc_SetAlarmNotifyAll(alarm_ldev, ALARM_SOURCE_ID_NETWORK_ID, ALARM_NOTIFY) != GC_SUCCESS) {
1807 sprintf(str, "gc_SetAlarmNotifyAll(linedev=%ld, ALARM_SOURCE_ID_NETWORK_ID, \
1808ALARM_NOTIFY) Failed", pline->ldev);
1809 printandlog(pline->index, GC_APIERR, NULL, str, 0);
1810 printandlog(pline->index, MISC, NULL, "You may ignore this error - \
1811most likely the underlying technology does not support alarms yet", 0);
1812 } else {
1813 sprintf(str, "gc_SetAlarmNotifyAll(linedev=%ld, ALARM_SOURCE_ID_NETWORK_ID, ALARM_NOTIFY) PASSED", pline->ldev);
1814 printandlog(pline->index, MISC, NULL, str, 0);
1815 }
1816}
1817
1818
1819/****************************************************************
1820* NAME: open_all_devices(void)
1821* DESCRIPTION: Routine calls the specific technology function, which
1822* opens all the line devices.
1823* INPUT: None
1824* RETURNS: None - exits if error
1825* CAUTIONS: none
1826****************************************************************/
1827static void open_all_devices(void)
1828{
1829 int index;
1830
1831 for (index = 0; index < num_devices; index++) {
1832 open_device(index);
1833 }
1834}
1835/****************************************************************
1836* NAME: open_devices(int index)
1837* DESCRIPTION: Routine calls the specific technology function, which
1838* opens all specific line device.
1839* INPUT: index - index into port data structure
1840* RETURNS: None - exits if error
1841* CAUTIONS: Since multiple paths here, will init run-time fields of port data structure
1842****************************************************************/
1843static void open_device(int index)
1844{
1845 int callindex;
1846
1847 /* reset run-time variables that might now be wrong */
1848 port[index].ldev = 0;
1849
1850 /* reset the call related information */
1851 for (callindex = 0; callindex < MAX_CALLS; callindex++) {
1852 port[index].call[callindex].crn = 0;
1853 port[index].call[callindex].call_state = GCST_NULL;
1854 port[index].call[callindex].dropcall_active = NO;
1855 }
1856
1857 port[index].index = index;
1858 port[index].blocked = YES;
1859 port[index].waitcall_active = NO;
1860 port[index].makecall_active = NO;
1861 port[index].resetlinedev_active = NO;
1862 port[index].resetlinedev_required_after_unblocked = NO;
1863 port[index].dropcall_time = 0;
1864 port[index].makecall_time = 0;
1865 port[index].intercall_time = 0;
1866
1867 if ((strcmp(port[index].protname, "isdn") == 0) ||
1868 (strcmp(port[index].protname, "ISDN") == 0)) {
1869 gc_demo_open_isdn_channel(index);
1870 } else if ((strstr(port[index].protname, "ss7") != NULL) ||
1871 (strstr(port[index].protname, "SS7") != NULL)) {
1872 gc_demo_open_ss7_channel(index);
1873 } else if ((strstr(port[index].protname, "_AN_") != NULL) ||
1874 (strstr(port[index].protname, "_an_") != NULL)) {
1875 gc_demo_open_analog_channel(index);
1876 } else if (strstr(port[index].protname, "H323") != NULL) {
1877 gc_demo_open_H323_channel(index);
1878 } else if (strstr(port[index].protname, "SIP") != NULL) {
1879 gc_demo_open_SIP_channel(index);
1880 } else { /* assume everything else is E1 or T1 */
1881 /* if wrong, then gc_OpenEx() will fail */
1882 gc_demo_open_E1orT1_channel(index);
1883 }
1884}
1885
1886/****************************************************************
1887* NAME: gc_demo_makecall(index)
1888* DESCRIPTION: Routine calls the gc_Makecall() function if not blocked and there is not a call active
1889* INPUT: index into port[] for this channel
1890* RETURNS: exits with exitdemo() if not successful
1891* CAUTIONS: none
1892****************************************************************/
1893static void gc_demo_makecall(int index)
1894{
1895 char str[MAX_STRING_SIZE];
1896 struct channel *pline;
1897 GC_MAKECALL_BLK *makecallblkp; /* Variable for MAKECALL block */
1898 int callindex;
1899 char *numberstr;
1900
1901 pline = &port[index];
1902 if ( (pline->blocked == YES) || (pline->resetlinedev_active == YES)
1903 || (interrupted == YES) ) {
1904 return; /* cannot make a call until unblocked and completion of reset line device */
1905 }
1906
1907 /* Find empty place in array to store CRN. */
1908 for (callindex = 0; callindex < MAX_CALLS; callindex++) {
1909 if (pline->call[callindex].crn == 0) {
1910 break;
1911 }
1912 }
1913
1914 if (pline->makecall_active == YES) {
1915 printandlog(index, MISC, NULL, "Trying to make a call while makecall is already active\n", 0);
1916 return;
1917 }
1918
1919 /* verify OK to do a makecall now */
1920 if ((pline->makecall_time) || (pline->intercall_time)){
1921 /* makecall or intercall timer active, cannot do just now */
1922 return;
1923 }
1924
1925
1926 if(pline->techtype != SIP) {
1927 numberstr = pline->destination_num;
1928 } else { /* else if SIP: */
1929 /* we are setting destination address in makecallblk */
1930 numberstr = NULL;
1931 /* and we are specifying coders on a per call basis */
1932 gc_set_channel_codecs(index);
1933 }
1934
1935 /* if the makecall block has been initialized, use it */
1936 /* else use NULL for the makecall block pointer */
1937 makecallblkp = (pline->makecallblk.gclib != NULL) ? &pline->makecallblk : NULL;
1938
1939 if (gc_MakeCall(pline->ldev, &pline->call[callindex].crn, numberstr, makecallblkp, pline->makecall_timeout, EV_ASYNC) == GC_SUCCESS) {
1940 pline->makecall_active = YES; /* indicate there is a call active */
1941 sprintf(str, "gc_MakeCall(linedev=%ld, numberstr=%s, mode=EV_ASYNC) Success", pline->ldev, pline->destination_num);
1942 printandlog(index, GC_APICALL, NULL, str, 0);
1943 } else {
1944 sprintf(str, "gc_MakeCall(linedev=%ld, numberstr=%s, mode=EV_ASYNC) Failed", pline->ldev, pline->destination_num);
1945 printandlog(index, GC_APIERR, NULL, str, 0);
1946 if (pline->techtype == ISDN) {
1947 sprintf(str, "Please verify setup in gc_demo_open_isdn_channel() is correct");
1948 printandlog(index, MISC, NULL, str, 0);
1949 }
1950
1951 /* There are circumstances under which makecall will fail now, but will succeed later */
1952 /* so rather than exit the app, let's schedule a retry */
1953 time(&pline->makecall_time);
1954 pline->makecall_time += MIN_MAKECALL_RETRY_TIME + 1;
1955 sprintf(str, "Will retry the makecall in %d-%d seconds", MIN_MAKECALL_RETRY_TIME, MIN_MAKECALL_RETRY_TIME + 1);
1956 printandlog(index, MISC, NULL, str, 0);
1957 }
1958} /* End of Function */
1959
1960/****************************************************************
1961* NAME: gc_demo_open_isdn_channel(index)
1962* DESCRIPTION: initialize a port used by an ISDN protocol
1963* INPUT: index into port[] for this channel
1964* RETURNS: exits with exitdemo() if not successful
1965* CAUTIONS: 1. This will need to be modified for some configurations/environments
1966* 2. Many parameters are "commented" out. The purpose of this is to
1967* give you a template of how to change default parameters if you need
1968* to do so
1969* 3. Don't be intimidated by the size of this procedure.
1970* As previously indicated, much of this procedure has
1971* commented out code which is intended to give examples
1972* for those who need it. Many developers will not.
1973****************************************************************/
1974static void gc_demo_open_isdn_channel(int index)
1975{
1976 char str[MAX_STRING_SIZE];
1977 GCLIB_MAKECALL_BLK *gclib_makecallp;
1978 GC_PARM_BLKP parmblkp = NULL;
1979
1980
1981 printandlog(index, MISC, NULL, "ISDN device being opened", 0);
1982
1983 /* Initial channel state is blocked */
1984 port[index].blocked = YES;
1985 port[index].techtype = ISDN;
1986 port[index].makecall_timeout = 0; /* timeout parameter is ignored for ISDN */
1987
1988 /* initializes the GC_PARM_BLK with the respective set_id, parm_id and the values */
1989
1990 /*******
1991 -- To set the bearer channel transfer mode information
1992 -- Commented out as this is the default. Change if you wish to use
1993 -- other than the default value
1994 -- gc_util_insert_parm_val(&parmblkp, GCIS_SET_BEARERCHNL, GCIS_PARM_TRANSFERMODE,
1995 -- sizeof(int), ISDN_ITM_CIRCUIT);
1996 *******/
1997
1998 /*******
1999 -- To set the bearer channel transfer rate information
2000 -- Commented out as this is the default. Change if you wish to use
2001 -- other than the default value
2002 -- gc_util_insert_parm_val(&parmblkp, GCIS_SET_BEARERCHNL, GCIS_PARM_TRANSFERRATE,
2003 -- sizeof(int), BEAR_RATE_64KBPS);
2004 *******/
2005
2006 /*******
2007 -- To set Layer 1 protocol to use on bearer channel
2008 -- Commented out as this is the default. Change if you wish to use
2009 -- other than the default value
2010 -- gc_util_insert_parm_val(&parmblkp, GCSET_CHAN_CAPABILITY, GCPARM_TYPE,
2011 -- sizeof(int), GCCAPTYPE_AUDIO);
2012 *******/
2013
2014 /*******
2015 -- To set Layer 1 protocol to use on bearer channel
2016 -- The default is not good for all our E1 boards, for example Springware
2017 -- WARNING: this will need to be changed for T1. Commenting out this
2018 -- function call will work in many T1 cases
2019 *******/
2020 gc_util_insert_parm_val(&parmblkp, GCSET_CHAN_CAPABILITY, GCPARM_CAPABILITY,
2021 sizeof(int), GCCAP_AUDIO_g711Alaw64k);
2022
2023 /*******
2024 -- To set User rate to use on bearer channel (layer 1 rate)
2025 -- Commented out as this is the default. Change if you wish to use
2026 -- other than the default value
2027 -- gc_util_insert_parm_val(&parmblkp, GCSET_CHAN_CAPABILITY, GCPARM_RATE,
2028 -- sizeof(int), GCCAPRATE_DEFAULT);
2029 *******/
2030
2031 /*******
2032 -- To support the specific feature or service requested from the network
2033 -- Commented out as this is not supported by E1 and DM3.
2034 -- gc_util_insert_parm_val(&parmblkp, GCIS_SET_FACILITY, GCIS_PARM_FACILITY_CODINGVALUE,
2035 -- sizeof(int), 0x03);
2036 *******/
2037
2038 /*******
2039 -- To support the specific feature or service requested from the network
2040 -- Commented out as this is not supported by E1 and DM3.
2041 -- gc_util_insert_parm_val(&parmblkp, GCIS_SET_FACILITY, GCIS_PARM_FACILITY_FEATURESERVICE,
2042 -- sizeof(int), ISDN_SERVICE);
2043 *******/
2044
2045 /*******
2046 -- Note: gclib_makecallp is not freed since a "permanent" data
2047 -- structure. Assume program exit will free the resource
2048 *******/
2049 gclib_makecallp
2050 = (GCLIB_MAKECALL_BLKP) malloc(sizeof(GCLIB_MAKECALL_BLK) + parmblkp->parm_data_size);
2051 if (gclib_makecallp == NULL) {
2052 printandlog(ALL_DEVICES, MISC, NULL, "Could not allocate memory\n", 0);
2053 exitdemo(1);
2054 }
2055 port[index].makecallblk.cclib = NULL;
2056 port[index].makecallblk.gclib = gclib_makecallp;
2057
2058 /* Set the extra data in the makecall block */
2059 gclib_makecallp->ext_datap = parmblkp;
2060
2061 /*******
2062 -- Set the called number plan to unknown
2063 -- Modified, as this causes issues on some switches
2064 -- - UNKNOWN works on 99% of E1 switches
2065 -- gclib_makecallp->destination.address_plan = GCADDRPLAN_ISDN;
2066 *******/
2067 gclib_makecallp->destination.address_plan = GCADDRPLAN_UNKNOWN;
2068
2069 /*******
2070 -- Set the address type to transparent
2071 -- Modified, as this causes issues on some switches
2072 -- - TRANSPARENT works on 99% of E1 switches
2073 -- gclib_makecallp->destination.address_type = GCADDRTYPE_NAT;
2074 *******/
2075 gclib_makecallp->destination.address_type = GCADDRTYPE_TRANSPARENT;
2076
2077 /*******
2078 -- Removed sub-addresses, as this causes issues on some switches
2079 -- strcpy(gclib_makecallp->destination.sub_address, "456");
2080 -- gclib_makecallp->destination.sub_address_plan = 0;
2081 -- gclib_makecallp->destination.sub_address_type = GCSUBADDR_OSI;
2082 *******/
2083 gclib_makecallp->destination.sub_address[0] = '\x0';
2084 gclib_makecallp->destination.sub_address_plan = 0;
2085 gclib_makecallp->destination.sub_address_type = 0;
2086
2087 /*******
2088 -- Note to customers: make sure that the Calling Address is set correctly, as some switches
2089 -- could reject the call if the calling address is not correct.
2090 *******/
2091 strcpy(gclib_makecallp->origination.address, "7654321");
2092
2093 /*******
2094 -- Set the calling number plan to ISDN type
2095 -- Modified, as this causes issues on some switches
2096 -- gclib_makecallp->origination.address_plan = GCADDRPLAN_ISDN;
2097 *******/
2098 gclib_makecallp->origination.address_plan = GCADDRPLAN_UNKNOWN;
2099
2100 /*******
2101 -- Set the address type
2102 -- Modified, as this causes issues on some switches
2103 -- gclib_makecallp->origination.address_type = GCADDRTYPE_NAT;
2104 *******/
2105 gclib_makecallp->origination.address_type = GCADDRTYPE_TRANSPARENT;
2106
2107 /*******
2108 -- Removed sub-addresses, as this causes issues on some switches
2109 -- strcpy(gclib_makecallp->origination.sub_address, "456");
2110 -- gclib_makecallp->origination.sub_address_plan = 0;
2111 -- gclib_makecallp->origination.sub_address_type = GCSUBADDR_OSI;
2112 *******/
2113 gclib_makecallp->origination.sub_address[0] = '\x0';
2114 gclib_makecallp->origination.sub_address_plan = 0;
2115 gclib_makecallp->origination.sub_address_type = 0;
2116
2117 gclib_makecallp->call_info.address_info = GCADDRINFO_ENBLOC;
2118
2119 /* Open the line device */
2120 if (gc_OpenEx(&port[index].ldev, port[index].devname, EV_SYNC, (void *)&port[index]) != GC_SUCCESS) {
2121 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Failed", port[index].devname);
2122 printandlog(index, GC_APIERR, NULL, str, 0);
2123 exitdemo(1);
2124 }
2125 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Success", port[index].devname);
2126 printandlog(index, GC_APICALL, NULL, str, 0);
2127
2128 /* Due to implementation, must wait until get GCEV_UNBLOCKED before issueing the reset linedevice */
2129 port[index].resetlinedev_required_after_unblocked = YES;
2130
2131 /* Enable alarm notification */
2132 enable_alarm_notification(&port[index]);
2133} /* End of Function */
2134
2135/****************************************************************
2136* NAME: gc_demo_open_analog_channel(index)
2137* DESCRIPTION: initialize a port used by an Analog protocol
2138* INPUT: index into port[] for this channel
2139* RETURNS: exits with exitdemo() if not successful
2140* CAUTIONS: none
2141****************************************************************/
2142static void gc_demo_open_analog_channel(int index)
2143{
2144 char str[MAX_STRING_SIZE], msgbuffer[MAX_STRING_SIZE];
2145
2146
2147 printandlog(index, MISC, NULL, "Analog device being opened", 0);
2148
2149 /* Initial channel state is blocked */
2150 port[index].blocked = YES;
2151 port[index].techtype = ANALOG;
2152 port[index].makecall_timeout = 60;
2153
2154 if (gc_OpenEx(&port[index].ldev, port[index].devname, EV_SYNC, (void *)&port[index]) != GC_SUCCESS) {
2155 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Failed", port[index].devname);
2156 printandlog(index, GC_APIERR, NULL, str, 0);
2157 exitdemo(1);
2158 }
2159
2160 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Success", port[index].devname);
2161 printandlog(index, GC_APICALL, NULL, str, 0);
2162
2163 if ((gc_LoadDxParm(port[index].ldev,"dxchan.vcp", msgbuffer, MAX_STRING_SIZE)) !=0) {
2164 sprintf(str, "gc_LoadDxParm(linedev=%ld) Failed)%s)", port[index].ldev, msgbuffer);
2165 printandlog(index, GC_APIERR, NULL, str, 0);
2166 exitdemo(1);
2167 }
2168 sprintf(str, "gc_LoadDxParm(linedev=%ld) Success)", port[index].ldev);
2169 printandlog(index, MISC, NULL, str, 0);
2170
2171 /* Some implementations do not reset (i.e. go on-hook) if line is off-hook at open time */
2172 if (gc_ResetLineDev(port[index].ldev, EV_ASYNC) != GC_SUCCESS) {
2173 sprintf(str, "gc_ResetLineDev(linedev=%ld, EV_ASYNC) Failed", port[index].ldev);
2174 printandlog(index, GC_APIERR, NULL, str, 0);
2175 exitdemo(1);
2176 }
2177
2178 port[index].resetlinedev_active = YES;
2179 sprintf(str, "gc_ResetLineDev(linedev=%ld, EV_ASYNC) Success", port[index].ldev);
2180 printandlog(index, GC_APICALL, NULL, str, 0);
2181} /* End of Function */
2182
2183/****************************************************************
2184* NAME: gc_demo_open_E1orT1_channel(index)
2185* DESCRIPTION: initialize a port used by an E1 or T1 protocol
2186* INPUT: index into port[] for this channel
2187* RETURNS: exits with exitdemo if not successful
2188* CAUTIONS: none
2189****************************************************************/
2190static void gc_demo_open_E1orT1_channel(int index)
2191{
2192 char str[MAX_STRING_SIZE];
2193
2194 printandlog(index, MISC, NULL, "E1 or T1 device being opened", 0);
2195
2196 /* Initial channel state is blocked */
2197 port[index].blocked = YES;
2198 port[index].techtype = E1ORT1_CAS;
2199 port[index].makecall_timeout = 60; /* Use 1 minute */
2200
2201 if (gc_OpenEx(&port[index].ldev, port[index].devname, EV_SYNC, (void *)&port[index]) != GC_SUCCESS) {
2202 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Failed", port[index].devname);
2203 printandlog(index, GC_APIERR, NULL, str, 0);
2204 exitdemo(1);
2205 }
2206
2207 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Success", port[index].devname);
2208 printandlog(index, GC_APICALL, NULL, str, 0);
2209
2210 set_calling_num(&port[index], "7654321");
2211
2212 /* Enable alarm notification */
2213 enable_alarm_notification(&port[index]);
2214} /* End of function */
2215
2216/****************************************************************
2217* NAME: gc_demo_open_H323_channel(index)
2218* DESCRIPTION: initialize a port used by H323 IP protocol
2219* INPUT: index into port[] for this channel
2220* RETURNS: exits with exitdemo if not successful
2221* CAUTIONS: none
2222****************************************************************/
2223static void gc_demo_open_H323_channel(int index)
2224{
2225 char str[MAX_STRING_SIZE];
2226
2227 printandlog(index, MISC, NULL, "H.323 device being opened", 0);
2228
2229 /* Initial channel state is blocked */
2230 port[index].blocked = YES;
2231 port[index].techtype = H323;
2232 port[index].makecall_timeout = 60; /* Use 1 minute */
2233
2234 if (gc_OpenEx(&port[index].ldev, port[index].devname, EV_ASYNC, (void *)&port[index]) != GC_SUCCESS) {
2235 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_ASYNC) Failed", port[index].devname);
2236 printandlog(index, GC_APIERR, NULL, str, 0);
2237 exitdemo(1);
2238 }
2239
2240 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_ASYNC) Success", port[index].devname);
2241 printandlog(index, GC_APICALL, NULL, str, 0);
2242
2243 /* Specify TX and RX coders for device */
2244 gc_set_channel_codecs(index);
2245
2246} /* End of function */
2247
2248/****************************************************************
2249* NAME: gc_demo_open_SIP_channel(index)
2250* DESCRIPTION: initialize a port used by SIP protocol
2251* INPUT: index into port[] for this channel
2252* RETURNS: exits with exitdemo if not successful
2253* CAUTIONS: none
2254****************************************************************/
2255static void gc_demo_open_SIP_channel(int index)
2256{
2257 char str[MAX_STRING_SIZE];
2258 GCLIB_MAKECALL_BLK *gclib_makecallp;
2259 struct hostent *phostent = NULL;
2260 struct in_addr addr;
2261
2262 printandlog(index, MISC, NULL, "SIP device being opened", 0);
2263
2264 /* Initial channel state is blocked */
2265 port[index].blocked = YES;
2266 port[index].techtype = SIP;
2267 port[index].makecall_timeout = 60; /* Use 1 minute */
2268
2269 /* Note: gclib_makecallp is not freed since a "permanent" data */
2270 /* structure. Assume program exit will free the resource */
2271 gclib_makecallp = (GCLIB_MAKECALL_BLKP)
2272 malloc(sizeof(GCLIB_MAKECALL_BLK) );
2273 if (gclib_makecallp == NULL) {
2274 printandlog(ALL_DEVICES, MISC, NULL, "Could not allocate memory\n", 0);
2275 exitdemo(1);
2276 }
2277
2278 port[index].makecallblk.cclib = NULL;
2279 port[index].makecallblk.gclib = gclib_makecallp;
2280 gclib_makecallp->ext_datap = NULL;
2281
2282 /* set GCLIB_ADDRESS_BLK with destination string & type*/
2283 strcpy(gclib_makecallp->destination.address,port[index].destination_num);
2284 /* set the called number plan to Transparent type */
2285 gclib_makecallp->destination.address_type = GCADDRTYPE_TRANSPARENT;
2286
2287
2288 /* set GCLIB_ADDRESS_BLK with local string & type*/
2289 /* retrieve local host name and address */
2290#ifdef IPV6_CC
2291 if (sip_address_type == USE_IPv6)
2292 {
2293 sprintf(gclib_makecallp->origination.address, "%s@%s", port[index].netdevice,
2294 localIPv6address);
2295 }
2296 else if (sip_address_type == USE_IPv4)
2297 {
2298 sprintf(gclib_makecallp->origination.address, "%s@%s", port[index].netdevice,
2299 localIPv4address);
2300 }
2301 else
2302#endif
2303 {
2304 /* set GCLIB_ADDRESS_BLK with local string & type*/
2305 /* retrieve local host name and address */
2306 if (gethostname(str, MAX_STRING_SIZE) != 0) {
2307 printandlog(index, GC_APIERR, NULL, "Failed to retrieve Host Name", 0);
2308 exitdemo(1);
2309 }
2310 if ((phostent = (struct hostent *)gethostbyname(str)) == 0) {
2311 printandlog(index, GC_APIERR, NULL, "Failed to retrieve Host IP address", 0);
2312 exitdemo(1);
2313 }
2314 memcpy(&addr, phostent->h_addr_list[0], sizeof(struct in_addr));
2315 sprintf(gclib_makecallp->origination.address, "%s_%s@%s", port[index].netdevice,
2316 str, inet_ntoa(addr));
2317 }
2318
2319 /* set the called number plan to Transparent type */
2320 gclib_makecallp->origination.address_type = GCADDRTYPE_TRANSPARENT;
2321
2322 if (gc_OpenEx(&port[index].ldev, port[index].devname, EV_ASYNC, (void *)&port[index]) != GC_SUCCESS) {
2323 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_ASYNC) Failed", port[index].devname);
2324 printandlog(index, GC_APIERR, NULL, str, 0);
2325 exitdemo(1);
2326 }
2327
2328 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_ASYNC) Success", port[index].devname);
2329 printandlog(index, GC_APICALL, NULL, str, 0);
2330} /* End of function */
2331
2332/****************************************************************
2333* NAME: gc_set_channel_codecs(index)
2334* DESCRIPTION: configure codecs for a VoIP (H.323 or SIP) channel
2335* INPUT: index into port[] for this channel
2336* RETURNS: exits with exitdemo if not successful
2337* CAUTIONS: none
2338****************************************************************/
2339static void gc_set_channel_codecs(index)
2340{
2341 int i, j, scope;
2342 IP_CAPABILITY ipcap;
2343 GC_PARM_BLK *parmblkp = NULL;
2344 char str[MAX_STRING_SIZE];
2345
2346
2347 printandlog(index, MISC, NULL, "Specifying codecs", 0);
2348
2349 for (i= 0; i< port[index].num_codecs; i++) {
2350
2351 memset(&ipcap, 0, sizeof(IP_CAPABILITY));
2352
2353 /* First obtain value of transmit codec type from translation table */
2354 for (j = 0; j< (sizeof(codecxlat)/sizeof(CODECXLAT)); j++) {
2355 if (!strcmp(port[index].codec[i].txcodec, codecxlat[j].codecname)) {
2356 ipcap.capability = codecxlat[j].codecvalue;
2357 break;
2358 }
2359 }
2360
2361 /* if not found, log error and exit */
2362 if (ipcap.capability == 0) {
2363 printf("Invalid codec type entry in %s\n", cfgfile);
2364 exitdemo(1);
2365 }
2366
2367 /* Specify TX codec, unless type is T.38 UDP which is bi-directional */
2368 /* and handled separately as data codec. */
2369 if (ipcap.capability != GCCAP_DATA_t38UDPFax) {
2370 ipcap.type = GCCAPTYPE_AUDIO;
2371 ipcap.direction = IP_CAP_DIR_LCLTRANSMIT;
2372 ipcap.extra.audio.frames_per_pkt = port[index].codec[i].txrate;
2373 /* Set VAD if and only if supported by codec type */
2374 if (codecxlat[j].isVADsupported == YES) {
2375 /* If codec supports VAD, use setting from config file. */
2376 /* Only check for "ON" for simplicity sake. */
2377 ipcap.extra.audio.VAD = (strcmp(port[index].codec[i].txVAD,"ON") == 0);
2378 } else { /* VAD not supported */
2379 ipcap.extra.audio.VAD = NO;
2380 }
2381
2382 /* append the GC_PARM_BLK with the respective TX codec */
2383 gc_util_insert_parm_ref(&parmblkp, GCSET_CHAN_CAPABILITY, IPPARM_LOCAL_CAPABILITY,
2384 sizeof(IP_CAPABILITY), &ipcap);
2385
2386 /* Re-initialize for determining receive codec */
2387 ipcap.capability = 0;
2388 /* Next obtain value of receive codec type from translation table */
2389 for (j = 0; j< (sizeof(codecxlat)/sizeof(CODECXLAT)); j++) {
2390 if (!strcmp(port[index].codec[i].rxcodec, codecxlat[j].codecname)) {
2391 ipcap.capability = codecxlat[j].codecvalue;
2392 break;
2393 }
2394 }
2395
2396 /* If not found, log error and exit */
2397 if (ipcap.capability == 0) {
2398 printf("Invalid codec type entry in %s\n", cfgfile);
2399 exitdemo(1);
2400 }
2401
2402 ipcap.direction = IP_CAP_DIR_LCLRECEIVE;
2403 ipcap.extra.audio.frames_per_pkt = port[index].codec[i].rxrate;
2404 /* Set VAD if and only if supported by codec type */
2405 if (codecxlat[j].isVADsupported) {
2406 /* string validation minimized for simplicity sake */
2407 ipcap.extra.audio.VAD = (strcmp(port[index].codec[i].rxVAD,"ON") == 0);
2408 } else { /* VAD not supported */
2409 ipcap.extra.audio.VAD = NO;
2410 }
2411
2412 /* append the GC_PARM_BLK with the respective RX codec */
2413 gc_util_insert_parm_ref(&parmblkp, GCSET_CHAN_CAPABILITY, IPPARM_LOCAL_CAPABILITY,
2414 sizeof(IP_CAPABILITY), &ipcap);
2415
2416
2417 } else { /* else codec is T.38 UDP which is bi-directional data codec */
2418 ipcap.type = GCCAPTYPE_RDATA;
2419 ipcap.direction = IP_CAP_DIR_LCLTXRX;
2420 ipcap.extra.data.max_bit_rate = 144;
2421
2422 /* append the GC_PARM_BLK with the T.38 codec */
2423 gc_util_insert_parm_ref(&parmblkp, GCSET_CHAN_CAPABILITY, IPPARM_LOCAL_CAPABILITY,
2424 sizeof(IP_CAPABILITY), &ipcap);
2425 }
2426 } /* end for loop for all codecs specified for device */
2427
2428 /* To interoperate with Microsoft Phoenix Version 5.0 Soft SIP phone,*/
2429 /* we will set coders on SIP calls on a per call basis. Again, this */
2430 /* is not required for other SIP clients. */
2431 if(port[index].techtype == H323) {
2432 scope = GC_ALLCALLS;
2433 } else { /* else SIP */
2434 scope = GC_SINGLECALL;
2435 }
2436
2437 if (gc_SetUserInfo(GCTGT_GCLIB_CHAN, port[index].ldev, parmblkp, scope) != GC_SUCCESS) {
2438 sprintf(str, "gc_SetUserInfo(linedev=%ld) Failed when specifying coders", port[index].ldev);
2439 printandlog(index, GC_APIERR, NULL, str, 0);
2440 exitdemo(1);
2441 }
2442
2443 gc_util_delete_parm_blk(parmblkp);
2444} /* End of function */
2445
2446/****************************************************************
2447* NAME: gc_demo_open_ss7_channel(index)
2448* DESCRIPTION: initialize a port used by an SS7 protocol
2449* INPUT: index into port[] for this channel
2450* RETURNS: exits with exitdemo if not successful
2451* CAUTIONS: none
2452****************************************************************/
2453static void gc_demo_open_ss7_channel(int index)
2454{
2455 char str[MAX_STRING_SIZE];
2456 char temp_devname[MAX_DEVNAME];
2457 printandlog(index, MISC, NULL, "SS7 device being opened", 0);
2458
2459 /* Initial channel state is blocked */
2460 port[index].blocked = YES;
2461 port[index].techtype = SS7;
2462 port[index].makecall_timeout = 60; /* timeout is supported, try 1 minute */
2463
2464 /*
2465 NOTE:
2466 The SS7 MakeCallBlock is not initialized currently, as Gc_MakeCallBlk->cclib is set to
2467 Null to use default values. For Live Testing, initialize the SS7MakeCallBlock and set
2468 its address to Gc_MakeCallBlk->cclib field.
2469 */
2470
2471 sprintf(temp_devname,":L_SS7%s", port[index].devname);
2472 if (gc_OpenEx(&port[index].ldev, temp_devname, EV_SYNC, (void *)&port[index]) != GC_SUCCESS) {
2473 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Failed", temp_devname);
2474 printandlog(index, GC_APIERR, NULL, str, 0);
2475 exitdemo(1);
2476 }
2477
2478 sprintf(str, "gc_OpenEx(devicename=%s, mode=EV_SYNC) Success", temp_devname);
2479 printandlog(index, GC_APICALL, NULL, str, 0);
2480
2481 set_calling_num(&port[index], "7654321");
2482
2483} /* End of function */
2484
2485/***************************************************************************
2486 * NAME: void set_calling_num(struct channel *pline, char *phone_num)
2487 *DESCRIPTION: Sets the calling number for the outbound direction
2488 * INPUT: pline - pointer to entry in port table
2489 * phone_num - pointer to the phone number
2490 * RETURNS: None.
2491 ***************************************************************************/
2492static void set_calling_num(struct channel *pline, char *phone_num)
2493{
2494 char str[MAX_STRING_SIZE];
2495
2496 if (pline->direction == DIR_OUT) {
2497 if (gc_SetCallingNum(pline->ldev, phone_num) < 0) {
2498 sprintf(str, "gc_SetCallingNum(linedev=%ld, phone_num = %s) Failed", pline->ldev, phone_num);
2499 printandlog(pline->index, GC_APIERR, NULL, str, 0);
2500 exitdemo(1);
2501 }
2502 sprintf(str, "gc_SetCallingNum(linedev=%ld, phone_num = %s) Success\n", pline->ldev, phone_num);
2503 printandlog(pline->index, MISC, NULL, str, 0);
2504 }
2505}
2506
2507
2508/***************************************************************************
2509 * NAME: void intr_hdlr()
2510 *DESCRIPTION: Handler called when one of the following signals is
2511 * received: SIGHUP, SIGINT, SIGQUIT, SIGTERM.
2512 * This function stops I/O activity on all channels and
2513 * closes all the channels.
2514 * INPUT: None
2515 * RETURNS: None.
2516 ***************************************************************************/
2517static void intr_hdlr(void)
2518{
2519 if (interrupted == NO) {
2520 printandlog(ALL_DEVICES, MISC, NULL, " *******Received User Interrupted Signal *******\n", 0);
2521
2522 interrupted = YES;
2523 }
2524 /* will handle graceful shutdown outside this handler in main's forever loop */
2525}
2526
2527/********************************************************************************
2528 * NAME: load_config_file()
2529 *DESCRIPTION: This function reads the configuration file line
2530 * by line. Ignores commented lines. For valid lines,
2531 * it fills the devname field of corresponding element
2532 * of port (global array of struct channel) as it is
2533 * to be passed as an argument to gc_OpenEx(). It also
2534 * fills the direction field in port[index].
2535 *
2536 * INPUT: None
2537 * RETURNS: None - exits if error
2538 * CAUTIONS: Assumes config file is in a "reasonable" format
2539 * Not all fields are case insensitive
2540 * The parsing in this procedure is simplified for demo purposes,
2541 * i.e. error handling is weak to non-existent.
2542 * Your application will need to implement parsing that is appropriate
2543 * for your application
2544 ********************************************************************************/
2545static void load_config_file(void)
2546{
2547 static char line[MAX_STRING_SIZE], direction[MAX_STRING_SIZE];
2548 static char netdevice[MAX_DEVNAME], protname[MAX_DEVNAME], voicedevice[MAX_DEVNAME];
2549 static char devname[MAX_DEVNAME], destination_num[MAX_ADDRESS_LEN];
2550 static char txcodec[MAX_CODECNAME], rxcodec[MAX_CODECNAME];
2551 static char txVAD[MAX_VADSTR], rxVAD[MAX_VADSTR];
2552 static char sip_address_type_str[10];
2553 static char sdp_address_type_str[10];
2554 int intercall_delay=0;
2555 int txrate = 0;
2556 int rxrate = 0;
2557 int index, num_params, num_codec;
2558 FILE *cfg_fp;
2559 fpos_t fpos;
2560
2561 /* Open Configuration file in Read mode */
2562 cfg_fp = fopen(cfgfile, "r");
2563
2564 if (cfg_fp == NULL) {
2565 printf("Unable to open %s for read\n", cfgfile);
2566 perror("Reason is: ");
2567 exit(1);
2568 }
2569
2570 while (fgets(line, MAX_STRING_SIZE, cfg_fp)) {
2571 /* strip trailing white space */
2572 index = strlen(line) - 1;
2573 while (index && (line[index] <= ' ')) {
2574 line[index] = '\0';
2575 index--;
2576 }
2577
2578 /* ignore blank lines */
2579 if (index == 0) {
2580 continue;
2581 }
2582
2583 /* if 1st character is not alpha numeric, then treat the line as a comment & ignore it */
2584 if ((line[0] < 'A') || (line[0] > 'z')) { /* simplifed non-alpha check */
2585 continue; /* Ignore the line and read next */
2586 }
2587 num_devices++; /* Increment Global Variable */
2588
2589 if(num_devices > MAXCHAN)
2590 {
2591 printf(" [ERROR]: Number of devices in cfg file exceeded maximum. Increase MAXCHAN and recompile\n");
2592 num_devices--;
2593 fclose (cfg_fp);
2594 exitdemo(1);
2595 }
2596
2597 /* Parse DTI Device, Protocol Name, Direction and Voice Device Name */
2598 num_params = sscanf(line, "%s%s%s%s%s%d%s%d%s%s%d%s%s%s",
2599 netdevice,
2600 protname,
2601 direction,
2602 voicedevice,
2603 destination_num,
2604 &intercall_delay,
2605 /* following coder parameters apply to VoIP devices only: */
2606 txcodec,
2607 &txrate,
2608 txVAD,
2609 rxcodec,
2610 &rxrate,
2611 rxVAD,
2612 sip_address_type_str,
2613 sdp_address_type_str);
2614
2615 if ((netdevice[0] == '\0') || (protname[0] == '\0') || (direction == '\0')) {
2616 /* Invalid entry in the configuration file */
2617 fclose (cfg_fp);
2618 printf("Invalid entry in %s\n", cfgfile);
2619 printf("Line is: %s\n", line);
2620 exitdemo(1);
2621 }
2622
2623 /* Fill devname to be passed as arg in gc_OpenEx()
2624 in port array (global) of struct channel */
2625
2626 if (strcmp(netdevice, "NONE") != 0) {
2627 sprintf(devname, ":N_%s:P_%s", netdevice, protname);
2628 } else {
2629 sprintf(devname, ":P_%s", protname);
2630 }
2631 /*Fill :V_ only if voice device name is not specified as NONE in cfg file */
2632 if (tolower(voicedevice[0]) != 'n') {
2633 /* IP uses :M instead of :V */
2634 if (voicedevice[0] != 'i') {
2635 strcat(devname, ":V_");
2636 } else {
2637 strcat(devname, ":M_");
2638 }
2639 strcat(devname, voicedevice);
2640 }
2641
2642 /* Fill Direction in port array (global) of struct channel */
2643 if (tolower(direction[0]) == 'i') {
2644 port[num_devices - 1].direction = DIR_IN;
2645 } else {
2646 port[num_devices - 1].direction = DIR_OUT;
2647 }
2648
2649 /* Fill device index in port array (global) of struct channel */
2650 port[num_devices - 1].index = num_devices - 1;
2651
2652 /* Copying all the local variable values to Global Variables */
2653 strcpy(port[num_devices - 1].devname, devname);
2654 strcpy(port[num_devices - 1].netdevice, netdevice);
2655 strcpy(port[num_devices - 1].voicedevice, voicedevice);
2656 strcpy(port[num_devices - 1].protname, protname);
2657 strcpy(port[num_devices - 1].destination_num, destination_num);
2658
2659 port[num_devices - 1].intercall_delay = intercall_delay;
2660
2661 /* For VoIP devices (H.323 or SIP) copy the codec parameters */
2662 if ((!strcmp(protname, "H323")) || (!strcmp(protname, "SIP"))) {
2663 if ((num_params == 12) || (num_params == 14)) {
2664 strcpy(port[num_devices - 1].codec[0].txcodec, txcodec);
2665 port[num_devices - 1].codec[0].txrate = txrate;
2666 strcpy(port[num_devices - 1].codec[0].txVAD, txVAD);
2667 strcpy(port[num_devices - 1].codec[0].rxcodec, rxcodec);
2668 port[num_devices - 1].codec[0].rxrate = rxrate;
2669 strcpy(port[num_devices - 1].codec[0].rxVAD, rxVAD);
2670#ifdef IPV6_CC
2671 if (num_params == 14)
2672 {
2673 /* Check for IPv6 loopback address - "::1" or "0::1" or "0:0:0:0:0:0:0:1" or "fe80::..." */
2674 if(strstr(destination_num, "@::1"))
2675 {
2676 printf("Invalid destination address \"::1\", please use \"0::1\"\n");
2677 exitdemo(1);
2678 }
2679 else if(strstr(destination_num, "@0::1") ||
2680 strstr(destination_num, "0:0:0:0:0:0:0:1"))
2681 {
2682 sprintf(localname, "0::1");
2683 useloopback = 1;
2684 }
2685 else if(strstr(destination_num, "@fe80::"))
2686 {
2687 islinklocal = 1;
2688 }
2689 /* Check for IPv4 loopback address */
2690 else if(strstr(destination_num, "127.0.0.1"))
2691 {
2692 sprintf(localname, "127.0.0.1");
2693 useloopback = 1;
2694 }
2695
2696 /* Set the SIP signaling address type */
2697 if(!stricmp(sip_address_type_str, "IPv4"))
2698 sip_address_type = USE_IPv4;
2699 else if(!stricmp(sip_address_type_str, "IPv6"))
2700 sip_address_type = USE_IPv6;
2701
2702 /* Set the SDP address type */
2703 if(!stricmp(sdp_address_type_str, "IPv4"))
2704 sdp_address_type = USE_IPv4;
2705 else if(!stricmp(sdp_address_type_str, "IPv6"))
2706 sdp_address_type = USE_IPv6;
2707 else if(!stricmp(sdp_address_type_str, "Prefer_IPv6"))
2708 sdp_address_type = PREFER_IPv6;
2709 }
2710#endif
2711
2712 /* Get current position in config file for possible rewinding later */
2713 fgetpos(cfg_fp, &fpos);
2714 num_codec = 1;
2715
2716 while (fgets(line, MAX_STRING_SIZE, cfg_fp)) {
2717 /* Strip trailing white space */
2718 index = strlen(line) - 1;
2719 while (index && (line[index] <= ' ')) {
2720 line[index] = '\0';
2721 index--;
2722 }
2723
2724 /* ignore comments or blank lines */
2725 if ((line[0] == '#') || (index == 0)) {
2726 continue; /* Ignore the line and read next */
2727 }
2728
2729 num_params = sscanf(line, "%s%d%s%s%d%s",
2730 txcodec,
2731 &txrate,
2732 txVAD,
2733 rxcodec,
2734 &rxrate,
2735 rxVAD);
2736
2737 if (num_params !=6) {
2738 break;
2739 }
2740
2741 num_codec++;
2742 if (num_codec > MAXCODECS) {
2743 printf("More than 10 codecs specified for device - please modify gc_basic_call_model.cfg to correct\n");
2744 exitdemo(1);
2745 }
2746
2747 strcpy(port[num_devices - 1].codec[num_codec - 1].txcodec, txcodec);
2748 port[num_devices - 1].codec[num_codec - 1].txrate = txrate;
2749 strcpy(port[num_devices - 1].codec[num_codec - 1].txVAD, txVAD);
2750 strcpy(port[num_devices - 1].codec[num_codec - 1].rxcodec, rxcodec);
2751 port[num_devices - 1].codec[num_codec - 1].rxrate = rxrate;
2752 strcpy(port[num_devices - 1].codec[num_codec - 1].rxVAD, rxVAD);
2753
2754 /* Get current position in config file for possible rewinding later */
2755 fgetpos(cfg_fp, &fpos);
2756 } /* end while there are multiple codecs for device to parse */
2757
2758 /* Reset the previous file position for other line device entries to process */
2759 fsetpos(cfg_fp, &fpos);
2760
2761 } else { /* Not all codec parameters were in config file */
2762 fclose (cfg_fp);
2763 printf("Invalid or missing codec parameter entry in %s\n", cfgfile);
2764 printf("Line is: %s\n", line);
2765 exitdemo(1);
2766 }
2767
2768 /* Store number of codecs specified for device */
2769 port[num_devices - 1].num_codecs = num_codec;
2770
2771 } /* end if VoIP device */
2772
2773 } /* End of read all lines loop */
2774
2775 fclose (cfg_fp);
2776} /* End of Function load_config_file() */
2777
2778
2779/********************************************************************************
2780 * NAME: printandlog ()
2781 * DESCRIPTION: prints on the screen and to the log file.
2782 * Put time stamp to the log file. If the
2783 * message to be logged is specific to a device,
2784 * it is logged to the device specific log file.
2785 *
2786 * INPUT: index is the device index
2787 * log_type valid values are ERR, MISC, EVENT, GC_APICALL, GC_APIERR and GC_RESULT_INFO
2788 * metaeventp = pointer to metaevent data structure - only used for GC_RESULT_INFO
2789 * msg_str message string to be printed
2790 * callindex is the CRN index - used for retrieving callstate.
2791 * RETURNS: NA
2792 ********************************************************************************/
2793static void printandlog(int index, int log_type, METAEVENT *metaeventp, char *msg_str, int callindex)
2794{
2795 struct tm *Time;
2796 time_t Timet;
2797 int i;
2798
2799#ifdef _WIN32 /* Defined in Windows */
2800 struct _timeb Timeb; /* used to get milliseconds */
2801#else /* Defined in Unix */
2802 struct timeb Timeb;
2803#endif
2804
2805 char TimeStr[MAX_STRING_SIZE]; /* will hold the time string when built */
2806 char buffer[MAX_STRING_SIZE]; /* will have everything but the time */
2807 char timestamp[MAX_STRING_SIZE]; /* holds the formatted time */
2808 char str[MAX_STRING_SIZE];
2809 char gc_apierr[MAX_STRING_SIZE]; /* iff have GC API error - most of time will be NULL */
2810 char gc_result_info[MAX_STRING_SIZE]; /* iff have GC result info request - most of time will be null */
2811
2812 /* initialize temporary buffers */
2813 buffer[0] = '\0';
2814 gc_apierr[0] = gc_result_info[0] = timestamp[0] = '\0';
2815
2816 /* Put time stamp - everything but the milliseconds */
2817 TimeStr[0] = '\0';
2818 time(&Timet);
2819 Time = localtime(&Timet);
2820
2821#ifdef _WIN32 /* Defined in Windows */
2822 _ftime(&Timeb); /* needed to get the milliseconds */
2823#else /* Defined in Unix */
2824 ftime(&Timeb);
2825#endif
2826
2827 if (log_type == MISC_WITH_NL) {
2828 strcpy(timestamp, "\n");
2829 }
2830
2831 sprintf(TimeStr, "%02d/%02d %02d:%02d:%02d.%03d ", Time->tm_mon+1, Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec, Timeb.millitm);
2832 strcat(timestamp, TimeStr);
2833
2834 switch(log_type)
2835 {
2836 case GC_APICALL:
2837 strcat(buffer, " [GC_APICALL]: ");
2838 break;
2839
2840 case GC_APIERR:
2841 strcat(buffer, " [GC_APIERR]: ");
2842 format_error_info(gc_apierr);
2843 break;
2844
2845 case GC_RESULT_INFO:
2846 strcat(buffer, " [GC_RESULT_INFO]: ");
2847 format_result_info(index, metaeventp, gc_result_info);
2848 break;
2849
2850 case NON_GC_APIERR:
2851 strcat(buffer, " [NON_GC_APIERR]: ");
2852 break;
2853
2854 case EVENT:
2855 strcat(buffer, " [EVENT]: ");
2856 break;
2857
2858 case MISC:
2859 strcat(buffer, " [MISC]: ");
2860 break;
2861
2862 case MISC_WITH_NL:
2863 strcat(buffer, " [MISC]: ");
2864 break;
2865
2866 case STATE:
2867 strcat(buffer, " [STATE]: ");
2868 append_call_state_name(buffer, index, callindex);
2869 break;
2870
2871 case TITLE:
2872 strcat(buffer, " ");
2873 break;
2874
2875 default:
2876 sprintf(str, " [UNEXPECTED LOG TYPE %d]: ", log_type);
2877 strcat(buffer, str);
2878 break;
2879 } /* End of Switch */
2880
2881 strcat(buffer, msg_str);
2882
2883
2884 /* now do the actual logging */
2885 /* 1st to screen */
2886 printf("%s\n", buffer);
2887
2888 /* print GC error or GC result information if required */
2889 if (gc_apierr[0]) {
2890 printf("%s\n", gc_apierr);
2891 }
2892 if (gc_result_info[0]) {
2893 printf("%s\n", gc_result_info);
2894 }
2895
2896 /* then to log file(s) */
2897 if (index == ALL_DEVICES) {
2898 for (i = 0; i < num_devices; i++) {
2899 if (port[i].log_fp) {
2900 fprintf(port[i].log_fp, "%s ", timestamp);
2901 fprintf(port[i].log_fp, "%s\n", buffer);
2902 }
2903 }
2904 } else if (port[index].log_fp) {
2905 fprintf(port[index].log_fp, "%s", timestamp);
2906 fprintf(port[index].log_fp, "%s\n", buffer);
2907
2908 /* print GC error or GC result information if required */
2909 if (gc_apierr[0]) {
2910 fprintf(port[index].log_fp, "%s\n", gc_apierr);
2911 }
2912 if (gc_result_info[0]) {
2913 fprintf(port[index].log_fp, "%s\n", gc_result_info);
2914 }
2915 }
2916}
2917
2918/********************************************************************************
2919 * NAME: format_error_info()
2920 *DESCRIPTION: formats error information using gc_ErrorInfo()
2921 * INPUT: buffer - buffer to format into
2922 * RETURNS: None
2923 * CAUTIONS: No integrity checking done on dest_buffer overflow
2924 ********************************************************************************/
2925static void format_error_info(char *dest_buffer)
2926{
2927 GC_INFO t_info; /* Structure which stores information about GC related errors */
2928
2929 gc_ErrorInfo(&t_info);
2930
2931 strcpy(dest_buffer, " GC ERROR: ");
2932 strcat(dest_buffer, t_info.gcMsg);
2933 strcat(dest_buffer, "\n");
2934
2935
2936 strcat(dest_buffer, " CC Name: ");
2937 strcat(dest_buffer, t_info.ccLibName);
2938 strcat(dest_buffer, "\n");
2939
2940
2941 strcat(dest_buffer, " CC ERROR: ");
2942 strcat(dest_buffer, t_info.ccMsg);
2943 strcat(dest_buffer, "\n");
2944
2945}
2946
2947/********************************************************************************
2948 * NAME: format_result_info()
2949 *DESCRIPTION: formats result information using gc_ResultInfo()
2950 * INPUT: index - index into port data structure
2951 * metaeventp - pointer to metaevent structure
2952 * buffer - buffer to format into
2953 * RETURNS: None
2954 * CAUTIONS: No integrity checking done on dest_buffer overflow
2955 ********************************************************************************/
2956static void format_result_info(int index, METAEVENT *metaeventp, char *dest_buffer)
2957{
2958 char str[MAX_STRING_SIZE];
2959 int rc;
2960 GC_INFO t_info; /* Structure which stores information about GC related errors */
2961
2962 rc = gc_ResultInfo(metaeventp, &t_info);
2963 if (rc < 0) {
2964 sprintf(str, "gc_ResultInfo() call failed");
2965 printandlog(index, GC_APIERR, NULL, str, 0);
2966 exitdemo(1);
2967 }
2968
2969 strcpy(dest_buffer, " GC Result: ");
2970 strcat(dest_buffer, t_info.gcMsg);
2971 strcat(dest_buffer, "\n");
2972
2973
2974 strcat(dest_buffer, " CC Name: ");
2975 strcat(dest_buffer, t_info.ccLibName);
2976 strcat(dest_buffer, "\n");
2977
2978
2979 strcat(dest_buffer, " CC Result: ");
2980 strcat(dest_buffer, t_info.ccMsg);
2981 strcat(dest_buffer, "\n");
2982}
2983
2984/********************************************************************************
2985 * NAME: append_call_state_name ()
2986 *DESCRIPTION: Appends the call state to a given buffer for a specified channel index
2987 * INPUT: buffer - buffer to append to
2988 * index - index into port structure (from 0)
2989 * RETURNS: None
2990 * CAUTIONS: No integrity checking done on buffer overflow
2991 ********************************************************************************/
2992static void append_call_state_name(char *buffer, int index, int callindex)
2993{
2994 struct channel *pline;
2995 char str[100];
2996
2997 if ((callindex < 0) || (callindex > MAX_CALLS)) {
2998 sprintf(str, "Invalid callindex(%d)", callindex);
2999 strcat(buffer, str);
3000 return;
3001 }
3002
3003 pline = &port[index];
3004 switch(pline->call[callindex].call_state)
3005 {
3006 case GCST_NULL:
3007 strcat(buffer, "GCST_NULL");
3008 break;
3009
3010 case GCST_IDLE:
3011 strcat(buffer, "GCST_IDLE");
3012 break;
3013
3014 case GCST_OFFERED:
3015 strcat(buffer, "GCST_OFFERED");
3016 break;
3017
3018 case GCST_ALERTING:
3019 strcat(buffer, "GCST_ALERTING");
3020 break;
3021
3022 case GCST_PROCEEDING:
3023 strcat(buffer, "GCST_PROCEEDING");
3024 break;
3025
3026 case GCST_ACCEPTED:
3027 strcat(buffer, "GCST_ACCEPTED");
3028 break;
3029
3030 case GCST_CONNECTED:
3031 strcat(buffer, "GCST_CONNECTED");
3032 break;
3033
3034 case GCST_DISCONNECTED:
3035 strcat(buffer, "GCST_DISCONNECTED");
3036 break;
3037
3038 default:
3039 sprintf(str, "UNKNOWN GC CALL STATE (%d)", pline->call[callindex].call_state);
3040 strcat(buffer, str);
3041 break;
3042 }
3043}
3044
3045/*******************************************************************************
3046 * NAME: existCRN()
3047 *DESCRIPTION: Search the CRN array for a valid CRN.
3048 * INPUT: CRN crn - CRN associated with the call.
3049 * Note: if crn == -1 then this means return the index of the 1st valid
3050 * crn in the call array.
3051 * struct channel *pline - pointer to data structure for the channel
3052 * RETURNS: if CRN exists, then return callindex; otherwise return OUT_OF_RANGE.
3053 ******************************************************************************/
3054static int existCRN(CRN crn, struct channel *pline)
3055{
3056 int callindex;
3057
3058 if (crn == -1) {
3059 /* For drop_outbound_calls_if_required() case */
3060 /* The function does not have access to metaevent structure. */
3061 for (callindex = 0; callindex < MAX_CALLS; callindex++) {
3062 /* if crn is found, then break */
3063 if (pline->call[callindex].crn > 0) {
3064 break;
3065 }
3066 }
3067 } else {
3068 /* All other cases. */
3069 for (callindex = 0; callindex < MAX_CALLS; callindex++) {
3070 if (pline->call[callindex].crn == crn) {
3071 /* crn is found, break out of for loop and continue. */
3072 break;
3073 }
3074 }
3075 }
3076
3077 /* If the callindex < MAX_CALLS, then return CRN. */
3078 /* Otherwise return (-1) indicating CRN was not found. */
3079 if (callindex < MAX_CALLS) {
3080 /* CRN found */
3081 return (callindex);
3082 }
3083 else {
3084 /* CRN not found */
3085 return (-1);
3086 }
3087}
3088
3089/********************************************************************************
3090 * NAME: initiate_exit()
3091 * DESCRIPTION: Initiate termination of active calls.
3092 * This will be done via gc_ResetLineDev()
3093 * which will clean up active calls
3094 * INPUT: none
3095 * RETURNS: none
3096 *
3097 *******************************************************************************/
3098static void initiate_exit()
3099{
3100 int index;
3101 char str[MAX_STRING_SIZE];
3102 struct channel *pline;
3103 static int exit_active = NO;
3104
3105 if (exit_active == YES) {
3106 return;
3107 }
3108 printandlog(ALL_DEVICES, MISC, NULL, "********** Exit initiated ***********\n", 0);
3109
3110 exit_active = YES;
3111 for(index = 0; index < num_devices; index++) {
3112 pline = &port[index];
3113 if (pline->ldev) {
3114 if (pline->resetlinedev_active == NO) {
3115 if (gc_ResetLineDev(port[index].ldev, EV_ASYNC) == GC_SUCCESS) {
3116 reset_linedevs_left_to_complete++;
3117 pline->resetlinedev_active = YES;
3118 sprintf(str, "gc_ResetLineDev(linedev=%ld, mode=EV_ASYNC) Success",
3119 port[index].ldev);
3120 printandlog(index, GC_APICALL, NULL, str, 0);
3121 } else {
3122 sprintf(str, "gc_ResetLineDev(linedev=%ld, mode=EV_ASYNC) failure ignored while exiting (reload boards after exit)",
3123 port[index].ldev);
3124 printandlog(index, GC_APIERR, NULL, str, 0);
3125 }
3126 } else {
3127 reset_linedevs_left_to_complete++; /* reset linedev already active */
3128 } /* just wait for it to complete */
3129 }
3130 }
3131 if (reset_linedevs_left_to_complete) {
3132 sprintf(str, "Please wait while %d gc_ResetLineDev()s are being done...",
3133 reset_linedevs_left_to_complete);
3134 printandlog(pline->index, MISC, NULL, str, 0);
3135 }
3136}
3137
3138/********************************************************************************
3139 * NAME: exitdemo()
3140 *DESCRIPTION: Closes all the logfiles,
3141 * Closes all the devices
3142 * Disables event handler
3143 * Stops GlobalCall
3144 * And Exits
3145 * INPUT: exitlevel 0 success, 1 some error
3146 * Exit from program with the exitlevel as specified by exitlevel
3147 * RETURNS: exits
3148 ********************************************************************************/
3149static void exitdemo(int exitlevel)
3150{
3151 int index;
3152 char str[MAX_STRING_SIZE];
3153 struct channel *pline;
3154 time_t finish_time;
3155 double duration, min_time = 0;
3156 static int recursive = 0; /* called recursively. e.g. Multiple ^c. If yes, skip clean-up logic and exit */
3157
3158 printandlog(ALL_DEVICES, MISC_WITH_NL, NULL, "********* Program Exiting *********", 0);
3159 if (recursive == 0) {
3160 recursive++;
3161 for (index = 0; index < num_devices; index++) {
3162 pline = &port[index];
3163
3164 /* The total calls run on Inbound & Outbound side */
3165 if (pline->direction == DIR_IN) {
3166 sprintf(str, "The total no of Inbound calls on this device is: %d", pline->numb_calls);
3167 printandlog(pline->index, MISC, NULL, str, 0);
3168 } else {
3169 sprintf(str, "The total no of Outbound calls on this device is: %d", pline->numb_calls);
3170 printandlog(pline->index, MISC, NULL, str, 0);
3171 }
3172 sprintf(str, "The total no of Inbound calls over all devices is: %d", in_calls);
3173 printandlog(pline->index, MISC, NULL, str, 0);
3174 sprintf(str, "The total no of Outbound calls over all devices is: %d", out_calls);
3175 printandlog(pline->index, MISC, NULL, str, 0);
3176
3177 /* Note down the finish time for the test */
3178 time(&finish_time);
3179 /* Calculate the duration taken by the test */
3180 duration = difftime(finish_time, start_time);
3181 min_time = duration / 60;
3182 sprintf(str, "The total duration taken by the test is: %5.2f minutes", min_time);
3183 printandlog(pline->index, MISC, NULL, str, 0);
3184 }
3185 }
3186
3187 /* code that can never be called recursively */
3188
3189 /* close all log files */
3190 for (index = 0; index < num_devices; index++) {
3191 pline = &port[index];
3192 if (pline->log_fp != NULL) {
3193 fclose(pline->log_fp);
3194 pline->log_fp = NULL;
3195 }
3196 }
3197 if (gc_started == YES) {
3198 for(index = 0; index < num_devices; index++) {
3199 pline = &port[index];
3200
3201 if (pline->ldev) {
3202 /* nothing can do about errors, so ignore them */
3203 gc_Close(pline->ldev);
3204 pline->ldev = 0;
3205 }
3206 /* if GC makecall block being used, then free it */
3207 if (port[index].makecallblk.gclib) {
3208 /* free the parm block if used using the utility functions */
3209 /* CAUTION: this must be done before freeing the GC makecall block */
3210 if (port[index].makecallblk.gclib->ext_datap) {
3211 gc_util_delete_parm_blk(port[index].makecallblk.gclib->ext_datap);
3212 }
3213
3214 /* now free the GC makecall block */
3215 free(port[index].makecallblk.gclib);
3216 port[index].makecallblk.gclib = NULL;
3217 }
3218 }
3219 gc_Stop();
3220 }
3221 exit(exitlevel);
3222} /* End of function exitdemo() */
3223/*****<< End of File >>*****/