diff --git a/libs/freetdm/Makefile b/libs/freetdm/Makefile index 32b70f0cc4..94883699c5 100644 --- a/libs/freetdm/Makefile +++ b/libs/freetdm/Makefile @@ -135,6 +135,9 @@ testtones: $(SRC)/testtones.c $(MYLIB) detect_tones: $(SRC)/detect_tones.c $(MYLIB) $(CC) $(INCS) -L. $(SRC)/detect_tones.c -o detect_tones -lopenzap -lm +detect_dtmf: $(SRC)/detect_dtmf.c $(MYLIB) + $(CC) $(INCS) -L. $(SRC)/detect_dtmf.c -o detect_dtmf -lopenzap -lm + testisdn: $(SRC)/testisdn.c $(MYLIB) $(CC) $(INCS) $(ZAP_CFLAGS) -L. $(SRC)/testisdn.c -o testisdn -lopenzap -lm -lpthread @@ -174,6 +177,6 @@ mod_openzap-clean: @if [ -f mod_openzap/mod_openzap.so ] ; then cd mod_openzap && make clean ; fi clean: mod_openzap-clean - rm -f $(SRC)/*.o $(SRC)/isdn/*.o $(MYLIB) *~ \#* testapp testcid testtones detect_tones priserver testisdn testanalog + rm -f $(SRC)/*.o $(SRC)/isdn/*.o $(MYLIB) *~ \#* testapp testcid testtones detect_tones detect_dtmf priserver testisdn testanalog @if [ -f $(LIBPRI)/$(LIBPRIA) ] ; then cd $(LIBPRI) && make clean ; fi diff --git a/libs/freetdm/conf/pika.conf b/libs/freetdm/conf/pika.conf new file mode 100644 index 0000000000..1cba63b2ef --- /dev/null +++ b/libs/freetdm/conf/pika.conf @@ -0,0 +1,33 @@ +; each category is a config profile +; to apply the profile append it to a channel def in +; openzap.conf with @ +; e.g. +; [span pika] +; name => pika +; number => pika +; fxs-channel => 1:0:1-12@default + +[default] +rx-gain => 0 +rx-agc-enabled => 0 +rx-agc-targetPower => 0 +rx-agc-minGain => 0 +rx-agc-maxGain => 0 +rx-agc-attackRate => 0 +rx-agc-decayRate => 0 +rx-agc-speechThreshold => 0 +rx-vad-enabled => 0 +rx-vad-activationThreshold => 0 +rx-vad-activationDebounceTime => 0 +rx-vad-deactivationThreshold => 0 +rx-vad-deactivationDebounceTime => 0 +rx-vad-preSpeechBufferSize => 0 +tx-gain => 0 +tx-agc-enabled => 0 +tx-agc-targetPower => 0 +tx-agc-minGain => 0 +tx-agc-maxGain => 0 +tx-agc-attackRate => 0 +tx-agc-decayRate => 0 +tx-agc-speechThreshold => 0 + diff --git a/libs/freetdm/mod_openzap/mod_openzap.c b/libs/freetdm/mod_openzap/mod_openzap.c index fc63935a6c..a712b500f3 100644 --- a/libs/freetdm/mod_openzap/mod_openzap.c +++ b/libs/freetdm/mod_openzap/mod_openzap.c @@ -304,14 +304,14 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) switch (tech_pvt->zchan->type) { case ZAP_CHAN_TYPE_FXO: { - if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN) { - zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); - } + + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); + } break; case ZAP_CHAN_TYPE_FXS: { - if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN) { + if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_BUSY && tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN) { if (tech_pvt->zchan->token_count) { cycle_foreground(tech_pvt->zchan, 0); } else { @@ -466,20 +466,21 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch } if (!switch_test_flag(tech_pvt, TFLAG_IO)) { - return SWITCH_STATUS_FALSE; + goto fail; } + wflags = ZAP_READ; status = zap_channel_wait(tech_pvt->zchan, &wflags, chunk); - + if (status == ZAP_FAIL) { - return SWITCH_STATUS_GENERR; + goto fail; } if (status == ZAP_TIMEOUT) { if (timeout > 0 && !switch_test_flag(tech_pvt, TFLAG_HOLD)) { total_to -= chunk; if (total_to <= 0) { - return SWITCH_STATUS_BREAK; + goto fail; } } @@ -487,12 +488,12 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch } if (!(wflags & ZAP_READ)) { - return SWITCH_STATUS_GENERR; + goto fail; } len = tech_pvt->read_frame.buflen; if (zap_channel_read(tech_pvt->zchan, tech_pvt->read_frame.data, &len) != ZAP_SUCCESS) { - return SWITCH_STATUS_GENERR; + goto fail; } *frame = &tech_pvt->read_frame; @@ -509,6 +510,12 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch return SWITCH_STATUS_SUCCESS; + fail: + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_GENERR; + + } static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id) @@ -530,13 +537,13 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc } if (!switch_test_flag(tech_pvt, TFLAG_IO)) { - return SWITCH_STATUS_FALSE; + goto fail; } len = frame->datalen; if (zap_channel_write(tech_pvt->zchan, frame->data, frame->buflen, &len) != ZAP_SUCCESS) { if (++tech_pvt->wr_error > 10) { - return SWITCH_STATUS_GENERR; + goto fail; } } else { tech_pvt->wr_error = 0; @@ -544,6 +551,11 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc return SWITCH_STATUS_SUCCESS; + fail: + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_GENERR; + } static switch_status_t channel_receive_message_b(switch_core_session_t *session, switch_core_session_message_t *msg) @@ -833,7 +845,7 @@ zap_status_t zap_channel_from_event(zap_sigmsg_t *sigmsg, switch_core_session_t switch_set_string(sigmsg->channel->caller_data.cid_num, sigmsg->channel->chan_number); } } - + tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), "OpenZAP", SPAN_CONFIG[sigmsg->channel->span_id].dialplan, @@ -875,9 +887,29 @@ static ZIO_SIGNAL_CB_FUNCTION(on_fxo_signal) switch_channel_t *channel = NULL; zap_status_t status; - zap_log(ZAP_LOG_DEBUG, "got FXO sig [%s]\n", zap_signal_event2str(sigmsg->event_id)); + zap_log(ZAP_LOG_DEBUG, "got FXO sig %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id, zap_signal_event2str(sigmsg->event_id)); switch(sigmsg->event_id) { + + case ZAP_SIGEVENT_PROGRESS_MEDIA: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_pre_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_STOP: + { + while((session = zap_channel_get_session(sigmsg->channel, 0))) { + zap_channel_clear_token(sigmsg->channel, 0); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + switch_core_session_rwunlock(session); + } + } + break; case ZAP_SIGEVENT_UP: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { diff --git a/libs/freetdm/src/detect_dtmf.c b/libs/freetdm/src/detect_dtmf.c new file mode 100644 index 0000000000..941237b464 --- /dev/null +++ b/libs/freetdm/src/detect_dtmf.c @@ -0,0 +1,32 @@ +//#include "openzap.h" +#include "libteletone_detect.h" + +int main(int argc, char *argv[]) +{ + int fd, b; + short sln[512] = {0}; + teletone_dtmf_detect_state_t dtmf_detect = {0}; + char digit_str[128] = ""; + + if (argc < 2) { + fprintf(stderr, "Arg Error!\n"); + exit(-1); + } + + teletone_dtmf_detect_init (&dtmf_detect, 8000); + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "File Error!\n", strerror(errno)); + exit(-1); + } + + while((b = read(fd, sln, 320)) > 0) { + teletone_dtmf_detect(&dtmf_detect, sln, b / 2); + teletone_dtmf_get(&dtmf_detect, digit_str, sizeof(digit_str)); + if (*digit_str) { + printf("digit: %s\n", digit_str); + } + } + close(fd); +} + diff --git a/libs/freetdm/src/detect_tones.c b/libs/freetdm/src/detect_tones.c index 52ccbcd506..c5d475edc3 100644 --- a/libs/freetdm/src/detect_tones.c +++ b/libs/freetdm/src/detect_tones.c @@ -5,7 +5,7 @@ int main(int argc, char *argv[]) { teletone_generation_session_t ts; teletone_multi_tone_t mt = {0}; - teletone_tone_map_t map = {350.0, 440.0, 0.0}; + teletone_tone_map_t map = {0}; int fd, b; short sln[512] = {0}; @@ -15,6 +15,9 @@ int main(int argc, char *argv[]) exit(-1); } + + map.freqs[0] = atof("350"); + map.freqs[1] = atof("440"); teletone_multi_tone_init(&mt, &map); diff --git a/libs/freetdm/src/include/openzap.h b/libs/freetdm/src/include/openzap.h index 12bc936a75..c57ef1c5f3 100644 --- a/libs/freetdm/src/include/openzap.h +++ b/libs/freetdm/src/include/openzap.h @@ -211,12 +211,12 @@ #define zap_set_state_locked(obj, s) if ( obj->state == s ) { \ - zap_log(ZAP_LOG_WARNING, "Why bother changing state from %s to %s\n", zap_channel_state2str(obj->state), zap_channel_state2str(s)); \ + zap_log(ZAP_LOG_WARNING, "Why bother changing state on %d:%dfrom %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(obj->state), zap_channel_state2str(s)); \ } else if (zap_test_flag(obj, ZAP_CHANNEL_READY)) { \ int st = obj->state; \ zap_channel_set_state(obj, s); \ - if (obj->state == s) zap_log(ZAP_LOG_DEBUG, "Changing state from %s to %s\n", zap_channel_state2str(st), zap_channel_state2str(s)); \ - else zap_log(ZAP_LOG_WARNING, "VETO Changing state from %s to %s\n", zap_channel_state2str(st), zap_channel_state2str(s)); \ + if (obj->state == s) zap_log(ZAP_LOG_DEBUG, "Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(st), zap_channel_state2str(s)); \ + else zap_log(ZAP_LOG_WARNING, "VETO Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(st), zap_channel_state2str(s)); \ } @@ -344,7 +344,6 @@ struct zap_channel { zap_event_t event_header; char last_error[256]; zio_event_cb_t event_callback; - void *mod_data; uint32_t skip_read_frames; zap_buffer_t *dtmf_buffer; zap_buffer_t *digit_buffer; @@ -365,6 +364,7 @@ struct zap_channel { zap_fsk_data_state_t fsk; uint8_t fsk_buf[80]; uint32_t ring_count; + void *mod_data; struct zap_caller_data caller_data; struct zap_span *span; struct zap_io_interface *zio; @@ -416,7 +416,8 @@ struct zap_span { teletone_multi_tone_t tone_finder[ZAP_TONEMAP_INVALID+1]; zap_channel_t channels[ZAP_MAX_CHANNELS_SPAN]; zio_channel_outgoing_call_t outgoing_call; - void *app_data; + void *mod_data; + char *type; }; @@ -429,6 +430,7 @@ struct zap_io_interface { zio_open_t open; zio_close_t close; zio_channel_destroy_t channel_destroy; + zio_span_destroy_t span_destroy; zio_get_alarms_t get_alarms; zio_command_t command; zio_wait_t wait; @@ -505,6 +507,8 @@ void zap_global_set_default_logger(int level); uint32_t zap_separate_string(char *buf, char delim, char **array, int arraylen); void print_bits(uint8_t *b, int bl, char *buf, int blen, int e, uint8_t ss); void print_hex_bytes(uint8_t *data, zap_size_t dlen, char *buf, zap_size_t blen); +int zap_hash_equalkeys(void *k1, void *k2); +uint32_t zap_hash_hashfromstring(void *ky); ZIO_CODEC_FUNCTION(zio_slin2ulaw); ZIO_CODEC_FUNCTION(zio_ulaw2slin); ZIO_CODEC_FUNCTION(zio_slin2alaw); diff --git a/libs/freetdm/src/include/zap_pika.h b/libs/freetdm/src/include/zap_pika.h new file mode 100644 index 0000000000..6d1c68f5f6 --- /dev/null +++ b/libs/freetdm/src/include/zap_pika.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ZAP_PIKA_H +#define ZAP_PIKA_H +#include "openzap.h" +#include "pikahmpapi.h" +/* Openzap PIKA hardware interface functions */ +zap_status_t pika_init(zap_io_interface_t **zint); +zap_status_t pika_destroy(void); + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ diff --git a/libs/freetdm/src/include/zap_types.h b/libs/freetdm/src/include/zap_types.h index 4055b0c931..e05f219f25 100644 --- a/libs/freetdm/src/include/zap_types.h +++ b/libs/freetdm/src/include/zap_types.h @@ -267,10 +267,11 @@ typedef enum { } zap_chan_type_t; typedef enum { - ZAP_CHANNEL_FEATURE_DTMF = (1 << 0), - ZAP_CHANNEL_FEATURE_CODECS = (1 << 1), - ZAP_CHANNEL_FEATURE_INTERVAL = (1 << 2), - ZAP_CHANNEL_FEATURE_CALLERID = (1 << 3) + ZAP_CHANNEL_FEATURE_DTMF_DETECT = (1 << 0), + ZAP_CHANNEL_FEATURE_DTMF_GENERATE = (1 << 1), + ZAP_CHANNEL_FEATURE_CODECS = (1 << 2), + ZAP_CHANNEL_FEATURE_INTERVAL = (1 << 3), + ZAP_CHANNEL_FEATURE_CALLERID = (1 << 4) } zap_channel_feature_t; typedef enum { @@ -339,6 +340,7 @@ typedef struct zap_span zap_span_t; #define ZIO_OPEN_ARGS (zap_channel_t *zchan) #define ZIO_CLOSE_ARGS (zap_channel_t *zchan) #define ZIO_CHANNEL_DESTROY_ARGS (zap_channel_t *zchan) +#define ZIO_SPAN_DESTROY_ARGS (zap_span_t *span) #define ZIO_COMMAND_ARGS (zap_channel_t *zchan, zap_command_t command, void *obj) #define ZIO_WAIT_ARGS (zap_channel_t *zchan, zap_wait_flag_t *flags, int32_t to) #define ZIO_GET_ALARMS_ARGS (zap_channel_t *zchan) @@ -356,6 +358,7 @@ typedef zap_status_t (*zio_configure_t) ZIO_CONFIGURE_ARGS ; typedef zap_status_t (*zio_open_t) ZIO_OPEN_ARGS ; typedef zap_status_t (*zio_close_t) ZIO_CLOSE_ARGS ; typedef zap_status_t (*zio_channel_destroy_t) ZIO_CHANNEL_DESTROY_ARGS ; +typedef zap_status_t (*zio_span_destroy_t) ZIO_SPAN_DESTROY_ARGS ; typedef zap_status_t (*zio_get_alarms_t) ZIO_GET_ALARMS_ARGS ; typedef zap_status_t (*zio_command_t) ZIO_COMMAND_ARGS ; typedef zap_status_t (*zio_wait_t) ZIO_WAIT_ARGS ; @@ -373,6 +376,7 @@ typedef zap_status_t (*zio_write_t) ZIO_WRITE_ARGS ; #define ZIO_OPEN_FUNCTION(name) zap_status_t name ZIO_OPEN_ARGS #define ZIO_CLOSE_FUNCTION(name) zap_status_t name ZIO_CLOSE_ARGS #define ZIO_CHANNEL_DESTROY_FUNCTION(name) zap_status_t name ZIO_CHANNEL_DESTROY_ARGS +#define ZIO_SPAN_DESTROY_FUNCTION(name) zap_status_t name ZIO_SPAN_DESTROY_ARGS #define ZIO_GET_ALARMS_FUNCTION(name) zap_status_t name ZIO_GET_ALARMS_ARGS #define ZIO_COMMAND_FUNCTION(name) zap_status_t name ZIO_COMMAND_ARGS #define ZIO_WAIT_FUNCTION(name) zap_status_t name ZIO_WAIT_ARGS @@ -410,6 +414,69 @@ typedef struct value zap_hash_val_t; typedef struct zap_bitstream zap_bitstream_t; typedef struct zap_fsk_modulator zap_fsk_modulator_t; +typedef enum { + ZAP_CAUSE_UNALLOCATED = 0, + ZAP_CAUSE_SUCCESS = 1, + ZAP_CAUSE_NO_ROUTE_TRANSIT_NET = 2, + ZAP_CAUSE_NO_ROUTE_DESTINATION = 3, + ZAP_CAUSE_CHANNEL_UNACCEPTABLE = 6, + ZAP_CAUSE_CALL_AWARDED_DELIVERED = 7, + ZAP_CAUSE_NORMAL_CLEARING = 16, + ZAP_CAUSE_USER_BUSY = 17, + ZAP_CAUSE_NO_USER_RESPONSE = 18, + ZAP_CAUSE_NO_ANSWER = 19, + ZAP_CAUSE_SUBSCRIBER_ABSENT = 20, + ZAP_CAUSE_CALL_REJECTED = 21, + ZAP_CAUSE_NUMBER_CHANGED = 22, + ZAP_CAUSE_REDIRECTION_TO_NEW_DESTINATION = 23, + ZAP_CAUSE_EXCHANGE_ROUTING_ERROR = 25, + ZAP_CAUSE_DESTINATION_OUT_OF_ORDER = 27, + ZAP_CAUSE_INVALID_NUMBER_FORMAT = 28, + ZAP_CAUSE_FACILITY_REJECTED = 29, + ZAP_CAUSE_RESPONSE_TO_STATUS_ENQUIRY = 30, + ZAP_CAUSE_NORMAL_UNSPECIFIED = 31, + ZAP_CAUSE_NORMAL_CIRCUIT_CONGESTION = 34, + ZAP_CAUSE_NETWORK_OUT_OF_ORDER = 38, + ZAP_CAUSE_NORMAL_TEMPORARY_FAILURE = 41, + ZAP_CAUSE_SWITCH_CONGESTION = 42, + ZAP_CAUSE_ACCESS_INFO_DISCARDED = 43, + ZAP_CAUSE_REQUESTED_CHAN_UNAVAIL = 44, + ZAP_CAUSE_PRE_EMPTED = 45, + ZAP_CAUSE_FACILITY_NOT_SUBSCRIBED = 50, + ZAP_CAUSE_OUTGOING_CALL_BARRED = 52, + ZAP_CAUSE_INCOMING_CALL_BARRED = 54, + ZAP_CAUSE_BEARERCAPABILITY_NOTAUTH = 57, + ZAP_CAUSE_BEARERCAPABILITY_NOTAVAIL = 58, + ZAP_CAUSE_SERVICE_UNAVAILABLE = 63, + ZAP_CAUSE_BEARERCAPABILITY_NOTIMPL = 65, + ZAP_CAUSE_CHAN_NOT_IMPLEMENTED = 66, + ZAP_CAUSE_FACILITY_NOT_IMPLEMENTED = 69, + ZAP_CAUSE_SERVICE_NOT_IMPLEMENTED = 79, + ZAP_CAUSE_INVALID_CALL_REFERENCE = 81, + ZAP_CAUSE_INCOMPATIBLE_DESTINATION = 88, + ZAP_CAUSE_INVALID_MSG_UNSPECIFIED = 95, + ZAP_CAUSE_MANDATORY_IE_MISSING = 96, + ZAP_CAUSE_MESSAGE_TYPE_NONEXIST = 97, + ZAP_CAUSE_WRONG_MESSAGE = 98, + ZAP_CAUSE_IE_NONEXIST = 99, + ZAP_CAUSE_INVALID_IE_CONTENTS = 100, + ZAP_CAUSE_WRONG_CALL_STATE = 101, + ZAP_CAUSE_RECOVERY_ON_TIMER_EXPIRE = 102, + ZAP_CAUSE_MANDATORY_IE_LENGTH_ERROR = 103, + ZAP_CAUSE_PROTOCOL_ERROR = 111, + ZAP_CAUSE_INTERWORKING = 127, + ZAP_CAUSE_ORIGINATOR_CANCEL = 487, + ZAP_CAUSE_CRASH = 500, + ZAP_CAUSE_SYSTEM_SHUTDOWN = 501, + ZAP_CAUSE_LOSE_RACE = 502, + ZAP_CAUSE_MANAGER_REQUEST = 503, + ZAP_CAUSE_BLIND_TRANSFER = 600, + ZAP_CAUSE_ATTENDED_TRANSFER = 601, + ZAP_CAUSE_ALLOTTED_TIMEOUT = 602, + ZAP_CAUSE_USER_CHALLENGE = 603, + ZAP_CAUSE_MEDIA_TIMEOUT = 604 +} zap_call_cause_t; + #endif /* For Emacs: diff --git a/libs/freetdm/src/testanalog.c b/libs/freetdm/src/testanalog.c index dc90861834..1ad530db89 100644 --- a/libs/freetdm/src/testanalog.c +++ b/libs/freetdm/src/testanalog.c @@ -14,7 +14,8 @@ static void *test_call(zap_thread_t *me, void *obj) zap_log(ZAP_LOG_DEBUG, "answer call and start echo test\n"); zap_set_state_locked(chan, ZAP_CHANNEL_STATE_UP); - + zap_channel_command(chan, ZAP_COMMAND_SEND_DTMF, "5551212"); + while (chan->state == ZAP_CHANNEL_STATE_UP) { zap_wait_flag_t flags = ZAP_READ; diff --git a/libs/freetdm/src/testtones.c b/libs/freetdm/src/testtones.c index b041095b76..d6b3a68552 100644 --- a/libs/freetdm/src/testtones.c +++ b/libs/freetdm/src/testtones.c @@ -20,8 +20,8 @@ int main(int argc, char *argv[]) teletone_generation_session_t ts; struct ttmp tmp; - if (argc < 2) { - fprintf(stderr, "Arg Error!\n"); + if (argc < 3) { + fprintf(stderr, "Arg Error! \n"); exit(-1); } diff --git a/libs/freetdm/src/zap_analog.c b/libs/freetdm/src/zap_analog.c index 23a9054dd0..bc02794acd 100644 --- a/libs/freetdm/src/zap_analog.c +++ b/libs/freetdm/src/zap_analog.c @@ -44,7 +44,8 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj); static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_fxo_outgoing_call) { if (!zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { - //zap_channel_command(zchan, ZAP_COMMAND_TRACE_INPUT, "/tmp/inbound.ul"); + zap_channel_command(zchan, ZAP_COMMAND_TRACE_INPUT, "/tmp/inbound.ul"); + zap_channel_command(zchan, ZAP_COMMAND_TRACE_OUTPUT, "/tmp/outbound.ul"); zap_channel_clear_needed_tones(zchan); zap_channel_clear_detected_tones(zchan); @@ -281,13 +282,14 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj) case ZAP_CHANNEL_STATE_HANGUP: { if (state_counter > 500) { - + if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) { + zap_channel_command(zchan, ZAP_COMMAND_GENERATE_RING_OFF, NULL); + } + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && zchan->last_state >= ZAP_CHANNEL_STATE_IDLE) { zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); } else { - if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) { - zap_channel_command(zchan, ZAP_COMMAND_GENERATE_RING_OFF, NULL); - } + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CLEARING; zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); } } @@ -346,7 +348,9 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj) zap_clear_flag_locked(zchan->span, ZAP_SPAN_STATE_CHANGE); indicate = 0; state_counter = 0; - zap_log(ZAP_LOG_DEBUG, "Executing state handler for %s\n", zap_channel_state2str(zchan->state)); + zap_log(ZAP_LOG_DEBUG, "Executing state handler on %d:%d for %s\n", + zchan->span_id, zchan->chan_id, + zap_channel_state2str(zchan->state)); switch(zchan->state) { case ZAP_CHANNEL_STATE_UP: { @@ -399,9 +403,9 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj) break; case ZAP_CHANNEL_STATE_DOWN: { - zap_channel_done(zchan); sig.event_id = ZAP_SIGEVENT_STOP; analog_data->sig_cb(&sig); + zap_channel_done(zchan); goto done; } break; @@ -453,18 +457,27 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj) break; case ZAP_CHANNEL_STATE_BUSY: { - zap_channel_done(zchan); - zap_buffer_zero(dt_buffer); - teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_BUSY]); - indicate = 1; + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CIRCUIT_CONGESTION; + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_channel_done(zchan); + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_BUSY]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } } break; case ZAP_CHANNEL_STATE_ATTN: { - zap_channel_done(zchan); - zap_buffer_zero(dt_buffer); - teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_ATTN]); - indicate = 1; + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_channel_done(zchan); + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_ATTN]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } } break; default: @@ -514,25 +527,42 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj) for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { if (zchan->detected_tones[i]) { - zap_log(ZAP_LOG_DEBUG, "Detected tone %s\n", zap_tonemap2str(zchan->detected_tones[i])); + zap_log(ZAP_LOG_DEBUG, "Detected tone %s on %d:%d\n", zap_tonemap2str(i), zchan->span_id, zchan->chan_id); sig.raw_data = &i; - analog_data->sig_cb(&sig); + if (analog_data->sig_cb) { + analog_data->sig_cb(&sig); + } } } - if (zchan->detected_tones[ZAP_TONEMAP_DIAL]) { - zap_channel_command(zchan, ZAP_COMMAND_SEND_DTMF, zchan->caller_data.ani); - state_counter = 0; - zchan->needed_tones[ZAP_TONEMAP_RING] = 1; - zchan->needed_tones[ZAP_TONEMAP_BUSY] = 1; - zchan->needed_tones[ZAP_TONEMAP_FAIL1] = 1; - zchan->needed_tones[ZAP_TONEMAP_FAIL2] = 1; - zchan->needed_tones[ZAP_TONEMAP_FAIL3] = 1; - dial_timeout = (zchan->dtmf_on + zchan->dtmf_off) * strlen(zchan->caller_data.ani) + 50; + if (zchan->detected_tones[ZAP_TONEMAP_BUSY] || + zchan->detected_tones[ZAP_TONEMAP_FAIL1] || + zchan->detected_tones[ZAP_TONEMAP_FAIL2] || + zchan->detected_tones[ZAP_TONEMAP_FAIL3] || + zchan->detected_tones[ZAP_TONEMAP_ATTN] + ) { + zap_log(ZAP_LOG_ERROR, "Failure indication detected!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else if (zchan->detected_tones[ZAP_TONEMAP_DIAL]) { + if (zap_strlen_zero(zchan->caller_data.ani)) { + zap_log(ZAP_LOG_ERROR, "No Digits to send!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + if (zap_channel_command(zchan, ZAP_COMMAND_SEND_DTMF, zchan->caller_data.ani) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Send Digits Failed [%s]\n", zchan->last_error); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + state_counter = 0; + zchan->needed_tones[ZAP_TONEMAP_RING] = 1; + zchan->needed_tones[ZAP_TONEMAP_BUSY] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL1] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL2] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL3] = 1; + dial_timeout = ((zchan->dtmf_on + zchan->dtmf_off) * strlen(zchan->caller_data.ani)) + 3000; + } + } } else if (zchan->detected_tones[ZAP_TONEMAP_RING]) { zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); - } else if (zchan->detected_tones[ZAP_TONEMAP_BUSY]) { - zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); } zap_channel_clear_detected_tones(zchan); @@ -580,6 +610,7 @@ static void *zap_analog_channel_run(zap_thread_t *me, void *obj) } done: + zap_channel_done(zchan); @@ -647,7 +678,10 @@ static __inline__ zap_status_t process_event(zap_span_t *span, zap_event_t *even if (zap_test_flag(event->channel, ZAP_CHANNEL_RINGING)) { zap_channel_command(event->channel, ZAP_COMMAND_GENERATE_RING_OFF, NULL); } - zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + + if (event->channel->state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } } break; case ZAP_OOB_FLASH: diff --git a/libs/freetdm/src/zap_io.c b/libs/freetdm/src/zap_io.c index 2030e5ecc5..30a9771beb 100644 --- a/libs/freetdm/src/zap_io.c +++ b/libs/freetdm/src/zap_io.c @@ -47,6 +47,9 @@ #ifdef ZAP_ZT_SUPPORT #include "zap_zt.h" #endif +#ifdef ZAP_PIKA_SUPPORT +#include "zap_pika.h" +#endif static int time_is_init = 0; @@ -192,12 +195,12 @@ void zap_global_set_default_logger(int level) zap_log_level = level; } -static int equalkeys(void *k1, void *k2) +int zap_hash_equalkeys(void *k1, void *k2) { return strcmp((char *) k1, (char *) k2) ? 0 : 1; } -static uint32_t hashfromstring(void *ky) +uint32_t zap_hash_hashfromstring(void *ky) { unsigned char *str = (unsigned char *) ky; uint32_t hash = 0; @@ -211,6 +214,19 @@ static uint32_t hashfromstring(void *ky) } +static zap_status_t zap_span_destroy(zap_span_t *span) +{ + zap_status_t status = ZAP_FAIL; + + if (zap_test_flag(span, ZAP_SPAN_CONFIGURED) && span->zio && span->zio->span_destroy) { + zap_log(ZAP_LOG_INFO, "Destroying span %u type (%s)\n", span->span_id, span->type); + status = span->zio->span_destroy(span); + zap_safe_free(span->type); + } + + return status; +} + static zap_status_t zap_channel_destroy(zap_channel_t *zchan) { @@ -227,7 +243,7 @@ static zap_status_t zap_channel_destroy(zap_channel_t *zchan) if (zchan->span->zio->channel_destroy) { - zap_log(ZAP_LOG_INFO, "Closing channel %u:%u fd:%d\n", zchan->span_id, zchan->chan_id, zchan->sockfd); + zap_log(ZAP_LOG_INFO, "Closing channel %s:%u:%u fd:%d\n", zchan->span->type, zchan->span_id, zchan->chan_id, zchan->sockfd); if (zchan->span->zio->channel_destroy(zchan) == ZAP_SUCCESS) { zap_clear_flag_locked(zchan, ZAP_CHANNEL_CONFIGURED); } else { @@ -315,14 +331,13 @@ zap_status_t zap_span_close_all(void) uint32_t i, j; zap_mutex_lock(globals.mutex); - for(i = 0; i < globals.span_index; i++) { + for(i = 1; i <= globals.span_index; i++) { span = &globals.spans[i]; - - for(j = 0; j < span->chan_count; j++) { - zap_channel_destroy(&span->channels[i]); - } - - zap_safe_free(span->signal_data); + if (zap_test_flag(span, ZAP_SPAN_CONFIGURED)) { + for(j = 0; j < span->chan_count; j++) { + zap_channel_destroy(&span->channels[i]); + } + } } zap_mutex_unlock(globals.mutex); @@ -938,6 +953,7 @@ static zap_status_t zchan_activate_dtmf_buffer(zap_channel_t *zchan) zap_log(ZAP_LOG_DEBUG, "Created DTMF Buffer!\n"); } } + if (!zchan->tone_session.buffer) { memset(&zchan->tone_session, 0, sizeof(zchan->tone_session)); @@ -947,6 +963,8 @@ static zap_status_t zchan_activate_dtmf_buffer(zap_channel_t *zchan) zchan->tone_session.rate = zchan->rate; zchan->tone_session.duration = zchan->dtmf_on * (zchan->tone_session.rate / 1000); zchan->tone_session.wait = zchan->dtmf_off * (zchan->tone_session.rate / 1000); + zchan->tone_session.volume = -7; + /* zchan->tone_session.debug = 1; zchan->tone_session.debug_stream = stdout; @@ -1083,6 +1101,8 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo case ZAP_COMMAND_ENABLE_PROGRESS_DETECT: { /* if they don't have thier own, use ours */ + zap_channel_clear_detected_tones(zchan); + zap_channel_clear_needed_tones(zchan); teletone_multi_tone_init(&zchan->span->tone_finder[ZAP_TONEMAP_DIAL], &zchan->span->tone_detect_map[ZAP_TONEMAP_DIAL]); teletone_multi_tone_init(&zchan->span->tone_finder[ZAP_TONEMAP_RING], &zchan->span->tone_detect_map[ZAP_TONEMAP_RING]); teletone_multi_tone_init(&zchan->span->tone_finder[ZAP_TONEMAP_BUSY], &zchan->span->tone_detect_map[ZAP_TONEMAP_BUSY]); @@ -1101,7 +1121,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo case ZAP_COMMAND_ENABLE_DTMF_DETECT: { /* if they don't have thier own, use ours */ - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_DETECT)) { zap_tone_type_t tt = ZAP_COMMAND_OBJ_INT; if (tt == ZAP_TONE_DTMF) { teletone_dtmf_detect_init (&zchan->dtmf_detect, zchan->rate); @@ -1117,7 +1137,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo break; case ZAP_COMMAND_DISABLE_DTMF_DETECT: { - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_DETECT)) { zap_tone_type_t tt = ZAP_COMMAND_OBJ_INT; if (tt == ZAP_TONE_DTMF) { teletone_dtmf_detect_init (&zchan->dtmf_detect, zchan->rate); @@ -1132,7 +1152,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo } case ZAP_COMMAND_GET_DTMF_ON_PERIOD: { - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; GOTO_STATUS(done, ZAP_SUCCESS); } @@ -1140,7 +1160,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo break; case ZAP_COMMAND_GET_DTMF_OFF_PERIOD: { - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; GOTO_STATUS(done, ZAP_SUCCESS); } @@ -1148,7 +1168,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo break; case ZAP_COMMAND_SET_DTMF_ON_PERIOD: { - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { int val = ZAP_COMMAND_OBJ_INT; if (val > 10 && val < 1000) { zchan->dtmf_on = val; @@ -1162,7 +1182,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo break; case ZAP_COMMAND_SET_DTMF_OFF_PERIOD: { - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { int val = ZAP_COMMAND_OBJ_INT; if (val > 10 && val < 1000) { zchan->dtmf_off = val; @@ -1176,7 +1196,7 @@ zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, vo break; case ZAP_COMMAND_SEND_DTMF: { - if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF)) { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { char *cur; char *digits = ZAP_COMMAND_OBJ_CHAR_P; int x = 0; @@ -1523,7 +1543,7 @@ zap_status_t zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *data *(str+mlen) = '\0'; zap_copy_string(str, sp, ++mlen); zap_clean_string(str); - zap_log(ZAP_LOG_ERROR, "FSK: TYPE %s LEN %d VAL [%s]\n", zap_mdmf_type2str(type), mlen-1, str); + zap_log(ZAP_LOG_DEBUG, "FSK: TYPE %s LEN %d VAL [%s]\n", zap_mdmf_type2str(type), mlen-1, str); switch(type) { case MDMF_DDN: @@ -1570,13 +1590,14 @@ zap_status_t zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *data if (zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS_DETECT)) { uint32_t i; - + for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { if (zchan->span->tone_finder[i].tone_count) { if (zchan->needed_tones[i] && teletone_multi_tone_detect(&zchan->span->tone_finder[i], sln, (int)slen)) { - zchan->detected_tones[i] = 1; - zchan->needed_tones[i] = 0; - zchan->detected_tones[0]++; + if (++zchan->detected_tones[i]) { + zchan->needed_tones[i] = 0; + zchan->detected_tones[0]++; + } } } } @@ -1713,6 +1734,7 @@ zap_status_t zap_channel_write(zap_channel_t *zchan, void *data, zap_size_t data static struct { zap_io_interface_t *wanpipe_interface; zap_io_interface_t *zt_interface; + zap_io_interface_t *pika_interface; } interfaces; @@ -1760,7 +1782,14 @@ static zap_status_t load_config(void) continue; } + if (!zio->configure_span) { + zap_log(ZAP_LOG_CRIT, "failure creating span, no configure_span method for '%s'\n", type); + span = NULL; + continue; + } + if (zap_span_create(zio, &span) == ZAP_SUCCESS) { + span->type = strdup(type); zap_log(ZAP_LOG_DEBUG, "created span %d of type %s\n", span->span_id, type); d = 0; } else { @@ -1886,7 +1915,7 @@ zap_status_t zap_global_init(void) zap_isdn_init(); memset(&interfaces, 0, sizeof(interfaces)); - globals.interface_hash = create_hashtable(16, hashfromstring, equalkeys); + globals.interface_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); modcount = 0; zap_mutex_create(&globals.mutex); @@ -1914,6 +1943,19 @@ zap_status_t zap_global_init(void) } #endif +#ifdef ZAP_PIKA_SUPPORT + if (pika_init(&interfaces.pika_interface) == ZAP_SUCCESS) { + zap_mutex_lock(globals.mutex); + hashtable_insert(globals.interface_hash, (void *)interfaces.pika_interface->name, interfaces.pika_interface); + process_module_config(interfaces.pika_interface); + zap_mutex_unlock(globals.mutex); + modcount++; + } else { + zap_log(ZAP_LOG_ERROR, "Error initilizing pika.\n"); + } +#endif + + if (!modcount) { zap_log(ZAP_LOG_ERROR, "Error initilizing anything.\n"); return ZAP_FAIL; @@ -1953,6 +1995,10 @@ zap_status_t zap_global_destroy(void) if (cur_span->mutex) { zap_mutex_destroy(&cur_span->mutex); } + + zap_safe_free(cur_span->signal_data); + zap_span_destroy(cur_span); + } } @@ -1962,6 +2008,13 @@ zap_status_t zap_global_destroy(void) zt_destroy(); } #endif + +#ifdef ZAP_PIKA_SUPPORT + if (interfaces.pika_interface) { + pika_destroy(); + } +#endif + #ifdef ZAP_WANPIPE_SUPPORT if (interfaces.wanpipe_interface) { wanpipe_destroy(); diff --git a/libs/freetdm/src/zap_pika.c b/libs/freetdm/src/zap_pika.c new file mode 100644 index 0000000000..48be6f7a3f --- /dev/null +++ b/libs/freetdm/src/zap_pika.c @@ -0,0 +1,1012 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "openzap.h" +#include "zap_pika.h" +#define MAX_NUMBER_OF_TRUNKS 64 +#define PIKA_BLOCK_SIZE 160 +#define PIKA_BLOCK_LEN 20 + +struct pika_channel_profile { + char name[80]; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; +}; +typedef struct pika_channel_profile pika_channel_profile_t; + +static struct { + PKH_TSystemDeviceList board_list; + TPikaHandle open_boards[MAX_NUMBER_OF_TRUNKS]; + TPikaHandle system_handle; + PKH_TSystemConfig system_config; + zap_hash_t *profile_hash; +} globals; + + +struct pika_span_data { + TPikaHandle event_queue; + PKH_TPikaEvent last_oob_event; + uint32_t boardno; +}; +typedef struct pika_span_data pika_span_data_t; + +struct pika_chan_data { + TPikaHandle handle; + TPikaHandle media_in; + TPikaHandle media_out; + TPikaHandle media_in_queue; + TPikaHandle media_out_queue; + PKH_TPikaEvent last_media_event; + PKH_TPikaEvent last_oob_event; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + zap_buffer_t *digit_buffer; + zap_mutex_t *digit_mutex; + int dtmf_len; +}; +typedef struct pika_chan_data pika_chan_data_t; + +static char *pika_board_type_string(PK_UINT type) +{ + if (type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) { + return "digital_gateway"; + } + + if (type == PKH_BOARD_TYPE_ANALOG_GATEWAY) { + return "analog_gateway"; + } + + return "unknown"; +} + +static ZIO_CONFIGURE_FUNCTION(pika_configure) +{ + pika_channel_profile_t *profile = NULL; + int ok = 1; + + if (!(profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) { + profile = malloc(sizeof(*profile)); + memset(profile, 0, sizeof(*profile)); + zap_set_string(profile->name, category); + hashtable_insert(globals.profile_hash, (void *)profile->name, profile); + zap_log(ZAP_LOG_INFO, "creating profile [%s]\n", category); + } + + if (!strcasecmp(var, "rx-gain")) { + profile->record_config.gain = atof(val); + } else if (!strcasecmp(var, "rx-agc-enabled")) { + profile->record_config.AGC.enabled = zap_true(val); + } else if (!strcasecmp(var, "rx-agc-targetPower")) { + profile->record_config.AGC.targetPower = atof(val); + } else if (!strcasecmp(var, "rx-agc-minGain")) { + profile->record_config.AGC.minGain = atof(val); + } else if (!strcasecmp(var, "rx-agc-maxGain")) { + profile->record_config.AGC.maxGain = atof(val); + } else if (!strcasecmp(var, "rx-agc-attackRate")) { + profile->record_config.AGC.attackRate = atoi(val); + } else if (!strcasecmp(var, "rx-agc-decayRate")) { + profile->record_config.AGC.decayRate = atoi(val); + } else if (!strcasecmp(var, "rx-agc-speechThreshold")) { + profile->record_config.AGC.speechThreshold = atof(val); + } else if (!strcasecmp(var, "rx-vad-enabled")) { + profile->record_config.VAD.enabled = zap_true(val); + } else if (!strcasecmp(var, "rx-vad-activationThreshold")) { + profile->record_config.VAD.activationThreshold = atof(val); + } else if (!strcasecmp(var, "rx-vad-activationDebounceTime")) { + profile->record_config.VAD.activationDebounceTime = atoi(val); + } else if (!strcasecmp(var, "rx-vad-deactivationThreshold")) { + profile->record_config.VAD.deactivationThreshold = atof(val); + } else if (!strcasecmp(var, "rx-vad-deactivationDebounceTime")) { + profile->record_config.VAD.deactivationDebounceTime = atoi(val); + } else if (!strcasecmp(var, "rx-vad-preSpeechBufferSize")) { + profile->record_config.VAD.preSpeechBufferSize = atoi(val); + } else if (!strcasecmp(var, "tx-gain")) { + profile->play_config.gain = atof(val); + } else if (!strcasecmp(var, "tx-agc-enabled")) { + profile->play_config.AGC.enabled = zap_true(val); + } else if (!strcasecmp(var, "tx-agc-targetPower")) { + profile->play_config.AGC.targetPower = atof(val); + } else if (!strcasecmp(var, "tx-agc-minGain")) { + profile->play_config.AGC.minGain = atof(val); + } else if (!strcasecmp(var, "tx-agc-maxGain")) { + profile->play_config.AGC.maxGain = atof(val); + } else if (!strcasecmp(var, "tx-agc-attackRate")) { + profile->play_config.AGC.attackRate = atoi(val); + } else if (!strcasecmp(var, "tx-agc-decayRate")) { + profile->play_config.AGC.decayRate = atoi(val); + } else if (!strcasecmp(var, "tx-agc-speechThreshold")) { + profile->play_config.AGC.speechThreshold = atof(val); + } else { + ok = 0; + } + + if (ok) { + zap_log(ZAP_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category); + } else { + zap_log(ZAP_LOG_ERROR, "unknown param [%s]\n", var); + } + + return ZAP_SUCCESS; +} + +PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event) +{ + PK_STATUS pk_status; + zap_channel_t *zchan = event->userData; + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + + //PK_CHAR g_EventText[PKH_EVENT_MAX_NAME_LENGTH]; + //PKH_EVENT_GetText(event->id, g_EventText, sizeof(g_EventText)); + //zap_log(ZAP_LOG_DEBUG, "Event: %s\n", g_EventText); + + switch (event->id) { + case PKH_EVENT_PLAY_IDLE: + { + while (zap_buffer_inuse(chan_data->digit_buffer)) { + char dtmf[128] = ""; + zap_mutex_lock(chan_data->digit_mutex); + chan_data->dtmf_len = zap_buffer_read(chan_data->digit_buffer, dtmf, sizeof(dtmf)); + pk_status = PKH_TG_PlayDTMF(chan_data->media_out, dtmf); + zap_mutex_unlock(chan_data->digit_mutex); + } + } + break; + case PKH_EVENT_TG_TONE_PLAYED: + { + + if (!event->p1) { + zap_mutex_lock(chan_data->digit_mutex); + PKH_PLAY_Start(chan_data->media_out); + chan_data->dtmf_len = 0; + zap_mutex_unlock(chan_data->digit_mutex); + } + + + } + break; + default: + break; + } + +} + + +static unsigned pika_open_range(zap_span_t *span, unsigned boardno, unsigned spanno, unsigned start, unsigned end, + zap_chan_type_t type, char *name, char *number, pika_channel_profile_t *profile) +{ + unsigned configured = 0, x; + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + pika_span_data_t *span_data; + + if (boardno >= globals.board_list.numberOfBoards) { + zap_log(ZAP_LOG_ERROR, "Board %u is not present!\n", boardno); + return 0; + } + + if (!globals.open_boards[boardno]) { + status = PKH_BOARD_Open(globals.board_list.board[boardno].id, + NULL, + &globals.open_boards[boardno]); + if(status != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_BOARD_Open %d failed(%s)!\n", boardno, + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return 0; + } + + zap_log(ZAP_LOG_DEBUG, "Open board %u\n", boardno); + + //PKH_BOARD_SetDebugTrace(globals.open_boards[boardno], 1, 0); + + } + + if (!span->mod_data) { + span_data = malloc(sizeof(*span_data)); + assert(span_data != NULL); + memset(span_data, 0, sizeof(*span_data)); + span_data->boardno = boardno; + + status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &span_data->event_queue); + + if (status != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_QUEUE_Create failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + free(span_data); + return 0; + } + + PKH_QUEUE_Attach(span_data->event_queue, globals.open_boards[boardno], NULL); + + span->mod_data = span_data; + } + start--; + end--; + for(x = start; x < end; x++) { + zap_channel_t *chan; + pika_chan_data_t *chan_data = NULL; + + chan_data = malloc(sizeof *chan_data); + assert(chan_data); + memset(chan_data, 0, sizeof(*chan_data)); + + if (type == ZAP_CHAN_TYPE_FXO) { + PKH_TTrunkConfig trunkConfig; + + if((status = PKH_TRUNK_Open(globals.open_boards[boardno], x, &chan_data->handle)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_TRUNK_Seize(chan_data->handle) != PK_SUCCESS)) { + goto fail_fxo; + } + + if (zap_span_add_channel(span, 0, type, &chan) != ZAP_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_TRUNK_GetConfig(chan_data->handle, &trunkConfig) != PK_SUCCESS)) { + goto fail_fxo; + } + + trunkConfig.internationalControl = PKH_TRUNK_NA; + trunkConfig.audioFormat = PKH_AUDIO_MULAW; + + if ((status = PKH_TRUNK_SetConfig(chan_data->handle, &trunkConfig) != PK_SUCCESS)) { + goto fail_fxo; + } + + if ((status = PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_TRUNK_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out)) != ZAP_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan)) != PK_SUCCESS) { + goto fail_fxo; + } + + if ((status = PKH_TRUNK_Start(chan_data->handle)) != PK_SUCCESS) { + goto fail_fxo; + } + + goto ok; + + fail_fxo: + zap_log(ZAP_LOG_ERROR, "failure configuring device s%dc%d\n", spanno, x); + zap_log(ZAP_LOG_ERROR, "Error: PKH_TRUNK_Open %u failed(%s)!\n", x, + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + if (chan_data->handle) { + PKH_TRUNK_Close(chan_data->handle); + } + PKH_QUEUE_Destroy(chan_data->media_in_queue); + + free(chan_data); + continue; + + } else if (type == ZAP_CHAN_TYPE_FXS) { + if((status = PKH_PHONE_Open(globals.open_boards[boardno], x, &chan_data->handle)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_PHONE_Seize(chan_data->handle) != PK_SUCCESS)) { + goto fail_fxs; + } + + if (zap_span_add_channel(span, 0, type, &chan) != ZAP_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_PHONE_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out)) != ZAP_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan)) != PK_SUCCESS) { + goto fail_fxs; + } + + if ((status = PKH_PHONE_Start(chan_data->handle)) != PK_SUCCESS) { + goto fail_fxs; + } + + goto ok; + + + fail_fxs: + zap_log(ZAP_LOG_ERROR, "failure configuring device s%dc%d\n", spanno, x); + zap_log(ZAP_LOG_ERROR, "Error: PKH_PHONE_Open %u failed(%s)!\n", x, + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + if (chan_data->handle) { + PKH_PHONE_Close(chan_data->handle); + } + PKH_QUEUE_Destroy(chan_data->media_in_queue); + free(chan_data); + + + } + + ok: + + status = PKH_RECORD_GetConfig(chan_data->media_in, &chan_data->record_config); + chan_data->record_config.encoding = PKH_RECORD_ENCODING_MU_LAW; + chan_data->record_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ; + chan_data->record_config.bufferSize = PIKA_BLOCK_SIZE; + chan_data->record_config.numberOfBuffers = chan_data->record_config.bufferSize; + chan_data->record_config.VAD.enabled = PK_FALSE; + //chan_data->record_config.speechSegmentEventsEnabled = PK_FALSE; + //chan_data->record_config.gain = rxgain; + + + + status = PKH_PLAY_GetConfig(chan_data->media_out, &chan_data->play_config); + chan_data->play_config.encoding = PKH_RECORD_ENCODING_MU_LAW; + chan_data->play_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ; + chan_data->play_config.AGC.enabled = PK_FALSE; + zap_log(ZAP_LOG_INFO, "configuring device b%ds%dc%d as OpenZAP device %d:%d\n", boardno, spanno, x, chan->span_id, chan->chan_id); + + if (profile) { + zap_log(ZAP_LOG_INFO, "applying config profile %s to device %d:%d\n", profile->name, chan->span_id, chan->chan_id); + chan_data->record_config.gain = profile->record_config.gain; + chan_data->record_config.AGC.enabled = profile->record_config.AGC.enabled; + chan_data->record_config.AGC.targetPower = profile->record_config.AGC.targetPower; + chan_data->record_config.AGC.minGain = profile->record_config.AGC.minGain; + chan_data->record_config.AGC.maxGain = profile->record_config.AGC.maxGain; + chan_data->record_config.AGC.attackRate = profile->record_config.AGC.attackRate; + chan_data->record_config.AGC.decayRate = profile->record_config.AGC.decayRate; + chan_data->record_config.AGC.speechThreshold = profile->record_config.AGC.speechThreshold; + chan_data->record_config.VAD.enabled = profile->record_config.VAD.enabled; + chan_data->record_config.VAD.activationThreshold = profile->record_config.VAD.activationThreshold; + chan_data->record_config.VAD.activationDebounceTime = profile->record_config.VAD.activationDebounceTime; + chan_data->record_config.VAD.deactivationThreshold = profile->record_config.VAD.deactivationThreshold; + chan_data->record_config.VAD.deactivationDebounceTime = profile->record_config.VAD.deactivationDebounceTime; + chan_data->record_config.VAD.preSpeechBufferSize = profile->record_config.VAD.preSpeechBufferSize; + chan_data->play_config.gain = profile->play_config.gain; + chan_data->play_config.AGC.enabled = profile->play_config.AGC.enabled; + chan_data->play_config.AGC.targetPower = profile->play_config.AGC.targetPower; + chan_data->play_config.AGC.minGain = profile->play_config.AGC.minGain; + chan_data->play_config.AGC.maxGain = profile->play_config.AGC.maxGain; + chan_data->play_config.AGC.attackRate = profile->play_config.AGC.attackRate; + chan_data->play_config.AGC.decayRate = profile->play_config.AGC.decayRate; + chan_data->play_config.AGC.speechThreshold = profile->play_config.AGC.speechThreshold; + } + + status = PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config); + status = PKH_PLAY_SetConfig(chan_data->media_out, &chan_data->play_config); + + + chan->mod_data = chan_data; + chan->physical_span_id = spanno; + chan->physical_chan_id = x; + + chan->rate = 8000; + chan->packet_len = chan_data->record_config.bufferSize; + chan->effective_interval = chan->native_interval = chan->packet_len / 8; + + PKH_RECORD_Start(chan_data->media_in); + PKH_PLAY_Start(chan_data->media_out); + + PKH_EC_Start(chan_data->media_in, chan_data->media_in, chan_data->media_out); + + if (!zap_strlen_zero(name)) { + zap_copy_string(chan->chan_name, name, sizeof(chan->chan_name)); + } + + if (!zap_strlen_zero(number)) { + zap_copy_string(chan->chan_number, number, sizeof(chan->chan_number)); + } + + zap_channel_set_feature(chan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE); + zap_buffer_create(&chan_data->digit_buffer, 128, 128, 0); + zap_mutex_create(&chan_data->digit_mutex); + + configured++; + } + + + return configured; +} + +static ZIO_CONFIGURE_SPAN_FUNCTION(pika_configure_span) +{ + int items, i; + char *mydata, *item_list[10]; + char *bd, *sp, *ch, *mx; + int boardno; + int channo; + int spanno; + int top = 0; + unsigned configured = 0; + char *profile_name = NULL; + pika_channel_profile_t *profile = NULL; + + assert(str != NULL); + + mydata = strdup(str); + assert(mydata != NULL); + + if ((profile_name = strchr(mydata, '@'))) { + *profile_name++ = '\0'; + if (!zap_strlen_zero(profile_name)) { + profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)profile_name); + } + } + + items = zap_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + bd = item_list[i]; + if ((sp = strchr(bd, ':'))) { + *sp++ = '\0'; + } + + if ((ch = strchr(sp, ':'))) { + *ch++ = '\0'; + } + + if (!(bd && sp && ch)) { + zap_log(ZAP_LOG_ERROR, "Invalid input\n"); + continue; + } + + boardno = atoi(bd); + channo = atoi(ch); + spanno = atoi(sp); + + + if (boardno < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid board number %d\n", boardno); + continue; + } + + if (channo < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if (spanno < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid span number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + + configured += pika_open_range(span, boardno, spanno, channo, top, type, name, number, profile); + + } + + free(mydata); + + return configured; +} + +static ZIO_OPEN_FUNCTION(pika_open) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + PKH_QUEUE_Flush(chan_data->media_in_queue); + PKH_PLAY_Start(chan_data->media_out); + return ZAP_SUCCESS; +} + +static ZIO_CLOSE_FUNCTION(pika_close) +{ + return ZAP_SUCCESS; +} + +static ZIO_WAIT_FUNCTION(pika_wait) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + PK_STATUS status; + zap_wait_flag_t myflags = *flags; + PK_CHAR g_EventText[PKH_EVENT_MAX_NAME_LENGTH]; + + *flags = ZAP_NO_FLAGS; + + if (myflags & ZAP_READ) { + status = PKH_QUEUE_WaitOnEvent(chan_data->media_in_queue, to, &chan_data->last_media_event); + + if (status == PK_SUCCESS) { + if (chan_data->last_media_event.id == PKH_EVENT_QUEUE_TIMEOUT || chan_data->last_media_event.id == PKH_EVENT_RECORD_BUFFER_OVERFLOW) { + return ZAP_TIMEOUT; + } + + *flags |= ZAP_READ; + return ZAP_SUCCESS; + } + + PKH_EVENT_GetText(chan_data->last_media_event.id, g_EventText, sizeof(g_EventText)); + zap_log(ZAP_LOG_DEBUG, "Event: %s\n", g_EventText); + } + + return ZAP_SUCCESS; +} + +static ZIO_READ_FUNCTION(pika_read) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + PK_STATUS status; + PK_CHAR g_EventText[PKH_EVENT_MAX_NAME_LENGTH]; + + if (zchan->packet_len < *datalen) { + *datalen = zchan->packet_len; + } + + if ((status = PKH_RECORD_GetData(chan_data->media_in, data, *datalen)) == PK_SUCCESS) { + return ZAP_SUCCESS; + } + + + PKH_ERROR_GetText(status, g_EventText, sizeof(g_EventText)); + zap_log(ZAP_LOG_DEBUG, "ERR: %s\n", g_EventText); + return ZAP_FAIL; +} + +static ZIO_WRITE_FUNCTION(pika_write) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + + if (PKH_PLAY_AddData(chan_data->media_out, 0, data, *datalen) == PK_SUCCESS) { + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +static ZIO_COMMAND_FUNCTION(pika_command) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + //pika_span_data_t *span_data = (pika_span_data_t *) zchan->span->mod_data; + PK_STATUS pk_status; + zap_status_t status = ZAP_SUCCESS; + + switch(command) { + case ZAP_COMMAND_OFFHOOK: + { + if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_OFFHOOK)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_set_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + } + break; + case ZAP_COMMAND_ONHOOK: + { + if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_ONHOOK)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + } + break; + case ZAP_COMMAND_GENERATE_RING_ON: + { + if ((pk_status = PKH_PHONE_RingStart(chan_data->handle, 0, 0)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_set_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + } + break; + case ZAP_COMMAND_GENERATE_RING_OFF: + { + if ((pk_status = PKH_PHONE_RingStop(chan_data->handle)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + } + break; + case ZAP_COMMAND_GET_INTERVAL: + { + + ZAP_COMMAND_OBJ_INT = zchan->native_interval; + + } + break; + case ZAP_COMMAND_SET_INTERVAL: + { + int interval = ZAP_COMMAND_OBJ_INT; + int len = interval * 8; + chan_data->record_config.bufferSize = len; + chan_data->record_config.numberOfBuffers = chan_data->record_config.bufferSize; + zchan->packet_len = chan_data->record_config.bufferSize; + zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8; + PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config); + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_GET_DTMF_ON_PERIOD: + { + + ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; + GOTO_STATUS(done, ZAP_SUCCESS); + + } + break; + case ZAP_COMMAND_GET_DTMF_OFF_PERIOD: + { + ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_SET_DTMF_ON_PERIOD: + { + int val = ZAP_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + zchan->dtmf_on = val; + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, ZAP_FAIL); + } + } + break; + case ZAP_COMMAND_SET_DTMF_OFF_PERIOD: + { + int val = ZAP_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + zchan->dtmf_off = val; + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, ZAP_FAIL); + } + } + break; + case ZAP_COMMAND_SEND_DTMF: + { + char *digits = ZAP_COMMAND_OBJ_CHAR_P; + zap_log(ZAP_LOG_DEBUG, "Adding DTMF SEQ [%s]\n", digits); + zap_mutex_lock(chan_data->digit_mutex); + zap_buffer_write(chan_data->digit_buffer, digits, strlen(digits)); + zap_mutex_unlock(chan_data->digit_mutex); + pk_status = PKH_PLAY_Stop(chan_data->media_out); + + if (pk_status != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + default: + break; + }; + + done: + return status; +} + +static ZIO_SPAN_POLL_EVENT_FUNCTION(pika_poll_event) +{ + pika_span_data_t *span_data = (pika_span_data_t *) span->mod_data; + PK_STATUS status; + + status = PKH_QUEUE_WaitOnEvent(span_data->event_queue, ms, &span_data->last_oob_event); + + if (status == PK_SUCCESS) { + zap_channel_t *zchan = span_data->last_oob_event.userData; + //PK_CHAR g_EventText[PKH_EVENT_MAX_NAME_LENGTH]; + + if (span_data->last_oob_event.id == PKH_EVENT_QUEUE_TIMEOUT) { + return ZAP_TIMEOUT; + } + + //PKH_EVENT_GetText(span_data->last_oob_event.id, g_EventText, sizeof(g_EventText)); + //zap_log(ZAP_LOG_DEBUG, "Event: %s\n", g_EventText); + + if (zchan) { + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + zap_set_flag(zchan, ZAP_CHANNEL_EVENT); + zchan->last_event_time = zap_current_time_in_ms(); + chan_data->last_oob_event = span_data->last_oob_event; + } + + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +static ZIO_SPAN_NEXT_EVENT_FUNCTION(pika_next_event) +{ + uint32_t i, event_id; + + for(i = 1; i <= span->chan_count; i++) { + if (zap_test_flag((&span->channels[i]), ZAP_CHANNEL_EVENT)) { + pika_chan_data_t *chan_data = (pika_chan_data_t *) span->channels[i].mod_data; + PK_CHAR g_EventText[PKH_EVENT_MAX_NAME_LENGTH]; + + zap_clear_flag((&span->channels[i]), ZAP_CHANNEL_EVENT); + + PKH_EVENT_GetText(chan_data->last_oob_event.id, g_EventText, sizeof(g_EventText)); + zap_log(ZAP_LOG_DEBUG, "Event %d on channel %d:%d [%s]\n", + chan_data->last_oob_event.id, + span->channels[i].span_id, + span->channels[i].chan_id, + g_EventText); + + switch(chan_data->last_oob_event.id) { + case PKH_EVENT_TRUNK_HOOKFLASH: + event_id = ZAP_OOB_FLASH; + break; + case PKH_EVENT_TRUNK_RING_OFF: + event_id = ZAP_OOB_RING_STOP; + break; + case PKH_EVENT_TRUNK_RING_ON: + event_id = ZAP_OOB_RING_START; + break; + + case PKH_EVENT_PHONE_OFFHOOK: + zap_set_flag_locked((&span->channels[i]), ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_OFFHOOK; + break; + + case PKH_EVENT_TRUNK_BELOW_THRESHOLD: + case PKH_EVENT_TRUNK_ABOVE_THRESHOLD: + case PKH_EVENT_PHONE_ONHOOK: + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_ONHOOK; + break; + + case PKH_EVENT_TRUNK_ONHOOK: + case PKH_EVENT_TRUNK_OFFHOOK: + case PKH_EVENT_TRUNK_DIALED : + case PKH_EVENT_TRUNK_REVERSAL: + case PKH_EVENT_TRUNK_LCSO: + case PKH_EVENT_TRUNK_DROPOUT: + case PKH_EVENT_TRUNK_LOF: + case PKH_EVENT_TRUNK_RX_OVERLOAD: + default: + zap_log(ZAP_LOG_DEBUG, "Unhandled event %d on channel %d [%s]\n", chan_data->last_oob_event.id, i, g_EventText); + event_id = ZAP_OOB_INVALID; + break; + } + + span->channels[i].last_event_time = 0; + span->event_header.e_type = ZAP_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = &span->channels[i]; + *event = &span->event_header; + return ZAP_SUCCESS; + } + } + + return ZAP_FAIL; +} + +static ZIO_SPAN_DESTROY_FUNCTION(pika_span_destroy) +{ + pika_span_data_t *span_data = (pika_span_data_t *) span->mod_data; + + if (span_data) { + PKH_QUEUE_Destroy(span_data->event_queue); + free(span_data); + } + + return ZAP_SUCCESS; +} + +static ZIO_CHANNEL_DESTROY_FUNCTION(pika_channel_destroy) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + pika_span_data_t *span_data = (pika_span_data_t *) zchan->span->mod_data; + + if (zchan->type == ZAP_CHAN_TYPE_FXS || zchan->type == ZAP_CHAN_TYPE_FXO) { + PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle); + PKH_TRUNK_Close(chan_data->handle); + } + + PKH_RECORD_Stop(chan_data->media_in); + PKH_PLAY_Stop(chan_data->media_out); + PKH_QUEUE_Destroy(chan_data->media_in_queue); + PKH_QUEUE_Destroy(chan_data->media_out_queue); + zap_mutex_destroy(&chan_data->digit_mutex); + zap_buffer_destroy(&chan_data->digit_buffer); + zap_safe_free(chan_data); + + return ZAP_SUCCESS; +} + +static ZIO_GET_ALARMS_FUNCTION(pika_get_alarms) +{ + return ZAP_FAIL; +} + +static zap_io_interface_t pika_interface; + +zap_status_t pika_init(zap_io_interface_t **zint) +{ + + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + uint32_t i; + int ok = 0; + PKH_TLogMasks m; + + + assert(zint != NULL); + memset(&pika_interface, 0, sizeof(pika_interface)); + memset(&globals, 0, sizeof(globals)); + + globals.profile_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + + // Open the system object, to enumerate boards configured for this system + if ((status = PKH_SYSTEM_Open(&globals.system_handle)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_SYSTEM_Open failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return ZAP_FAIL; + } + + // Retrieves a list of all boards in this system, existing, + // or listed in pika.cfg + if ((status = PKH_SYSTEM_Detect(globals.system_handle, &globals.board_list)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_SYSTEM_Detect failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return ZAP_FAIL; + } + + PKH_SYSTEM_GetConfig(globals.system_handle, &globals.system_config); + globals.system_config.maxAudioProcessBlockSize = PIKA_BLOCK_LEN; + globals.system_config.playBufferSize = PIKA_BLOCK_SIZE; + globals.system_config.recordBufferSize = PIKA_BLOCK_SIZE; + globals.system_config.recordNumberOfBuffers = 8; + PKH_SYSTEM_SetConfig(globals.system_handle, &globals.system_config); + + zap_log(ZAP_LOG_DEBUG, "Found %u board%s\n", globals.board_list.numberOfBoards, globals.board_list.numberOfBoards == 1 ? "" : "s"); + for(i = 0; i < globals.board_list.numberOfBoards; ++i) { + zap_log(ZAP_LOG_INFO, "Found PIKA board type:[%s] id:[%u] serno:[%u]\n", + pika_board_type_string(globals.board_list.board[i].type), globals.board_list.board[i].id, (uint32_t) + globals.board_list.board[i].serialNumber); + + ok++; + + } + + if (!ok) { + return ZAP_FAIL; + } + + pika_interface.name = "pika"; + pika_interface.configure = pika_configure; + pika_interface.configure_span = pika_configure_span; + pika_interface.open = pika_open; + pika_interface.close = pika_close; + pika_interface.wait = pika_wait; + pika_interface.read = pika_read; + pika_interface.write = pika_write; + pika_interface.command = pika_command; + pika_interface.poll_event = pika_poll_event; + pika_interface.next_event = pika_next_event; + pika_interface.channel_destroy = pika_channel_destroy; + pika_interface.span_destroy = pika_span_destroy; + pika_interface.get_alarms = pika_get_alarms; + *zint = &pika_interface; + + + + + memset(&m, 0, sizeof(m)); + //m.apiMask = 0xffffffff; + //PKH_LOG_SetMasks(&m); + + return ZAP_SUCCESS; +} + +zap_status_t pika_destroy(void) +{ + uint32_t x; + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + + for (x = 0; x < MAX_NUMBER_OF_TRUNKS; x++) { + if (globals.open_boards[x]) { + zap_log(ZAP_LOG_INFO, "Closing board %u\n", x); + PKH_BOARD_Close(globals.open_boards[x]); + } + } + + // The system can now be closed. + if ((status = PKH_SYSTEM_Close(globals.system_handle)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_SYSTEM_Close failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + } + + hashtable_destroy(globals.profile_hash, 0, 1); + + return ZAP_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ + diff --git a/libs/freetdm/src/zap_skel.c b/libs/freetdm/src/zap_skel.c index 992ca264e1..575550979a 100644 --- a/libs/freetdm/src/zap_skel.c +++ b/libs/freetdm/src/zap_skel.c @@ -82,6 +82,11 @@ static ZIO_WRITE_FUNCTION(skel_write) return ZAP_FAIL; } +static ZIO_COMMAND_FUNCTION(skel_command) +{ + return ZAP_FAIL; +} + static ZIO_SPAN_POLL_EVENT_FUNCTION(skel_poll_event) { return ZAP_FAIL; @@ -97,6 +102,11 @@ static ZIO_CHANNEL_DESTROY_FUNCTION(skel_channel_destroy) return ZAP_FAIL; } +static ZIO_CHANNEL_DESTROY_FUNCTION(skel_span_destroy) +{ + return ZAP_FAIL; +} + static ZIO_GET_ALARMS_FUNCTION(skel_get_alarms) { return zap_fail; @@ -117,9 +127,11 @@ zap_status_t skel_init(zap_io_interface_t **zint) skel_interface.wait = skel_wait; skel_interface.read = skel_read; skel_interface.write = skel_write; + skel_interface.command = skel_command; skel_interface.poll_event = skel_poll_event; skel_interface.next_event = skel_next_event; skel_interface.channel_destroy = skel_channel_destroy; + skel_interface.span_destroy = skel_span_destroy; skel_interface.get_alarms = skel_get_alarms; *zint = &skel_interface;