1 /*
2  * Copyright (c) 2007-2014, Anthony Minessale II
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of the original author; nor the names of any contributors
17  * may be used to endorse or promote products derived from this software
18  * without specific prior written permission.
19  *
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
25  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 
35 #include "freetdm.h"
36 #include "ftdm_pika.h"
37 
38 
39 #define MAX_NUMBER_OF_TRUNKS 64
40 #define PIKA_BLOCK_SIZE 160
41 #define PIKA_BLOCK_LEN 20
42 #define PIKA_NUM_BUFFERS 8
43 #define TRY_OR_DIE(__code, __status, __label) if ((status = __code ) != __status) goto __label
44 #define pk_atof(__a) (PK_FLOAT) atof(__a)
45 
46 PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event);
47 
48 FTDM_ENUM_NAMES(PIKA_SPAN_NAMES, PIKA_SPAN_STRINGS)
49 PIKA_STR2ENUM(pika_str2span, pika_span2str, PIKA_TSpanFraming, PIKA_SPAN_NAMES, PIKA_SPAN_INVALID)
50 
51 FTDM_ENUM_NAMES(PIKA_SPAN_ENCODING_NAMES, PIKA_SPAN_ENCODING_STRINGS)
52 PIKA_STR2ENUM(pika_str2span_encoding, pika_span_encoding2str, PIKA_TSpanEncoding, PIKA_SPAN_ENCODING_NAMES, PIKA_SPAN_ENCODING_INVALID)
53 
54 FTDM_ENUM_NAMES(PIKA_LL_NAMES, PIKA_LL_STRINGS)
55 PIKA_STR2ENUM(pika_str2loop_length, pika_loop_length2str, PIKA_TSpanLoopLength, PIKA_LL_NAMES, PIKA_SPAN_LOOP_INVALID)
56 
57 FTDM_ENUM_NAMES(PIKA_LBO_NAMES, PIKA_LBO_STRINGS)
58 PIKA_STR2ENUM(pika_str2lbo, pika_lbo2str, PIKA_TSpanBuildOut, PIKA_LBO_NAMES, PIKA_SPAN_LBO_INVALID)
59 
60 FTDM_ENUM_NAMES(PIKA_SPAN_COMPAND_MODE_NAMES, PIKA_SPAN_COMPAND_MODE_STRINGS)
61 PIKA_STR2ENUM(pika_str2compand_mode, pika_compand_mode2str, PIKA_TSpanCompandMode, PIKA_SPAN_COMPAND_MODE_NAMES, PIKA_SPAN_COMPAND_MODE_INVALID)
62 
63 
64 typedef enum {
65 	PK_FLAG_READY = (1 << 0),
66 	PK_FLAG_LOCKED = (1 << 1)
67 } pk_flag_t;
68 
69 struct general_config {
70 	uint32_t region;
71 };
72 typedef struct general_config general_config_t;
73 
74 struct pika_channel_profile {
75 	char name[80];
76 	PKH_TRecordConfig record_config;
77 	PKH_TPlayConfig play_config;
78 	int ec_enabled;
79 	PKH_TECConfig ec_config;
80 	PKH_TSpanConfig span_config;
81 	general_config_t general_config;
82 	int cust_span;
83 };
84 typedef struct pika_channel_profile pika_channel_profile_t;
85 
86 static struct {
87 	PKH_TSystemDeviceList board_list;
88 	TPikaHandle open_boards[MAX_NUMBER_OF_TRUNKS];
89 	TPikaHandle system_handle;
90 	PKH_TSystemConfig system_config;
91 	PKH_TRecordConfig record_config;
92 	PKH_TPlayConfig play_config;
93 	PKH_TECConfig ec_config;
94 	PKH_TSpanConfig t1_span_config;
95 	PKH_TSpanConfig e1_span_config;
96 	ftdm_hash_t *profile_hash;
97 	general_config_t general_config;
98 } globals;
99 
100 
101 struct pika_span_data {
102 	TPikaHandle event_queue;
103 	PKH_TPikaEvent last_oob_event;
104 	uint32_t boardno;
105 	PKH_TSpanConfig span_config;
106 	TPikaHandle handle;
107 	uint32_t flags;
108 };
109 typedef struct pika_span_data pika_span_data_t;
110 
111 struct pika_chan_data {
112 	TPikaHandle handle;
113 	TPikaHandle media_in;
114 	TPikaHandle media_out;
115 	TPikaHandle media_in_queue;
116 	TPikaHandle media_out_queue;
117 	PKH_TPikaEvent last_media_event;
118 	PKH_TPikaEvent last_oob_event;
119 	PKH_TRecordConfig record_config;
120 	PKH_TPlayConfig play_config;
121 	int ec_enabled;
122 	PKH_TECConfig ec_config;
123 	PKH_THDLCConfig hdlc_config;
124 	ftdm_buffer_t *digit_buffer;
125 	ftdm_mutex_t *digit_mutex;
126 	ftdm_size_t dtmf_len;
127 	uint32_t flags;
128 	uint32_t hdlc_bytes;
129 };
130 typedef struct pika_chan_data pika_chan_data_t;
131 
pika_board_type_string(PK_UINT type)132 static const char *pika_board_type_string(PK_UINT type)
133 {
134 	if (type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) {
135 		return "digital_gateway";
136 	}
137 
138 	if (type == PKH_BOARD_TYPE_ANALOG_GATEWAY) {
139 		return "analog_gateway";
140 	}
141 
142 	return "unknown";
143 }
144 
145 /**
146  * \brief Process configuration variable for a pika profile
147  * \param category Pika profile name
148  * \param var Variable name
149  * \param val Variable value
150  * \param lineno Line number from configuration file (unused)
151  * \return Success
152  */
FIO_CONFIGURE_FUNCTION(pika_configure)153 static FIO_CONFIGURE_FUNCTION(pika_configure)
154 {
155 	pika_channel_profile_t *profile = NULL;
156 	int ok = 1;
157 
158 	if (!(profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) {
159 		profile = ftdm_malloc(sizeof(*profile));
160 		memset(profile, 0, sizeof(*profile));
161 		ftdm_set_string(profile->name, category);
162 		profile->ec_config = globals.ec_config;
163 		profile->record_config = globals.record_config;
164 		profile->play_config = globals.play_config;
165 		hashtable_insert(globals.profile_hash, (void *)profile->name, profile, HASHTABLE_FLAG_NONE);
166 		ftdm_log(FTDM_LOG_INFO, "creating profile [%s]\n", category);
167 	}
168 
169 	if (!strcasecmp(var, "rx-gain")) {
170 		profile->record_config.gain = pk_atof(val);
171 	} else if (!strcasecmp(var, "rx-agc-enabled")) {
172 		profile->record_config.AGC.enabled = ftdm_true(val);
173 	} else if (!strcasecmp(var, "rx-agc-targetPower")) {
174 		profile->record_config.AGC.targetPower = pk_atof(val);
175 	} else if (!strcasecmp(var, "rx-agc-minGain")) {
176 		profile->record_config.AGC.minGain = pk_atof(val);
177 	} else if (!strcasecmp(var, "rx-agc-maxGain")) {
178 		profile->record_config.AGC.maxGain = pk_atof(val);
179 	} else if (!strcasecmp(var, "rx-agc-attackRate")) {
180 		profile->record_config.AGC.attackRate = atoi(val);
181 	} else if (!strcasecmp(var, "rx-agc-decayRate")) {
182 		profile->record_config.AGC.decayRate = atoi(val);
183 	} else if (!strcasecmp(var, "rx-agc-speechThreshold")) {
184 		profile->record_config.AGC.speechThreshold = pk_atof(val);
185 	} else if (!strcasecmp(var, "rx-vad-enabled")) {
186 		profile->record_config.VAD.enabled = ftdm_true(val);
187 	} else if (!strcasecmp(var, "rx-vad-activationThreshold")) {
188 		profile->record_config.VAD.activationThreshold = pk_atof(val);
189 	} else if (!strcasecmp(var, "rx-vad-activationDebounceTime")) {
190 		profile->record_config.VAD.activationDebounceTime = atoi(val);
191 	} else if (!strcasecmp(var, "rx-vad-deactivationThreshold")) {
192 		profile->record_config.VAD.deactivationThreshold = pk_atof(val);
193 	} else if (!strcasecmp(var, "rx-vad-deactivationDebounceTime")) {
194 		profile->record_config.VAD.deactivationDebounceTime = atoi(val);
195 	} else if (!strcasecmp(var, "rx-vad-preSpeechBufferSize")) {
196 		profile->record_config.VAD.preSpeechBufferSize = atoi(val);
197 	} else if (!strcasecmp(var, "tx-gain")) {
198 		profile->play_config.gain = pk_atof(val);
199 	} else if (!strcasecmp(var, "tx-agc-enabled")) {
200 		profile->play_config.AGC.enabled = ftdm_true(val);
201 	} else if (!strcasecmp(var, "tx-agc-targetPower")) {
202 		profile->play_config.AGC.targetPower = pk_atof(val);
203 	} else if (!strcasecmp(var, "tx-agc-minGain")) {
204 		profile->play_config.AGC.minGain = pk_atof(val);
205 	} else if (!strcasecmp(var, "tx-agc-maxGain")) {
206 		profile->play_config.AGC.maxGain = pk_atof(val);
207 	} else if (!strcasecmp(var, "tx-agc-attackRate")) {
208 		profile->play_config.AGC.attackRate = atoi(val);
209 	} else if (!strcasecmp(var, "tx-agc-decayRate")) {
210 		profile->play_config.AGC.decayRate = atoi(val);
211 	} else if (!strcasecmp(var, "tx-agc-speechThreshold")) {
212 		profile->play_config.AGC.speechThreshold = pk_atof(val);
213 	} else if (!strcasecmp(var, "ec-enabled")) {
214 		profile->ec_enabled = ftdm_true(val);
215 	} else if (!strcasecmp(var, "ec-doubleTalkerThreshold")) {
216 		profile->ec_config.doubleTalkerThreshold = pk_atof(val);
217 	} else if (!strcasecmp(var, "ec-speechPresentThreshold")) {
218 		profile->ec_config.speechPresentThreshold = pk_atof(val);
219 	} else if (!strcasecmp(var, "ec-echoSuppressionThreshold")) {
220 		profile->ec_config.echoSuppressionThreshold = pk_atof(val);
221 	} else if (!strcasecmp(var, "ec-echoSuppressionEnabled")) {
222 		profile->ec_config.echoSuppressionEnabled = ftdm_true(val);
223 	} else if (!strcasecmp(var, "ec-comfortNoiseEnabled")) {
224 		profile->ec_config.comfortNoiseEnabled = ftdm_true(val);
225 	} else if (!strcasecmp(var, "ec-adaptationModeEnabled")) {
226 		profile->ec_config.adaptationModeEnabled = ftdm_true(val);
227 	} else if (!strcasecmp(var, "framing")) {
228 		profile->span_config.framing = pika_str2span(val);
229 		profile->cust_span++;
230 	} else if (!strcasecmp(var, "encoding")) {
231 		profile->span_config.encoding = pika_str2span_encoding(val);
232 		profile->cust_span++;
233 	} else if (!strcasecmp(var, "loopLength")) {
234 		profile->span_config.loopLength = pika_str2loop_length(val);
235 		profile->cust_span++;
236 	} else if (!strcasecmp(var, "buildOut")) {
237 		profile->span_config.buildOut = pika_str2lbo(val);
238 		profile->cust_span++;
239 	} else if (!strcasecmp(var, "compandMode")) {
240 		profile->span_config.compandMode = pika_str2compand_mode(val);
241 		profile->cust_span++;
242 	} else if (!strcasecmp(var, "region")) {
243 		if (!strcasecmp(val, "eu")) {
244 			profile->general_config.region = PKH_TRUNK_EU;
245 		} else {
246 			profile->general_config.region = PKH_TRUNK_NA;
247 		}
248 	} else {
249 		ok = 0;
250 	}
251 
252 	if (ok) {
253 		ftdm_log(FTDM_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category);
254 	} else {
255 		ftdm_log(FTDM_LOG_ERROR, "unknown param [%s]\n", var);
256 	}
257 
258 	return FTDM_SUCCESS;
259 }
260 
261 /**
262  * \brief Pika event handler
263  * \param event Pika event
264  */
media_out_callback(PKH_TPikaEvent * event)265 PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event)
266 {
267 	PK_STATUS pk_status;
268 	ftdm_channel_t *ftdmchan = event->userData;
269 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
270 
271 	//PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH];
272 	//PKH_EVENT_GetText(event->id, event_text, sizeof(event_text));
273 	//ftdm_log(FTDM_LOG_DEBUG, "Event: %s\n", event_text);
274 
275 	switch (event->id) {
276 	case PKH_EVENT_PLAY_IDLE:
277 		{
278 			while (ftdm_buffer_inuse(chan_data->digit_buffer)) {
279 				char dtmf[128] = "";
280 				ftdm_mutex_lock(chan_data->digit_mutex);
281 				chan_data->dtmf_len = ftdm_buffer_read(chan_data->digit_buffer, dtmf, sizeof(dtmf));
282 				pk_status = PKH_TG_PlayDTMF(chan_data->media_out, dtmf);
283 				ftdm_mutex_unlock(chan_data->digit_mutex);
284 			}
285 		}
286 		break;
287 	case PKH_EVENT_TG_TONE_PLAYED:
288 		{
289 
290 			if (!event->p1) {
291 				ftdm_mutex_lock(chan_data->digit_mutex);
292 				PKH_PLAY_Start(chan_data->media_out);
293 				chan_data->dtmf_len = 0;
294 				ftdm_mutex_unlock(chan_data->digit_mutex);
295 			}
296 
297 
298 		}
299 		break;
300 	default:
301 		break;
302 	}
303 
304 }
305 
306 /**
307  * \brief Initialises a range of pika channels
308  * \param span FreeTDM span
309  * \param boardno Pika board number
310  * \param spanno Pika span number
311  * \param start Initial pika channel number
312  * \param end Final pika channel number
313  * \param type FreeTDM channel type
314  * \param name FreeTDM span name
315  * \param number FreeTDM span number
316  * \param profile Pika channel profile
317  * \return number of spans configured
318  */
pika_open_range(ftdm_span_t * span,unsigned boardno,unsigned spanno,unsigned start,unsigned end,ftdm_chan_type_t type,char * name,char * number,pika_channel_profile_t * profile)319 static unsigned pika_open_range(ftdm_span_t *span, unsigned boardno, unsigned spanno, unsigned start, unsigned end,
320 								ftdm_chan_type_t type, char *name, char *number, pika_channel_profile_t *profile)
321 {
322 	unsigned configured = 0, x;
323 	PK_STATUS status;
324 	PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH];
325 	pika_span_data_t *span_data;
326 
327 	if (boardno >= globals.board_list.numberOfBoards) {
328 		ftdm_log(FTDM_LOG_ERROR, "Board %u is not present!\n", boardno);
329 		return 0;
330 	}
331 
332 	if (!globals.open_boards[boardno]) {
333 		status = PKH_BOARD_Open(globals.board_list.board[boardno].id,
334 								NULL,
335 								&globals.open_boards[boardno]);
336 		if(status != PK_SUCCESS) {
337 			ftdm_log(FTDM_LOG_ERROR, "Error: PKH_BOARD_Open %d failed(%s)!\n",  boardno,
338 					PKH_ERROR_GetText(status, error_text, sizeof(error_text)));
339 			return 0;
340 		}
341 
342 		ftdm_log(FTDM_LOG_DEBUG, "Open board %u\n",  boardno);
343 
344 		//PKH_BOARD_SetDebugTrace(globals.open_boards[boardno], 1, 0);
345 
346 	}
347 
348 	if (span->io_data) {
349 		span_data = span->io_data;
350 	} else {
351 		span_data = ftdm_malloc(sizeof(*span_data));
352 		assert(span_data != NULL);
353 		memset(span_data, 0, sizeof(*span_data));
354 		span_data->boardno = boardno;
355 
356 		status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &span_data->event_queue);
357 
358 		if (status != PK_SUCCESS) {
359 			ftdm_log(FTDM_LOG_ERROR, "Error: PKH_QUEUE_Create failed(%s)!\n",
360 					PKH_ERROR_GetText(status, error_text, sizeof(error_text)));
361 			ftdm_safe_free(span_data);
362 			return 0;
363 		}
364 
365 		//PKH_QUEUE_Attach(span_data->event_queue, globals.open_boards[boardno], NULL);
366 
367 		span->io_data = span_data;
368 	}
369 
370 	if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO) {
371 		start--;
372 		end--;
373 	}
374 
375 	for(x = start; x < end; x++) {
376 		ftdm_channel_t *chan;
377 		pika_chan_data_t *chan_data = NULL;
378 
379 		chan_data = ftdm_malloc(sizeof *chan_data);
380 		assert(chan_data);
381 		memset(chan_data, 0, sizeof(*chan_data));
382 		ftdm_span_add_channel(span, 0, type, &chan);
383 		chan->io_data = chan_data;
384 
385 		if ((type == FTDM_CHAN_TYPE_B || type == FTDM_CHAN_TYPE_DQ921) && !span_data->handle) {
386 			PKH_TBoardConfig boardConfig;
387 
388 			TRY_OR_DIE(PKH_BOARD_GetConfig(globals.open_boards[boardno], &boardConfig), PK_SUCCESS, error);
389 			if ((profile && profile->general_config.region == PKH_TRUNK_EU) || ftdm_test_flag(span_data, PK_FLAG_LOCKED)) {
390 				if (span->trunk_type == FTDM_TRUNK_T1) {
391 					ftdm_log(FTDM_LOG_WARNING, "Changing trunk type to E1 based on previous config.\n");
392 				}
393 				span->trunk_type = FTDM_TRUNK_E1;
394 			}
395 
396 			if (span->trunk_type == FTDM_TRUNK_T1) {
397 				if (ftdm_test_flag(span_data, PK_FLAG_LOCKED)) {
398 					ftdm_log(FTDM_LOG_WARNING, "Already locked into E1 mode!\n");
399 				}
400 			} else if (span->trunk_type == FTDM_TRUNK_E1) {
401 				boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_E1;
402 				if ((status = PKH_BOARD_SetConfig(globals.open_boards[boardno], &boardConfig)) != PK_SUCCESS) {
403 					ftdm_log(FTDM_LOG_ERROR, "Error: [%s]\n",
404 							PKH_ERROR_GetText(status, error_text, sizeof(error_text)));
405 				}
406 				ftdm_set_flag(span_data, PK_FLAG_LOCKED);
407 			}
408 
409 			TRY_OR_DIE(PKH_SPAN_Open(globals.open_boards[boardno], spanno, NULL, &span_data->handle), PK_SUCCESS, error);
410 			TRY_OR_DIE(PKH_SPAN_GetConfig(span_data->handle, &span_data->span_config), PK_SUCCESS, error);
411 			TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, span_data->handle, (PK_VOID*) span), PK_SUCCESS, error);
412 		}
413 
414 		if (type == FTDM_CHAN_TYPE_FXO) {
415 			PKH_TTrunkConfig trunkConfig;
416 
417 			TRY_OR_DIE(PKH_TRUNK_Open(globals.open_boards[boardno], x, &chan_data->handle), PK_SUCCESS, error);
418 			TRY_OR_DIE(PKH_TRUNK_Seize(chan_data->handle), PK_SUCCESS, error);
419 
420 			if (profile && profile->general_config.region == PKH_TRUNK_EU) {
421 				TRY_OR_DIE(PKH_TRUNK_GetConfig(chan_data->handle, &trunkConfig), PK_SUCCESS, error);
422 				trunkConfig.internationalControl = PKH_PHONE_INTERNATIONAL_CONTROL_EU;
423 				trunkConfig.audioFormat = PKH_AUDIO_ALAW;
424 				trunkConfig.compandMode = PKH_PHONE_AUDIO_ALAW;
425 				chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW;
426 				TRY_OR_DIE(PKH_TRUNK_SetConfig(chan_data->handle, &trunkConfig), PK_SUCCESS, error);
427 			} else {
428 				chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW;
429 			}
430 
431 
432 			TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error);
433 			TRY_OR_DIE(PKH_TRUNK_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error);
434 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error);
435 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error);
436 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error);
437 			TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error);
438 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error);
439 			TRY_OR_DIE(PKH_TRUNK_Start(chan_data->handle), PK_SUCCESS, error);
440 		} else if (type == FTDM_CHAN_TYPE_FXS) {
441 			PKH_TPhoneConfig phoneConfig;
442 
443 			if (profile && profile->general_config.region == PKH_TRUNK_EU) {
444 				TRY_OR_DIE(PKH_PHONE_GetConfig(chan_data->handle, &phoneConfig), PK_SUCCESS, error);
445 				phoneConfig.internationalControl = PKH_PHONE_INTERNATIONAL_CONTROL_EU;
446 				phoneConfig.compandMode = PKH_PHONE_AUDIO_ALAW;
447 				chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW;
448 				TRY_OR_DIE(PKH_PHONE_SetConfig(chan_data->handle, &phoneConfig), PK_SUCCESS, error);
449 			} else {
450 				chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW;
451 			}
452 
453 			TRY_OR_DIE(PKH_PHONE_Open(globals.open_boards[boardno], x, &chan_data->handle), PK_SUCCESS, error);
454 			TRY_OR_DIE(PKH_PHONE_Seize(chan_data->handle), PK_SUCCESS, error);
455 			TRY_OR_DIE(PKH_PHONE_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error);
456 			TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error);
457 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error);
458 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error);
459 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error);
460 			TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error);
461 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error);
462 			TRY_OR_DIE(PKH_PHONE_Start(chan_data->handle), PK_SUCCESS, error);
463 		} else if (type == FTDM_CHAN_TYPE_B) {
464 			TRY_OR_DIE(PKH_SPAN_SeizeChannel(span_data->handle, x), PK_SUCCESS, error);
465 			TRY_OR_DIE(PKH_SPAN_GetMediaStreams(span_data->handle, x, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error);
466 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error);
467 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error);
468 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error);
469 			TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error);
470 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error);
471 		} else if (type == FTDM_CHAN_TYPE_DQ921) {
472 			TRY_OR_DIE(PKH_SPAN_HDLC_Open(span_data->handle, PKH_SPAN_HDLC_MODE_NORMAL, &chan_data->handle), PK_SUCCESS, error);
473 			TRY_OR_DIE(PKH_SPAN_HDLC_GetConfig(chan_data->handle, &chan_data->hdlc_config), PK_SUCCESS, error);
474 			chan_data->hdlc_config.channelId = x;
475 			TRY_OR_DIE(PKH_SPAN_HDLC_SetConfig(chan_data->handle, &chan_data->hdlc_config), PK_SUCCESS, error);
476 			TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error);
477 			TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error);
478 			TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error);
479 
480 			if (profile) {
481 				if (profile->cust_span) {
482 					span_data->span_config.framing = profile->span_config.framing;
483 					span_data->span_config.encoding = profile->span_config.encoding;
484 					span_data->span_config.loopLength = profile->span_config.loopLength;
485 					span_data->span_config.buildOut = profile->span_config.buildOut;
486 					span_data->span_config.compandMode = profile->span_config.compandMode;
487 				} else {
488 					if (profile->general_config.region == PKH_TRUNK_EU) {
489 						span_data->span_config = globals.e1_span_config;
490 					} else {
491 						span_data->span_config = globals.t1_span_config;
492 					}
493 				}
494 			} else {
495 				if (span->trunk_type == FTDM_TRUNK_E1) {
496 					span_data->span_config = globals.e1_span_config;
497 				} else {
498 					span_data->span_config = globals.t1_span_config;
499 				}
500 			}
501 
502 			PKH_SPAN_SetConfig(span_data->handle, &span_data->span_config);
503 			TRY_OR_DIE(PKH_SPAN_Start(span_data->handle), PK_SUCCESS, error);
504 		}
505 
506 		goto ok;
507 
508 	error:
509 		PKH_ERROR_GetText(status, error_text, sizeof(error_text));
510 		ftdm_log(FTDM_LOG_ERROR, "failure configuring device b%ds%dc%d [%s]\n", boardno, spanno, x, error_text);
511 		continue;
512 	ok:
513 		ftdm_set_flag(chan_data, PK_FLAG_READY);
514 		status = PKH_RECORD_GetConfig(chan_data->media_in, &chan_data->record_config);
515 		chan_data->record_config.encoding = PKH_RECORD_ENCODING_MU_LAW;
516 		chan_data->record_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ;
517 		chan_data->record_config.bufferSize = PIKA_BLOCK_SIZE;
518 		chan_data->record_config.numberOfBuffers = PIKA_NUM_BUFFERS;
519 		chan_data->record_config.VAD.enabled = PK_FALSE;
520 		//chan_data->record_config.speechSegmentEventsEnabled = PK_FALSE;
521 		//chan_data->record_config.gain = rxgain;
522 
523 		status = PKH_PLAY_GetConfig(chan_data->media_out, &chan_data->play_config);
524 		chan_data->play_config.encoding = PKH_RECORD_ENCODING_MU_LAW;
525 		chan_data->play_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ;
526 		chan_data->play_config.AGC.enabled = PK_FALSE;
527 		ftdm_log(FTDM_LOG_INFO, "configuring device b%ds%dc%d as FreeTDM device %d:%d\n", boardno, spanno, x, chan->span_id, chan->chan_id);
528 
529 		if (profile) {
530 			ftdm_log(FTDM_LOG_INFO, "applying config profile %s to device %d:%d\n", profile->name, chan->span_id, chan->chan_id);
531 			chan_data->record_config.gain = profile->record_config.gain;
532 			chan_data->record_config.AGC = profile->record_config.AGC;
533 			chan_data->record_config.VAD = profile->record_config.VAD;
534 			chan_data->play_config.gain = profile->play_config.gain;
535 			chan_data->play_config.AGC = profile->play_config.AGC;
536 			chan_data->ec_enabled = profile->ec_enabled;
537 			chan_data->ec_config = profile->ec_config;
538 		}
539 
540 		if (type == FTDM_CHAN_TYPE_B) {
541 			if (span_data->span_config.compandMode == PKH_SPAN_COMPAND_MODE_A_LAW) {
542 				chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW;
543 			} else {
544 				chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW;
545 			}
546 		}
547 
548 		status = PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config);
549 		status = PKH_PLAY_SetConfig(chan_data->media_out, &chan_data->play_config);
550 
551 		chan->physical_span_id = spanno;
552 		chan->physical_chan_id = x;
553 
554 		chan->rate = 8000;
555 		chan->packet_len = (uint32_t)chan_data->record_config.bufferSize;
556 		chan->effective_interval = chan->native_interval = chan->packet_len / 8;
557 
558 		PKH_RECORD_Start(chan_data->media_in);
559 		PKH_PLAY_Start(chan_data->media_out);
560 		if (chan_data->ec_enabled) {
561 			PKH_EC_SetConfig(chan_data->media_in, &chan_data->ec_config);
562 			PKH_EC_Start(chan_data->media_in, chan_data->media_in, chan_data->media_out);
563 		}
564 
565 		if (!ftdm_strlen_zero(name)) {
566 			ftdm_copy_string(chan->chan_name, name, sizeof(chan->chan_name));
567 		}
568 
569 		if (!ftdm_strlen_zero(number)) {
570 			ftdm_copy_string(chan->chan_number, number, sizeof(chan->chan_number));
571 		}
572 
573 		ftdm_channel_set_feature(chan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE);
574 		ftdm_buffer_create(&chan_data->digit_buffer, 128, 128, 0);
575 		ftdm_mutex_create(&chan_data->digit_mutex);
576 
577 		configured++;
578 	}
579 
580 
581 	return configured;
582 }
583 
584 /**
585  * \brief Initialises an freetdm pika span from a configuration string
586  * \param span FreeTDM span
587  * \param str Configuration string
588  * \param type FreeTDM span type
589  * \param name FreeTDM span name
590  * \param number FreeTDM span number
591  * \return Success or failure
592  */
FIO_CONFIGURE_SPAN_FUNCTION(pika_configure_span)593 static FIO_CONFIGURE_SPAN_FUNCTION(pika_configure_span)
594 {
595 	int items, i;
596     char *mydata, *item_list[10];
597 	char *bd, *sp, *ch = NULL, *mx;
598 	int boardno;
599 	int channo;
600 	int spanno;
601 	int top = 0;
602 	unsigned configured = 0;
603 	char *profile_name = NULL;
604 	pika_channel_profile_t *profile = NULL;
605 
606 	assert(str != NULL);
607 
608 	mydata = ftdm_strdup(str);
609 	assert(mydata != NULL);
610 
611 	if ((profile_name = strchr(mydata, '@'))) {
612 		*profile_name++ = '\0';
613 		if (!ftdm_strlen_zero(profile_name)) {
614 			profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)profile_name);
615 		}
616 	}
617 
618 	items = ftdm_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0])));
619 
620 	for(i = 0; i < items; i++) {
621 		bd = item_list[i];
622 		if ((sp = strchr(bd, ':'))) {
623 			*sp++ = '\0';
624 			if ((ch = strchr(sp, ':'))) {
625 				*ch++ = '\0';
626 			}
627 		}
628 
629 		if (!(bd && sp && ch)) {
630 			ftdm_log(FTDM_LOG_ERROR, "Invalid input\n");
631 			continue;
632 		}
633 
634 		boardno = atoi(bd);
635 		channo = atoi(ch);
636 		spanno = atoi(sp);
637 
638 
639 		if (boardno < 0) {
640 			ftdm_log(FTDM_LOG_ERROR, "Invalid board number %d\n", boardno);
641 			continue;
642 		}
643 
644 		if (channo < 0) {
645 			ftdm_log(FTDM_LOG_ERROR, "Invalid channel number %d\n", channo);
646 			continue;
647 		}
648 
649 		if (spanno < 0) {
650 			ftdm_log(FTDM_LOG_ERROR, "Invalid span number %d\n", channo);
651 			continue;
652 		}
653 
654 		if ((mx = strchr(ch, '-'))) {
655 			mx++;
656 			top = atoi(mx) + 1;
657 		} else {
658 			top = channo + 1;
659 		}
660 
661 
662 		if (top < 0) {
663 			ftdm_log(FTDM_LOG_ERROR, "Invalid range number %d\n", top);
664 			continue;
665 		}
666 
667 		configured += pika_open_range(span, boardno, spanno, channo, top, type, name, number, profile);
668 
669 	}
670 
671 	ftdm_safe_free(mydata);
672 
673 	return configured;
674 }
675 
676 /**
677  * \brief Opens Pika channel
678  * \param ftdmchan Channel to open
679  * \return Success or failure
680  */
FIO_OPEN_FUNCTION(pika_open)681 static FIO_OPEN_FUNCTION(pika_open)
682 {
683 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
684 
685 	if (!chan_data && !ftdm_test_flag(chan_data, PK_FLAG_READY)) {
686 		return FTDM_FAIL;
687 	}
688 
689 	if (chan_data->media_in_queue) {
690 		PKH_QUEUE_Flush(chan_data->media_in_queue);
691 	}
692 
693 	if (ftdmchan->type == FTDM_CHAN_TYPE_FXS || ftdmchan->type == FTDM_CHAN_TYPE_FXO || ftdmchan->type == FTDM_CHAN_TYPE_B) {
694 		PKH_PLAY_Start(chan_data->media_out);
695 	}
696 	return FTDM_SUCCESS;
697 }
698 
699 /**
700  * \brief Closes Pika channel
701  * \param ftdmchan Channel to close
702  * \return Success
703  */
FIO_CLOSE_FUNCTION(pika_close)704 static FIO_CLOSE_FUNCTION(pika_close)
705 {
706 	return FTDM_SUCCESS;
707 }
708 
709 /**
710  * \brief Waits for an event on a Pika channel
711  * \param ftdmchan Channel to open
712  * \param flags Type of event to wait for
713  * \param to Time to wait (in ms)
714  * \return Success, failure or timeout
715  */
FIO_WAIT_FUNCTION(pika_wait)716 static FIO_WAIT_FUNCTION(pika_wait)
717 {
718 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
719 	PK_STATUS status;
720 	ftdm_wait_flag_t myflags = *flags;
721 	PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH];
722 
723 	*flags = FTDM_NO_FLAGS;
724 
725 	if (myflags & FTDM_READ) {
726 		if (chan_data->hdlc_bytes) {
727 			*flags |= FTDM_READ;
728 			return FTDM_SUCCESS;
729 		}
730 		status = PKH_QUEUE_WaitOnEvent(chan_data->media_in_queue, to, &chan_data->last_media_event);
731 
732 		if (status == PK_SUCCESS) {
733 			if (chan_data->last_media_event.id == PKH_EVENT_QUEUE_TIMEOUT || chan_data->last_media_event.id == PKH_EVENT_RECORD_BUFFER_OVERFLOW) {
734 				return FTDM_TIMEOUT;
735 			}
736 
737 			*flags |= FTDM_READ;
738 			return FTDM_SUCCESS;
739 		}
740 
741 		PKH_EVENT_GetText(chan_data->last_media_event.id, event_text, sizeof(event_text));
742 		ftdm_log(FTDM_LOG_DEBUG, "Event: %s\n", event_text);
743 	}
744 
745 	return FTDM_SUCCESS;
746 }
747 
748 /**
749  * \brief Reads data from a Pika channel
750  * \param ftdmchan Channel to read from
751  * \param data Data buffer
752  * \param datalen Size of data buffer
753  * \return Success or failure
754  */
FIO_READ_FUNCTION(pika_read)755 static FIO_READ_FUNCTION(pika_read)
756 {
757 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
758 	PK_STATUS status;
759 	PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH];
760 	uint32_t len;
761 
762 	if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
763 		if ((status = PKH_SPAN_HDLC_GetMessage(chan_data->handle, data, *datalen)) == PK_SUCCESS) {
764 			*datalen = chan_data->hdlc_bytes;
765 			chan_data->hdlc_bytes = 0;
766 			return FTDM_SUCCESS;
767 		}
768 		return FTDM_FAIL;
769 	}
770 
771 	if (!(len = chan_data->last_media_event.p0)) {
772 		len = ftdmchan->packet_len;
773 	}
774 
775 	if (len < *datalen) {
776 		*datalen = len;
777 	}
778 
779 	if ((status = PKH_RECORD_GetData(chan_data->media_in, data, *datalen)) == PK_SUCCESS) {
780 		return FTDM_SUCCESS;
781 	}
782 
783 
784 	PKH_ERROR_GetText(status, event_text, sizeof(event_text));
785 	ftdm_log(FTDM_LOG_DEBUG, "ERR: %s\n", event_text);
786 	return FTDM_FAIL;
787 }
788 
789 /**
790  * \brief Writes data to a Pika channel
791  * \param ftdmchan Channel to write to
792  * \param data Data buffer
793  * \param datalen Size of data buffer
794  * \return Success or failure
795  */
FIO_WRITE_FUNCTION(pika_write)796 static FIO_WRITE_FUNCTION(pika_write)
797 {
798 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
799 	PK_STATUS status;
800 
801 	if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
802 		if ((status = PKH_SPAN_HDLC_SendMessage(chan_data->handle, data, *datalen)) == PK_SUCCESS) {
803 			return FTDM_SUCCESS;
804 		}
805 		return FTDM_FAIL;
806 	}
807 
808 	if (PKH_PLAY_AddData(chan_data->media_out, 0, data, *datalen) == PK_SUCCESS) {
809 		return FTDM_SUCCESS;
810 	}
811 
812 	return FTDM_FAIL;
813 }
814 
815 /**
816  * \brief Executes an FreeTDM command on a Pika channel
817  * \param ftdmchan Channel to execute command on
818  * \param command FreeTDM command to execute
819  * \param obj Object (unused)
820  * \return Success or failure
821  */
FIO_COMMAND_FUNCTION(pika_command)822 static FIO_COMMAND_FUNCTION(pika_command)
823 {
824 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
825 	//pika_span_data_t *span_data = (pika_span_data_t *) ftdmchan->span->io_data;
826 	PK_STATUS pk_status;
827 	ftdm_status_t status = FTDM_SUCCESS;
828 
829 	switch(command) {
830 	case FTDM_COMMAND_OFFHOOK:
831 		{
832 			if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_OFFHOOK)) != PK_SUCCESS) {
833 				PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error));
834 				GOTO_STATUS(done, FTDM_FAIL);
835 			} else {
836 				ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
837 			}
838 		}
839 		break;
840 	case FTDM_COMMAND_ONHOOK:
841 		{
842 			if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_ONHOOK)) != PK_SUCCESS) {
843 				PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error));
844 				GOTO_STATUS(done, FTDM_FAIL);
845 			} else {
846 				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
847 			}
848 		}
849 		break;
850 	case FTDM_COMMAND_GENERATE_RING_ON:
851 		{
852 			if ((pk_status = PKH_PHONE_RingStart(chan_data->handle, 0, 0)) != PK_SUCCESS) {
853 				PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error));
854 				GOTO_STATUS(done, FTDM_FAIL);
855 			} else {
856 				ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING);
857 			}
858 		}
859 		break;
860 	case FTDM_COMMAND_GENERATE_RING_OFF:
861 		{
862 			if ((pk_status = PKH_PHONE_RingStop(chan_data->handle)) != PK_SUCCESS) {
863 				PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error));
864 				GOTO_STATUS(done, FTDM_FAIL);
865 			} else {
866 				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING);
867 			}
868 		}
869 		break;
870 	case FTDM_COMMAND_GET_INTERVAL:
871 		{
872 
873 			FTDM_COMMAND_OBJ_INT = ftdmchan->native_interval;
874 
875 		}
876 		break;
877 	case FTDM_COMMAND_SET_INTERVAL:
878 		{
879 			int interval = FTDM_COMMAND_OBJ_INT;
880 			int len = interval * 8;
881 			chan_data->record_config.bufferSize = len;
882 			chan_data->record_config.numberOfBuffers = (PK_UINT)chan_data->record_config.bufferSize;
883 			ftdmchan->packet_len = (uint32_t)chan_data->record_config.bufferSize;
884 			ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8;
885 			PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config);
886 			GOTO_STATUS(done, FTDM_SUCCESS);
887 		}
888 		break;
889 	case FTDM_COMMAND_GET_DTMF_ON_PERIOD:
890 		{
891 
892 			FTDM_COMMAND_OBJ_INT = ftdmchan->dtmf_on;
893 			GOTO_STATUS(done, FTDM_SUCCESS);
894 
895 		}
896 		break;
897 	case FTDM_COMMAND_GET_DTMF_OFF_PERIOD:
898 		{
899 				FTDM_COMMAND_OBJ_INT = ftdmchan->dtmf_on;
900 				GOTO_STATUS(done, FTDM_SUCCESS);
901 		}
902 		break;
903 	case FTDM_COMMAND_SET_DTMF_ON_PERIOD:
904 		{
905 			int val = FTDM_COMMAND_OBJ_INT;
906 			if (val > 10 && val < 1000) {
907 				ftdmchan->dtmf_on = val;
908 				GOTO_STATUS(done, FTDM_SUCCESS);
909 			} else {
910 				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid value %d range 10-1000", val);
911 				GOTO_STATUS(done, FTDM_FAIL);
912 			}
913 		}
914 		break;
915 	case FTDM_COMMAND_SET_DTMF_OFF_PERIOD:
916 		{
917 			int val = FTDM_COMMAND_OBJ_INT;
918 			if (val > 10 && val < 1000) {
919 				ftdmchan->dtmf_off = val;
920 				GOTO_STATUS(done, FTDM_SUCCESS);
921 			} else {
922 				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid value %d range 10-1000", val);
923 				GOTO_STATUS(done, FTDM_FAIL);
924 			}
925 		}
926 		break;
927 	case FTDM_COMMAND_SEND_DTMF:
928 		{
929 			char *digits = FTDM_COMMAND_OBJ_CHAR_P;
930 			ftdm_log(FTDM_LOG_DEBUG, "Adding DTMF SEQ [%s]\n", digits);
931 			ftdm_mutex_lock(chan_data->digit_mutex);
932 			ftdm_buffer_write(chan_data->digit_buffer, digits, strlen(digits));
933 			ftdm_mutex_unlock(chan_data->digit_mutex);
934 			pk_status = PKH_PLAY_Stop(chan_data->media_out);
935 
936 			if (pk_status != PK_SUCCESS) {
937 				PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error));
938 				GOTO_STATUS(done, FTDM_FAIL);
939 			}
940 			GOTO_STATUS(done, FTDM_SUCCESS);
941 		}
942 		break;
943 	default:
944 		break;
945 	};
946 
947  done:
948 	return status;
949 }
950 
951 /**
952  * \brief Checks for events on a Pika span
953  * \param span Span to check for events
954  * \param ms Time to wait for event
955  * \return Success if event is waiting or failure if not
956  */
FIO_SPAN_POLL_EVENT_FUNCTION(pika_poll_event)957 static FIO_SPAN_POLL_EVENT_FUNCTION(pika_poll_event)
958 {
959 	pika_span_data_t *span_data = (pika_span_data_t *) span->io_data;
960 	PK_STATUS status;
961 	PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH];
962 
963 	status = PKH_QUEUE_WaitOnEvent(span_data->event_queue, ms, &span_data->last_oob_event);
964 
965 	if (status == PK_SUCCESS) {
966 		ftdm_channel_t *ftdmchan = NULL;
967 		uint32_t *data = (uint32_t *) span_data->last_oob_event.userData;
968 		ftdm_data_type_t data_type = FTDM_TYPE_NONE;
969 
970 		if (span_data->last_oob_event.id == PKH_EVENT_QUEUE_TIMEOUT) {
971 			return FTDM_TIMEOUT;
972 		}
973 
974 		if (data) {
975 			data_type = *data;
976 		}
977 
978 		if (data_type == FTDM_TYPE_CHANNEL) {
979 			ftdmchan = span_data->last_oob_event.userData;
980 		} else if (data_type == FTDM_TYPE_SPAN) {
981             ftdm_time_t last_event_time = ftdm_current_time_in_ms();
982 			uint32_t event_id = 0;
983 
984 			switch (span_data->last_oob_event.id) {
985 			case PKH_EVENT_SPAN_ALARM_T1_RED:
986 			case PKH_EVENT_SPAN_ALARM_T1_YELLOW:
987 			case PKH_EVENT_SPAN_ALARM_T1_AIS:
988 			case PKH_EVENT_SPAN_ALARM_E1_RED:
989 			case PKH_EVENT_SPAN_ALARM_E1_RAI:
990 			case PKH_EVENT_SPAN_ALARM_E1_AIS:
991 			case PKH_EVENT_SPAN_ALARM_E1_RMAI:
992 			case PKH_EVENT_SPAN_ALARM_E1_TS16AIS:
993 			case PKH_EVENT_SPAN_ALARM_E1_TS16LOS:
994 			case PKH_EVENT_SPAN_OUT_OF_SYNC:
995 			case PKH_EVENT_SPAN_FRAMING_ERROR:
996 			case PKH_EVENT_SPAN_LOSS_OF_SIGNAL:
997 			case PKH_EVENT_SPAN_OUT_OF_CRC_MF_SYNC:
998 			case PKH_EVENT_SPAN_OUT_OF_CAS_MF_SYNC:
999 				event_id = FTDM_OOB_ALARM_TRAP;
1000 				break;
1001 			case PKH_EVENT_SPAN_ALARM_T1_RED_CLEAR:
1002 			case PKH_EVENT_SPAN_ALARM_T1_YELLOW_CLEAR:
1003 			case PKH_EVENT_SPAN_ALARM_T1_AIS_CLEAR:
1004 			case PKH_EVENT_SPAN_ALARM_E1_RED_CLEAR:
1005 			case PKH_EVENT_SPAN_ALARM_E1_RAI_CLEAR:
1006 			case PKH_EVENT_SPAN_ALARM_E1_AIS_CLEAR:
1007 			case PKH_EVENT_SPAN_ALARM_E1_RMAI_CLEAR:
1008 			case PKH_EVENT_SPAN_ALARM_E1_TS16AIS_CLEAR:
1009 			case PKH_EVENT_SPAN_ALARM_E1_TS16LOS_CLEAR:
1010 			case PKH_EVENT_SPAN_IN_SYNC:
1011 			case PKH_EVENT_SPAN_LOSS_OF_SIGNAL_CLEAR:
1012 			case PKH_EVENT_SPAN_IN_CRC_MF_SYNC:
1013 			case PKH_EVENT_SPAN_IN_CAS_MF_SYNC:
1014 				event_id = FTDM_OOB_ALARM_CLEAR;
1015 				break;
1016 			case PKH_EVENT_SPAN_MESSAGE:
1017 			case PKH_EVENT_SPAN_ABCD_SIGNAL_CHANGE:
1018 				break;
1019 			}
1020 
1021 			if (event_id) {
1022 				uint32_t x = 0;
1023 				ftdm_channel_t *ftdmchan;
1024 				pika_chan_data_t *chan_data;
1025 				for(x = 1; x <= span->chan_count; x++) {
1026 					ftdmchan = span->channels[x];
1027 					assert(ftdmchan != NULL);
1028 					chan_data = (pika_chan_data_t *) ftdmchan->io_data;
1029 					assert(chan_data != NULL);
1030 
1031 
1032 					ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT);
1033 					ftdmchan->last_event_time = last_event_time;
1034 					chan_data->last_oob_event = span_data->last_oob_event;
1035 				}
1036 
1037 			}
1038 
1039 		}
1040 
1041 		PKH_EVENT_GetText(span_data->last_oob_event.id, event_text, sizeof(event_text));
1042 		//ftdm_log(FTDM_LOG_DEBUG, "Event: %s\n", event_text);
1043 
1044 		if (ftdmchan) {
1045 			pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
1046 
1047 			assert(chan_data != NULL);
1048 			ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT);
1049             ftdmchan->last_event_time = ftdm_current_time_in_ms();
1050 			chan_data->last_oob_event = span_data->last_oob_event;
1051 		}
1052 
1053 		return FTDM_SUCCESS;
1054 	}
1055 
1056 	return FTDM_FAIL;
1057 }
1058 
1059 /**
1060  * \brief Retrieves an event from a Pika span
1061  * \param span Span to retrieve event from
1062  * \param event FreeTDM event to return
1063  * \return Success or failure
1064  */
FIO_SPAN_NEXT_EVENT_FUNCTION(pika_next_event)1065 static FIO_SPAN_NEXT_EVENT_FUNCTION(pika_next_event)
1066 {
1067 	uint32_t i, event_id = 0;
1068 
1069 	for(i = 1; i <= span->chan_count; i++) {
1070 		if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) {
1071 			pika_chan_data_t *chan_data = (pika_chan_data_t *) span->channels[i]->io_data;
1072 			PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH];
1073 
1074 			ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT);
1075 
1076 			PKH_EVENT_GetText(chan_data->last_oob_event.id, event_text, sizeof(event_text));
1077 
1078 			switch(chan_data->last_oob_event.id) {
1079 			case PKH_EVENT_HDLC_MESSAGE:
1080 				chan_data->hdlc_bytes = chan_data->last_oob_event.p2;
1081 				continue;
1082 			case PKH_EVENT_TRUNK_HOOKFLASH:
1083 				event_id = FTDM_OOB_FLASH;
1084 				break;
1085 			case PKH_EVENT_TRUNK_RING_OFF:
1086 				event_id = FTDM_OOB_RING_STOP;
1087 				break;
1088 			case PKH_EVENT_TRUNK_RING_ON:
1089 				event_id = FTDM_OOB_RING_START;
1090 				break;
1091 
1092 			case PKH_EVENT_PHONE_OFFHOOK:
1093 				ftdm_set_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK);
1094 				event_id = FTDM_OOB_OFFHOOK;
1095 				break;
1096 
1097 			case PKH_EVENT_TRUNK_BELOW_THRESHOLD:
1098 			case PKH_EVENT_TRUNK_ABOVE_THRESHOLD:
1099 			case PKH_EVENT_PHONE_ONHOOK:
1100 				ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK);
1101 				event_id = FTDM_OOB_ONHOOK;
1102 				break;
1103 
1104 
1105 
1106 			case PKH_EVENT_SPAN_ALARM_T1_RED:
1107 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED);
1108 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RED ALARM");
1109 				event_id = FTDM_OOB_ALARM_TRAP;
1110 				break;
1111 			case PKH_EVENT_SPAN_ALARM_T1_YELLOW:
1112 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_YELLOW);
1113 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "YELLOW ALARM");
1114 				event_id = FTDM_OOB_ALARM_TRAP;
1115 				break;
1116 			case PKH_EVENT_SPAN_ALARM_T1_AIS:
1117 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS);
1118 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "AIS ALARM");
1119 				event_id = FTDM_OOB_ALARM_TRAP;
1120 				break;
1121 			case PKH_EVENT_SPAN_ALARM_E1_RED:
1122 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED);
1123 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RED ALARM");
1124 				event_id = FTDM_OOB_ALARM_TRAP;
1125 				break;
1126 			case PKH_EVENT_SPAN_ALARM_E1_RAI:
1127 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RAI);
1128 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RAI ALARM");
1129 				event_id = FTDM_OOB_ALARM_TRAP;
1130 				break;
1131 			case PKH_EVENT_SPAN_ALARM_E1_AIS:
1132 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS);
1133 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "AIS ALARM");
1134 				event_id = FTDM_OOB_ALARM_TRAP;
1135 				break;
1136 			case PKH_EVENT_SPAN_ALARM_E1_RMAI:
1137 			case PKH_EVENT_SPAN_ALARM_E1_TS16AIS:
1138 			case PKH_EVENT_SPAN_ALARM_E1_TS16LOS:
1139 			case PKH_EVENT_SPAN_OUT_OF_SYNC:
1140 			case PKH_EVENT_SPAN_FRAMING_ERROR:
1141 			case PKH_EVENT_SPAN_LOSS_OF_SIGNAL:
1142 			case PKH_EVENT_SPAN_OUT_OF_CRC_MF_SYNC:
1143 			case PKH_EVENT_SPAN_OUT_OF_CAS_MF_SYNC:
1144 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_GENERAL);
1145 				snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "GENERAL ALARM");
1146 				event_id = FTDM_OOB_ALARM_TRAP;
1147 				break;
1148 			case PKH_EVENT_SPAN_ALARM_T1_RED_CLEAR:
1149 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED);
1150 			case PKH_EVENT_SPAN_ALARM_T1_YELLOW_CLEAR:
1151 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_YELLOW);
1152 			case PKH_EVENT_SPAN_ALARM_T1_AIS_CLEAR:
1153 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS);
1154 			case PKH_EVENT_SPAN_ALARM_E1_RED_CLEAR:
1155 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED);
1156 			case PKH_EVENT_SPAN_ALARM_E1_RAI_CLEAR:
1157 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RAI);
1158 			case PKH_EVENT_SPAN_ALARM_E1_AIS_CLEAR:
1159 				ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS);
1160 			case PKH_EVENT_SPAN_ALARM_E1_RMAI_CLEAR:
1161 			case PKH_EVENT_SPAN_ALARM_E1_TS16AIS_CLEAR:
1162 			case PKH_EVENT_SPAN_ALARM_E1_TS16LOS_CLEAR:
1163 			case PKH_EVENT_SPAN_IN_SYNC:
1164 			case PKH_EVENT_SPAN_LOSS_OF_SIGNAL_CLEAR:
1165 			case PKH_EVENT_SPAN_IN_CRC_MF_SYNC:
1166 			case PKH_EVENT_SPAN_IN_CAS_MF_SYNC:
1167 				ftdm_clear_alarm_flag(span->channels[i], FTDM_ALARM_GENERAL);
1168 				event_id = FTDM_OOB_ALARM_CLEAR;
1169 				break;
1170 			case PKH_EVENT_SPAN_MESSAGE:
1171 			case PKH_EVENT_SPAN_ABCD_SIGNAL_CHANGE:
1172 				break;
1173 
1174 
1175 
1176 
1177 			case PKH_EVENT_TRUNK_ONHOOK:
1178 			case PKH_EVENT_TRUNK_OFFHOOK:
1179 			case PKH_EVENT_TRUNK_DIALED :
1180 			case PKH_EVENT_TRUNK_REVERSAL:
1181 			case PKH_EVENT_TRUNK_LCSO:
1182 			case PKH_EVENT_TRUNK_DROPOUT:
1183 			case PKH_EVENT_TRUNK_LOF:
1184 			case PKH_EVENT_TRUNK_RX_OVERLOAD:
1185 			default:
1186 				ftdm_log(FTDM_LOG_DEBUG, "Unhandled event %d on channel %d [%s]\n", chan_data->last_oob_event.id, i, event_text);
1187 				event_id = FTDM_OOB_INVALID;
1188 				break;
1189 			}
1190 
1191 			span->channels[i]->last_event_time = 0;
1192 			span->event_header.e_type = FTDM_EVENT_OOB;
1193 			span->event_header.enum_id = event_id;
1194 			span->event_header.channel = span->channels[i];
1195 			*event = &span->event_header;
1196 			return FTDM_SUCCESS;
1197 		}
1198 	}
1199 
1200 	return FTDM_FAIL;
1201 }
1202 
1203 /**
1204  * \brief Destroys a Pika Span
1205  * \param span Span to destroy
1206  * \return Success
1207  */
FIO_SPAN_DESTROY_FUNCTION(pika_span_destroy)1208 static FIO_SPAN_DESTROY_FUNCTION(pika_span_destroy)
1209 {
1210 	pika_span_data_t *span_data = (pika_span_data_t *) span->io_data;
1211 
1212 	if (span_data) {
1213 		PKH_QUEUE_Destroy(span_data->event_queue);
1214 		ftdm_safe_free(span_data);
1215 	}
1216 
1217 	return FTDM_SUCCESS;
1218 }
1219 
1220 /**
1221  * \brief Destroys a Pika Channel
1222  * \param ftdmchan Channel to destroy
1223  * \return Success or failure
1224  */
FIO_CHANNEL_DESTROY_FUNCTION(pika_channel_destroy)1225 static FIO_CHANNEL_DESTROY_FUNCTION(pika_channel_destroy)
1226 {
1227 	pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->io_data;
1228 	pika_span_data_t *span_data = (pika_span_data_t *) ftdmchan->span->io_data;
1229 
1230 	if (!chan_data) {
1231 		return FTDM_FAIL;
1232 	}
1233 
1234 	if (!ftdm_test_flag(chan_data, PK_FLAG_READY)) {
1235 		goto end;
1236 	}
1237 
1238 	PKH_RECORD_Stop(chan_data->media_in);
1239 	PKH_PLAY_Stop(chan_data->media_out);
1240 	PKH_QUEUE_Destroy(chan_data->media_in_queue);
1241 	PKH_QUEUE_Destroy(chan_data->media_out_queue);
1242 
1243 	switch(ftdmchan->type) {
1244 	case FTDM_CHAN_TYPE_FXS:
1245 		PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle);
1246 		PKH_PHONE_Close(chan_data->handle);
1247 		break;
1248 	case FTDM_CHAN_TYPE_FXO:
1249 		PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle);
1250 		PKH_TRUNK_Close(chan_data->handle);
1251 		break;
1252 	case FTDM_CHAN_TYPE_DQ921:
1253 		PKH_SPAN_Stop(span_data->handle);
1254 		break;
1255 	default:
1256 		break;
1257 	}
1258 
1259 
1260 	ftdm_mutex_destroy(&chan_data->digit_mutex);
1261 	ftdm_buffer_destroy(&chan_data->digit_buffer);
1262 
1263  end:
1264 	ftdm_safe_free(chan_data);
1265 
1266 	return FTDM_SUCCESS;
1267 }
1268 
1269 /**
1270  * \brief Gets alarms from a Pika Channel (does nothing)
1271  * \param ftdmchan Channel to get alarms from
1272  * \return Failure
1273  */
FIO_GET_ALARMS_FUNCTION(pika_get_alarms)1274 static FIO_GET_ALARMS_FUNCTION(pika_get_alarms)
1275 {
1276 	return FTDM_FAIL;
1277 }
1278 
1279 static ftdm_io_interface_t pika_interface;
1280 
1281 /**
1282  * \brief Loads Pika IO module
1283  * \param fio FreeTDM IO interface
1284  * \return Success or failure
1285  */
FIO_IO_LOAD_FUNCTION(pika_init)1286 static FIO_IO_LOAD_FUNCTION(pika_init)
1287 {
1288 
1289 	PK_STATUS status;
1290 	PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH];
1291 	uint32_t i;
1292 	int ok = 0;
1293 	PKH_TLogMasks m;
1294 	TPikaHandle tmpHandle;
1295 
1296 	assert(fio != NULL);
1297 	memset(&pika_interface, 0, sizeof(pika_interface));
1298 	memset(&globals, 0, sizeof(globals));
1299 	globals.general_config.region = PKH_TRUNK_NA;
1300 
1301 	globals.profile_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys);
1302 
1303 	// Open the system object, to enumerate boards configured for this system
1304 	if ((status = PKH_SYSTEM_Open(&globals.system_handle)) != PK_SUCCESS) {
1305 		ftdm_log(FTDM_LOG_ERROR, "Error: PKH_SYSTEM_Open failed(%s)!\n",
1306 				PKH_ERROR_GetText(status, error_text, sizeof(error_text)));
1307 		return FTDM_FAIL;
1308 	}
1309 
1310 	// Retrieves a list of all boards in this system, existing,
1311 	// or listed in pika.cfg
1312 	if ((status = PKH_SYSTEM_Detect(globals.system_handle, &globals.board_list)) != PK_SUCCESS) {
1313 		ftdm_log(FTDM_LOG_ERROR, "Error: PKH_SYSTEM_Detect failed(%s)!\n",
1314 				PKH_ERROR_GetText(status, error_text, sizeof(error_text)));
1315 		return FTDM_FAIL;
1316 	}
1317 
1318 	PKH_SYSTEM_GetConfig(globals.system_handle, &globals.system_config);
1319 	globals.system_config.maxAudioProcessBlockSize = PIKA_BLOCK_LEN;
1320 	globals.system_config.playBufferSize = PIKA_BLOCK_SIZE;
1321 	globals.system_config.recordBufferSize = PIKA_BLOCK_SIZE;
1322 	globals.system_config.recordNumberOfBuffers = PIKA_NUM_BUFFERS;
1323 	PKH_SYSTEM_SetConfig(globals.system_handle, &globals.system_config);
1324 
1325 	status = PKH_MEDIA_STREAM_Create(&tmpHandle);
1326 	status = PKH_RECORD_GetConfig(tmpHandle, &globals.record_config);
1327 	status = PKH_PLAY_GetConfig(tmpHandle, &globals.play_config);
1328 	status = PKH_EC_GetConfig(tmpHandle, &globals.ec_config);
1329 	status = PKH_MEDIA_STREAM_Destroy(tmpHandle);
1330 
1331 
1332 
1333 	ftdm_log(FTDM_LOG_DEBUG, "Found %u board%s\n", globals.board_list.numberOfBoards, globals.board_list.numberOfBoards == 1 ? "" : "s");
1334 	for(i = 0; i < globals.board_list.numberOfBoards; ++i) {
1335 		ftdm_log(FTDM_LOG_INFO, "Found PIKA board type:[%s] id:[%u] serno:[%u]\n",
1336 				pika_board_type_string(globals.board_list.board[i].type), globals.board_list.board[i].id, (uint32_t)
1337 				globals.board_list.board[i].serialNumber);
1338 
1339 		if (globals.board_list.board[i].type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) {
1340 			TPikaHandle board_handle, span_handle;
1341 			PKH_TBoardConfig boardConfig;
1342 			PKH_BOARD_GetConfig(board_handle, &boardConfig);
1343 			PKH_BOARD_Open(globals.board_list.board[i].id, NULL, &board_handle);
1344 			PKH_SPAN_Open(board_handle, 0, NULL, &span_handle);
1345 			PKH_SPAN_GetConfig(span_handle, &globals.t1_span_config);
1346 			PKH_SPAN_Close(span_handle);
1347 			boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_E1;
1348 			PKH_BOARD_SetConfig(board_handle, &boardConfig);
1349 			PKH_SPAN_Open(board_handle, 0, NULL, &span_handle);
1350 			PKH_SPAN_GetConfig(span_handle, &globals.e1_span_config);
1351 			PKH_SPAN_Close(span_handle);
1352 			boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_T1;
1353 			PKH_BOARD_SetConfig(board_handle, &boardConfig);
1354 			PKH_BOARD_Close(board_handle);
1355 		}
1356 		ok++;
1357 
1358 	}
1359 
1360 	if (!ok) {
1361 		return FTDM_FAIL;
1362 	}
1363 
1364 	pika_interface.name = "pika";
1365 	pika_interface.configure =  pika_configure;
1366 	pika_interface.configure_span = pika_configure_span;
1367 	pika_interface.open = pika_open;
1368 	pika_interface.close = pika_close;
1369 	pika_interface.wait = pika_wait;
1370 	pika_interface.read = pika_read;
1371 	pika_interface.write = pika_write;
1372 	pika_interface.command = pika_command;
1373 	pika_interface.poll_event = pika_poll_event;
1374 	pika_interface.next_event = pika_next_event;
1375 	pika_interface.channel_destroy = pika_channel_destroy;
1376 	pika_interface.span_destroy = pika_span_destroy;
1377 	pika_interface.get_alarms = pika_get_alarms;
1378 	*fio = &pika_interface;
1379 
1380 
1381 	ftdm_log(FTDM_LOG_INFO, "Dumping Default configs:\n");
1382 	ftdm_log(FTDM_LOG_INFO, "rx-gain => %0.2f\n", (float)globals.record_config.gain);
1383 	ftdm_log(FTDM_LOG_INFO, "rx-agc-enabled => %s\n", globals.record_config.AGC.enabled ? "true" : "false");
1384 	ftdm_log(FTDM_LOG_INFO, "rx-agc-targetPower => %0.2f\n", (float)globals.record_config.AGC.targetPower);
1385 	ftdm_log(FTDM_LOG_INFO, "rx-agc-minGain => %0.2f\n", (float)globals.record_config.AGC.minGain);
1386 	ftdm_log(FTDM_LOG_INFO, "rx-agc-maxGain => %0.2f\n", (float)globals.record_config.AGC.maxGain);
1387 	ftdm_log(FTDM_LOG_INFO, "rx-agc-attackRate => %d\n", (int)globals.record_config.AGC.attackRate);
1388 	ftdm_log(FTDM_LOG_INFO, "rx-agc-decayRate => %d\n", (int)globals.record_config.AGC.decayRate);
1389 	ftdm_log(FTDM_LOG_INFO, "rx-agc-speechThreshold => %0.2f\n", (float)globals.record_config.AGC.speechThreshold);
1390 	ftdm_log(FTDM_LOG_INFO, "rx-vad-enabled => %s\n", globals.record_config.VAD.enabled ? "true" : "false");
1391 	ftdm_log(FTDM_LOG_INFO, "rx-vad-activationThreshold => %0.2f\n", (float)globals.record_config.VAD.activationThreshold);
1392 	ftdm_log(FTDM_LOG_INFO, "rx-vad-activationDebounceTime => %d\n", (int)globals.record_config.VAD.activationDebounceTime);
1393 	ftdm_log(FTDM_LOG_INFO, "rx-vad-deactivationThreshold => %0.2f\n", (float)globals.record_config.VAD.deactivationThreshold);
1394 	ftdm_log(FTDM_LOG_INFO, "rx-vad-deactivationDebounceTime => %d\n", (int)globals.record_config.VAD.deactivationDebounceTime);
1395 	ftdm_log(FTDM_LOG_INFO, "rx-vad-preSpeechBufferSize => %d\n", (int)globals.record_config.VAD.preSpeechBufferSize);
1396 	ftdm_log(FTDM_LOG_INFO, "tx-gain => %0.2f\n", (float)globals.play_config.gain);
1397 	ftdm_log(FTDM_LOG_INFO, "tx-agc-enabled => %s\n", globals.play_config.AGC.enabled ? "true" : "false");
1398 	ftdm_log(FTDM_LOG_INFO, "tx-agc-targetPower => %0.2f\n", (float)globals.play_config.AGC.targetPower);
1399 	ftdm_log(FTDM_LOG_INFO, "tx-agc-minGain => %0.2f\n", (float)globals.play_config.AGC.minGain);
1400 	ftdm_log(FTDM_LOG_INFO, "tx-agc-maxGain => %0.2f\n", (float)globals.play_config.AGC.maxGain);
1401 	ftdm_log(FTDM_LOG_INFO, "tx-agc-attackRate => %d\n", (int)globals.play_config.AGC.attackRate);
1402 	ftdm_log(FTDM_LOG_INFO, "tx-agc-decayRate => %d\n", (int)globals.play_config.AGC.decayRate);
1403 	ftdm_log(FTDM_LOG_INFO, "tx-agc-speechThreshold => %0.2f\n", (float)globals.play_config.AGC.speechThreshold);
1404 	ftdm_log(FTDM_LOG_INFO, "ec-doubleTalkerThreshold => %0.2f\n", (float)globals.ec_config.doubleTalkerThreshold);
1405 	ftdm_log(FTDM_LOG_INFO, "ec-speechPresentThreshold => %0.2f\n", (float)globals.ec_config.speechPresentThreshold);
1406 	ftdm_log(FTDM_LOG_INFO, "ec-echoSuppressionThreshold => %0.2f\n", (float)globals.ec_config.echoSuppressionThreshold);
1407 	ftdm_log(FTDM_LOG_INFO, "ec-echoSuppressionEnabled => %s\n", globals.ec_config.echoSuppressionEnabled ? "true" : "false");
1408 	ftdm_log(FTDM_LOG_INFO, "ec-comfortNoiseEnabled => %s\n", globals.ec_config.comfortNoiseEnabled ? "true" : "false");
1409 	ftdm_log(FTDM_LOG_INFO, "ec-adaptationModeEnabled => %s\n", globals.ec_config.adaptationModeEnabled ? "true" : "false");
1410 
1411 
1412 
1413 	memset(&m, 0, sizeof(m));
1414 	//m.apiMask = 0xffffffff;
1415 	//PKH_LOG_SetMasks(&m);
1416 
1417 	return FTDM_SUCCESS;
1418 }
1419 
1420 /**
1421  * \brief Unloads Pika IO module
1422  * \return Success
1423  */
FIO_IO_UNLOAD_FUNCTION(pika_destroy)1424 static FIO_IO_UNLOAD_FUNCTION(pika_destroy)
1425 {
1426 	uint32_t x;
1427 	PK_STATUS status;
1428 	PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH];
1429 
1430 	for (x = 0; x < MAX_NUMBER_OF_TRUNKS; x++) {
1431 		if (globals.open_boards[x]) {
1432 			ftdm_log(FTDM_LOG_INFO, "Closing board %u\n", x);
1433 			PKH_BOARD_Close(globals.open_boards[x]);
1434 		}
1435 	}
1436 
1437 	// The system can now be closed.
1438 	if ((status = PKH_SYSTEM_Close(globals.system_handle)) != PK_SUCCESS) {
1439 		ftdm_log(FTDM_LOG_ERROR, "Error: PKH_SYSTEM_Close failed(%s)!\n",
1440 				PKH_ERROR_GetText(status, error_text, sizeof(error_text)));
1441 	} else {
1442 		ftdm_log(FTDM_LOG_INFO, "Closing system handle\n");
1443 	}
1444 
1445 	hashtable_destroy(globals.profile_hash);
1446 
1447 	return FTDM_SUCCESS;
1448 }
1449 
1450 /**
1451  * \brief Pika IO module definition
1452  */
1453 EX_DECLARE_DATA ftdm_module_t ftdm_module = {
1454 	"pika",
1455 	pika_init,
1456 	pika_destroy,
1457 };
1458 
1459 /* For Emacs:
1460  * Local Variables:
1461  * mode:c
1462  * indent-tabs-mode:t
1463  * tab-width:4
1464  * c-basic-offset:4
1465  * End:
1466  * For VIM:
1467  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
1468  */
1469 
1470