diff --git a/libs/freetdm/src/Makefile b/libs/freetdm/src/Makefile index 8041ea3a7d..8e9ba7b093 100644 --- a/libs/freetdm/src/Makefile +++ b/libs/freetdm/src/Makefile @@ -73,6 +73,9 @@ testapp: testapp.c $(MYLIB) testisdn: testisdn.c $(MYLIB) $(CC) $(INCS) -L. testisdn.c -o testisdn -lopenzap -lm -lpthread +testanalog: testanalog.c $(MYLIB) + $(CC) $(INCS) -L. testanalog.c -o testanalog -lopenzap -lm -lpthread + priserver.o: priserver.c $(CC) $(INCS) $(TMP) -c priserver.c -o priserver.o @@ -95,5 +98,5 @@ zap_zt.o: zap_zt.c $(CC) $(CC_CFLAGS) $(CFLAGS) -c $< -o $@ clean: - rm -f *.o isdn/*.o $(MYLIB) *~ \#* testapp priserver + rm -f *.o isdn/*.o $(MYLIB) *~ \#* testapp priserver testisdn testanalog diff --git a/libs/freetdm/src/include/openzap.h b/libs/freetdm/src/include/openzap.h index 5a0f74fafa..8235d30846 100644 --- a/libs/freetdm/src/include/openzap.h +++ b/libs/freetdm/src/include/openzap.h @@ -74,6 +74,11 @@ #pragma warning(disable:4706) #endif +#ifndef WIN32 +#include +#include +#endif + #include #include #include @@ -91,6 +96,8 @@ #include "Q931.h" #include "Q921.h" +#define XX if (0) + #ifdef NDEBUG #undef assert #define assert(_Expression) ((void)(_Expression)) @@ -131,6 +138,11 @@ */ #define zap_set_flag(obj, flag) (obj)->flags |= (flag) +#define zap_set_flag_locked(obj, flag) assert(obj->mutex != NULL);\ +zap_mutex_lock(obj->mutex);\ +(obj)->flags |= (flag);\ +zap_mutex_unlock(obj->mutex); + /*! \brief Clear a flag on an arbitrary object while locked \command obj the object to test @@ -138,6 +150,12 @@ */ #define zap_clear_flag(obj, flag) (obj)->flags &= ~(flag) +#define zap_clear_flag_locked(obj, flag) assert(obj->mutex != NULL); zap_mutex_lock(obj->mutex); (obj)->flags &= ~(flag); zap_mutex_unlock(obj->mutex); + +#define zap_set_state_locked(obj, s) assert(obj->mutex != NULL); zap_mutex_lock(obj->mutex); obj->state = s; zap_mutex_unlock(obj->mutex); + +#define zap_is_dtmf(key) ((key > 47 && key < 58) || (key > 64 && key < 69) || (key > 96 && key < 101) || key == 35 || key == 42 || key == 87 || key == 119) + /*! \brief Copy flags from one arbitrary object to another \command dest the object to copy the flags to @@ -157,6 +175,8 @@ struct zap_event { zap_event_type_t e_type; + uint32_t enum_id; + zap_channel_t *channel; void *data; }; @@ -172,6 +192,8 @@ struct zap_channel { uint32_t effective_interval; uint32_t native_interval; uint32_t packet_len; + zap_channel_state_t state; + zap_mutex_t *mutex; teletone_dtmf_detect_state_t dtmf_detect; zap_event_t event_header; char last_error[256]; @@ -179,9 +201,11 @@ struct zap_channel { void *mod_data; uint32_t skip_read_frames; zap_buffer_t *dtmf_buffer; + zap_buffer_t *digit_buffer; uint32_t dtmf_on; uint32_t dtmf_off; teletone_generation_session_t tone_session; + zap_time_t last_event_time; struct zap_span *span; struct zap_io_interface *zio; }; @@ -207,6 +231,10 @@ struct zap_isdn_data { uint32_t flags; }; +struct zap_analog_data { + uint32_t flags; +}; + struct zap_span { uint32_t span_id; uint32_t chan_count; @@ -217,6 +245,8 @@ struct zap_span { zap_trunk_type_t trunk_type; zap_signal_type_t signal_type; struct zap_isdn_data *isdn_data; + struct zap_analog_data *analog_data; + zap_event_t event_header; char last_error[256]; zap_channel_t channels[ZAP_MAX_CHANNELS_SPAN]; }; @@ -233,12 +263,21 @@ struct zap_io_interface { zio_wait_t wait; zio_read_t read; zio_write_t write; + zio_span_poll_event_t poll_event; + zio_span_next_event_t next_event; uint32_t span_index; struct zap_span spans[ZAP_MAX_SPANS_INTERFACE]; }; +zap_size_t zap_channel_dequeue_dtmf(zap_channel_t *zchan, char *dtmf, zap_size_t len); +zap_status_t zap_channel_queue_dtmf(zap_channel_t *zchan, const char *dtmf); +zap_time_t zap_current_time_in_ms(void); +zap_oob_event_t str2zap_oob_signal(char *name); +char *zap_oob_signal2str(zap_oob_event_t type); zap_trunk_type_t str2zap_trunk_type(char *name); char *zap_trunk_type2str(zap_trunk_type_t type); +zap_status_t zap_span_poll_event(zap_span_t *span, uint32_t ms); +zap_status_t zap_span_next_event(zap_span_t *span, zap_event_t **event); zap_status_t zap_span_find(const char *name, uint32_t id, zap_span_t **span); zap_status_t zap_span_create(zap_io_interface_t *zio, zap_span_t **span); zap_status_t zap_span_close_all(zap_io_interface_t *zio); @@ -246,6 +285,7 @@ zap_status_t zap_span_add_channel(zap_span_t *span, zap_socket_t sockfd, zap_cha zap_status_t zap_span_set_event_callback(zap_span_t *span, zio_event_cb_t event_callback); zap_status_t zap_channel_set_event_callback(zap_channel_t *zchan, zio_event_cb_t event_callback); zap_status_t zap_channel_open(const char *name, uint32_t span_id, uint32_t chan_id, zap_channel_t **zchan); +zap_status_t zap_channel_open_chan(zap_channel_t *zchan); zap_status_t zap_channel_open_any(const char *name, uint32_t span_id, zap_direction_t direction, zap_channel_t **zchan); zap_status_t zap_channel_close(zap_channel_t **zchan); zap_status_t zap_channel_command(zap_channel_t *zchan, zap_command_t command, void *obj); diff --git a/libs/freetdm/src/include/zap_analog.h b/libs/freetdm/src/include/zap_analog.h new file mode 100644 index 0000000000..8c12797bf2 --- /dev/null +++ b/libs/freetdm/src/include/zap_analog.h @@ -0,0 +1,48 @@ +/* + * 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_ANALOG_H +#define ZAP_ANALOG_H +#include "openzap.h" + +typedef enum { + ZAP_ANALOG_RUNNING = (1 << 0) +} zap_analog_flag_t; + + +typedef struct zap_analog_data zap_analog_data_t; + +zap_status_t zap_analog_start(zap_span_t *span); +zap_status_t zap_analog_configure_span(zap_span_t *span, zio_signal_cb_t sig_cb); + +#endif diff --git a/libs/freetdm/src/include/zap_types.h b/libs/freetdm/src/include/zap_types.h index ee958c6bfb..03d6ba8510 100644 --- a/libs/freetdm/src/include/zap_types.h +++ b/libs/freetdm/src/include/zap_types.h @@ -50,6 +50,8 @@ struct zap_io_interface; #define ZAP_COMMAND_OBJ_INT *((int *)obj) #define ZAP_COMMAND_OBJ_CHAR_P (char *)obj +typedef uint64_t zap_time_t; + typedef enum { ZAP_TRUNK_E1, ZAP_TRUNK_T1, @@ -79,6 +81,7 @@ typedef enum { typedef enum { ZAP_EVENT_NONE, ZAP_EVENT_DTMF, + ZAP_EVENT_OOB, ZAP_EVENT_COUNT } zap_event_type_t; @@ -143,7 +146,6 @@ typedef enum { ZAP_CHAN_TYPE_DQ931, ZAP_CHAN_TYPE_FXS, ZAP_CHAN_TYPE_FXO, - ZAP_CHAN_TYPE_COUNT } zap_chan_type_t; @@ -153,6 +155,13 @@ typedef enum { ZAP_CHANNEL_FEATURE_INTERVAL = (1 << 2) } zap_channel_feature_t; +typedef enum { + ZAP_CHANNEL_STATE_DOWN, + ZAP_CHANNEL_STATE_UP, + ZAP_CHANNEL_STATE_DIALTONE, + ZAP_CHANNEL_STATE_COLLECT +} zap_channel_state_t; + typedef enum { ZAP_CHANNEL_CONFIGURED = (1 << 0), ZAP_CHANNEL_READY = (1 << 1), @@ -160,14 +169,31 @@ typedef enum { ZAP_CHANNEL_DTMF_DETECT = (1 << 3), ZAP_CHANNEL_SUPRESS_DTMF = (1 << 4), ZAP_CHANNEL_TRANSCODE = (1 << 5), - ZAP_CHANNEL_BUFFER = (1 << 6) + ZAP_CHANNEL_BUFFER = (1 << 6), + ZAP_CHANNEL_EVENT = (1 << 7), + ZAP_CHANNEL_INTHREAD = (1 << 8), + ZAP_CHANNEL_WINK = (1 << 9), + ZAP_CHANNEL_FLASH = (1 << 10) } zap_channel_flag_t; +typedef enum { + ZAP_OOB_DTMF, + ZAP_OOB_ONHOOK, + ZAP_OOB_OFFHOOK, + ZAP_OOB_WINK, + ZAP_OOB_FLASH, + ZAP_OOB_RING_START, + ZAP_OOB_RING_STOP, + ZAP_OOB_INVALID +} zap_oob_event_t; + typedef struct zap_channel zap_channel_t; typedef struct zap_event zap_event_t; typedef struct zap_sigmsg zap_sigmsg_t; typedef struct zap_span zap_span_t; +#define ZIO_SPAN_POLL_EVENT_ARGS (zap_span_t *span, uint32_t ms) +#define ZIO_SPAN_NEXT_EVENT_ARGS (zap_span_t *span, zap_event_t **event) #define ZIO_SIGNAL_CB_ARGS (zap_span_t *span, zap_sigmsg_t *sigmsg, void *raw_data, uint32_t raw_data_len) #define ZIO_EVENT_CB_ARGS (zap_channel_t *zchan, zap_event_t *event) #define ZIO_CODEC_ARGS (void *data, zap_size_t max, zap_size_t *datalen) @@ -179,6 +205,8 @@ typedef struct zap_span zap_span_t; #define ZIO_READ_ARGS (zap_channel_t *zchan, void *data, zap_size_t *datalen) #define ZIO_WRITE_ARGS (zap_channel_t *zchan, void *data, zap_size_t *datalen) +typedef zap_status_t (*zio_span_poll_event_t) ZIO_SPAN_POLL_EVENT_ARGS ; +typedef zap_status_t (*zio_span_next_event_t) ZIO_SPAN_NEXT_EVENT_ARGS ; typedef zap_status_t (*zio_signal_cb_t) ZIO_SIGNAL_CB_ARGS ; typedef zap_status_t (*zio_event_cb_t) ZIO_EVENT_CB_ARGS ; typedef zap_status_t (*zio_codec_t) ZIO_CODEC_ARGS ; @@ -190,6 +218,8 @@ typedef zap_status_t (*zio_wait_t) ZIO_WAIT_ARGS ; typedef zap_status_t (*zio_read_t) ZIO_READ_ARGS ; typedef zap_status_t (*zio_write_t) ZIO_WRITE_ARGS ; +#define ZIO_SPAN_POLL_EVENT_FUNCTION(name) zap_status_t name ZIO_SPAN_POLL_EVENT_ARGS +#define ZIO_SPAN_NEXT_EVENT_FUNCTION(name) zap_status_t name ZIO_SPAN_NEXT_EVENT_ARGS #define ZIO_SIGNAL_CB_FUNCTION(name) zap_status_t name ZIO_SIGNAL_CB_ARGS #define ZIO_EVENT_CB_FUNCTION(name) zap_status_t name ZIO_EVENT_CB_ARGS #define ZIO_CODEC_FUNCTION(name) zap_status_t name ZIO_CODEC_ARGS diff --git a/libs/freetdm/src/testanalog.c b/libs/freetdm/src/testanalog.c new file mode 100644 index 0000000000..7254bf455e --- /dev/null +++ b/libs/freetdm/src/testanalog.c @@ -0,0 +1,36 @@ +#include "openzap.h" +#include "zap_analog.h" + +static ZIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return ZAP_FAIL; +} + +int main(int argc, char *argv[]) +{ + zap_span_t *span; + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + if (zap_span_find("wanpipe", 1, &span) != ZAP_SUCCESS) { + fprintf(stderr, "Error finding OpenZAP span\n"); + } + + + zap_analog_configure_span(span, on_signal); + zap_analog_start(span); + + while(zap_test_flag(span->analog_data, ZAP_ANALOG_RUNNING)) { + sleep(1); + } + + zap_global_destroy(); + +} diff --git a/libs/freetdm/src/zap_analog.c b/libs/freetdm/src/zap_analog.c index dabea87c4e..c9e93bdb83 100644 --- a/libs/freetdm/src/zap_analog.c +++ b/libs/freetdm/src/zap_analog.c @@ -32,3 +32,212 @@ */ #include "openzap.h" +#include "zap_analog.h" + +zap_status_t zap_analog_configure_span(zap_span_t *span, zio_signal_cb_t sig_cb) +{ + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return ZAP_FAIL; + } + + span->analog_data = malloc(sizeof(*span->analog_data)); + assert(span->analog_data != NULL); + memset(span->analog_data, 0, sizeof(*span->analog_data)); + span->signal_type = ZAP_SIGTYPE_ANALOG; + + return ZAP_SUCCESS; + +} + +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + zap_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + zap_buffer_write(dt_buffer, ts->buffer, wrote * 2); + printf("add %d\n", wrote * 2); + return 0; +} + +static void *zap_analog_channel_run(zap_thread_t *me, void *obj) +{ + zap_channel_t *chan = (zap_channel_t *) obj; + zap_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts; + uint8_t frame[1024]; + zap_size_t len, rlen; + zap_codec_t codec = ZAP_CODEC_SLIN, old_codec; + char *tones[] = { "%(1000,0,350,440)", + "%(500,500,480,620)", + "v=2000;%(100,100,1400,2060,2450,2600)", + NULL }; + + time_t start; + int isbz = 0; + int wtime = 10; + zap_tone_type_t tt = ZAP_TONE_DTMF; + int play_tones = 1; + + zap_log(ZAP_LOG_DEBUG, "ANALOG CHANNEL thread starting.\n"); + + if (zap_channel_open_chan(chan) != ZAP_SUCCESS) { + goto done; + } + + if (zap_buffer_create(&dt_buffer, 1024, 3192, 0) != ZAP_SUCCESS) { + goto done; + } + + if (zap_channel_command(chan, ZAP_COMMAND_ENABLE_TONE_DETECT, &tt) != ZAP_SUCCESS) { + goto done; + } + /*zap_channel_set_event_callback(chan, my_zap_event_handler);*/ + + + zap_set_flag_locked(chan, ZAP_CHANNEL_INTHREAD); + zap_set_state_locked(chan, ZAP_CHANNEL_STATE_DIALTONE); + + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; + ts.debug = 1; + ts.debug_stream = stdout; + teletone_run(&ts, tones[isbz++]); + zap_channel_command(chan, ZAP_COMMAND_GET_CODEC, &old_codec); + zap_channel_command(chan, ZAP_COMMAND_SET_CODEC, &codec); + zap_buffer_set_loops(dt_buffer, -1); + + time(&start); + + + + while (chan->state >= ZAP_CHANNEL_STATE_DIALTONE && zap_test_flag(chan, ZAP_CHANNEL_INTHREAD)) { + zap_wait_flag_t flags = ZAP_READ; + char dtmf[128]; + zap_size_t dlen = 0; + + len = sizeof(frame); + + if (play_tones && tones[isbz] && time(NULL) - start > wtime) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, tones[isbz++]); + time(&start); + wtime *= 2; + } + + if ((dlen = zap_channel_dequeue_dtmf(chan, dtmf, sizeof(dtmf)))) { + printf("DTMF %s\n", dtmf); + play_tones = 0; + } + + if (zap_channel_wait(chan, &flags, -1) == ZAP_FAIL) { + goto done; + } + + if (flags & ZAP_READ) { + if (zap_channel_read(chan, frame, &len) == ZAP_SUCCESS) { + rlen = zap_buffer_read_loop(dt_buffer, frame, len); + if (play_tones) { + zap_channel_write(chan, frame, &rlen); + } + } else { + goto done; + } + } + } + + done: + zap_channel_command(chan, ZAP_COMMAND_SET_CODEC, &old_codec); + if (ts.buffer) { + teletone_destroy_session(&ts); + } + if (dt_buffer) { + zap_buffer_destroy(&dt_buffer); + } + + zap_clear_flag(chan, ZAP_CHANNEL_INTHREAD); + zap_channel_close(&chan); + zap_log(ZAP_LOG_DEBUG, "ANALOG CHANNEL thread ended. %d\n", old_codec); + return NULL; +} + +static zap_status_t process_event(zap_span_t *span, zap_event_t *event) +{ + zap_log(ZAP_LOG_DEBUG, "EVENT [%s][%d:%d]\n", zap_oob_signal2str(event->enum_id), event->channel->span_id, event->channel->chan_id); + + switch(event->enum_id) { + case ZAP_OOB_ONHOOK: + { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_OOB_OFFHOOK: + { + if (!zap_test_flag(event->channel, ZAP_CHANNEL_INTHREAD)) { + zap_thread_create_detached(zap_analog_channel_run, event->channel); + } + } + } + + + return ZAP_SUCCESS; +} + +static void *zap_analog_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_analog_data_t *data = span->analog_data; + + zap_log(ZAP_LOG_DEBUG, "ANALOG thread starting.\n"); + + while(zap_test_flag(data, ZAP_ANALOG_RUNNING)) { + int waitms = 10; + zap_status_t status; + + status = zap_span_poll_event(span, waitms); + + switch(status) { + case ZAP_TIMEOUT: + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (process_event(span, event) != ZAP_SUCCESS) { + goto end; + } + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_DEBUG, "Failure!\n"); + goto end; + } + break; + default: + break; + } + + } + + end: + + zap_clear_flag(data, ZAP_ANALOG_RUNNING); + + zap_log(ZAP_LOG_DEBUG, "ANALOG thread ending.\n"); + + return NULL; +} + + + +zap_status_t zap_analog_start(zap_span_t *span) +{ + zap_set_flag(span->analog_data, ZAP_ANALOG_RUNNING); + return zap_thread_create_detached(zap_analog_run, span); +} + diff --git a/libs/freetdm/src/zap_io.c b/libs/freetdm/src/zap_io.c index f4dbeeeade..f0ca7dd395 100644 --- a/libs/freetdm/src/zap_io.c +++ b/libs/freetdm/src/zap_io.c @@ -41,6 +41,35 @@ #include "zap_zt.h" #endif +static int time_is_init = 0; + +static void time_init(void) +{ +#ifdef WIN32_TIME_GET_TIME + timeBeginPeriod(1); +#endif + time_is_init = 1; +} + +static void time_end(void) +{ +#ifdef WIN32_TIME_GET_TIME + timeEndPeriod(1); +#endif + time_is_init = 0; +} + +zap_time_t zap_current_time_in_ms(void) +{ +#ifdef WIN32_TIME_GET_TIME + return timeGetTime(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +#endif +} + static struct { zap_hash_t *interface_hash; zap_mutex_t *mutex; @@ -56,6 +85,41 @@ static char *TRUNK_TYPE_NAMES[] = { NULL }; +static char *OOB_NAMES[] = { + "ZAP_OOB_DTMF", + "ZAP_OOB_ONHOOK", + "ZAP_OOB_OFFHOOK", + "WINK", + "FLASH", + "ZAP_OOB_RING_START", + "ZAP_OOB_RING_STOP", + "INVALID", + NULL +}; + +zap_oob_event_t str2zap_oob_signal(char *name) +{ + int i; + zap_trunk_type_t t = ZAP_OOB_INVALID; + + for (i = 0; i < ZAP_OOB_INVALID; i++) { + if (!strcasecmp(name, OOB_NAMES[i])) { + t = (zap_oob_event_t) i; + break; + } + } + + return t; +} + +char *zap_oob_signal2str(zap_oob_event_t type) +{ + if (type > ZAP_OOB_INVALID) { + type = ZAP_OOB_INVALID; + } + return OOB_NAMES[(int)type]; +} + static char *LEVEL_NAMES[] = { "EMERG", "ALERT", @@ -206,16 +270,26 @@ zap_status_t zap_span_create(zap_io_interface_t *zio, zap_span_t **span) zap_status_t zap_span_close_all(zap_io_interface_t *zio) { zap_span_t *span; - uint32_t i; + uint32_t i, j; for(i = 0; i < zio->span_index; i++) { span = &zio->spans[i]; + + for(j = 0; j < span->chan_count; j++) { + zap_mutex_destroy(&span->channels[j].mutex); + zap_buffer_destroy(&span->channels[j].digit_buffer); + zap_buffer_destroy(&span->channels[j].dtmf_buffer); + } + if (span->mutex) { zap_mutex_destroy(&span->mutex); } if (span->isdn_data) { free(span->isdn_data); } + if (span->analog_data) { + free(span->isdn_data); + } } return i ? ZAP_SUCCESS : ZAP_FAIL; @@ -232,6 +306,8 @@ zap_status_t zap_span_add_channel(zap_span_t *span, zap_socket_t sockfd, zap_cha new_chan->span_id = span->span_id; new_chan->chan_id = span->chan_count; new_chan->span = span; + zap_mutex_create(&new_chan->mutex); + zap_buffer_create(&new_chan->digit_buffer, 128, 128, 0); zap_set_flag(new_chan, ZAP_CHANNEL_CONFIGURED | ZAP_CHANNEL_READY); *chan = new_chan; return ZAP_SUCCESS; @@ -277,6 +353,29 @@ zap_status_t zap_span_set_event_callback(zap_span_t *span, zio_event_cb_t event_ return ZAP_SUCCESS; } + +zap_status_t zap_span_poll_event(zap_span_t *span, uint32_t ms) +{ + assert(span->zio != NULL); + + if (span->zio->poll_event) { + return span->zio->poll_event(span, ms); + } + + return ZAP_NOTIMPL; +} + +zap_status_t zap_span_next_event(zap_span_t *span, zap_event_t **event) +{ + assert(span->zio != NULL); + + if (span->zio->next_event) { + return span->zio->next_event(span, event); + } + + return ZAP_NOTIMPL; +} + zap_status_t zap_channel_set_event_callback(zap_channel_t *zchan, zio_event_cb_t event_callback) { zap_mutex_lock(zchan->span->mutex); @@ -397,9 +496,15 @@ static zap_status_t zap_channel_reset(zap_channel_t *zchan) teletone_destroy_session(&zchan->tone_session); memset(&zchan->tone_session, 0, sizeof(zchan->tone_session)); } + if (zchan->dtmf_buffer) { - zap_buffer_destroy(&zchan->dtmf_buffer); + zap_buffer_zero(zchan->dtmf_buffer); } + + if (zchan->digit_buffer) { + zap_buffer_zero(zchan->digit_buffer); + } + zchan->dtmf_on = zchan->dtmf_off = 0; if (zap_test_flag(zchan, ZAP_CHANNEL_TRANSCODE)) { @@ -411,7 +516,21 @@ static zap_status_t zap_channel_reset(zap_channel_t *zchan) return ZAP_SUCCESS; } +zap_status_t zap_channel_open_chan(zap_channel_t *zchan) +{ + zap_status_t status; + zap_mutex_lock(zchan->span->mutex); + if (zap_test_flag(zchan, ZAP_CHANNEL_READY) && ! zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + status = zchan->span->zio->open(zchan); + if (status == ZAP_SUCCESS) { + zap_set_flag(zchan, ZAP_CHANNEL_OPEN); + } + } + zap_mutex_unlock(zchan->span->mutex); + + return status; +} zap_status_t zap_channel_open(const char *name, uint32_t span_id, uint32_t chan_id, zap_channel_t **zchan) { @@ -824,6 +943,56 @@ ZIO_CODEC_FUNCTION(zio_alaw2ulaw) /******************************/ +zap_size_t zap_channel_dequeue_dtmf(zap_channel_t *zchan, char *dtmf, zap_size_t len) +{ + zap_size_t bytes; + + assert(zchan != NULL); + + zap_mutex_lock(zchan->mutex); + if ((bytes = zap_buffer_read(zchan->digit_buffer, dtmf, len)) > 0) { + *(dtmf + bytes) = '\0'; + } + zap_mutex_unlock(zchan->mutex); + + return bytes; +} + + +zap_status_t zap_channel_queue_dtmf(zap_channel_t *zchan, const char *dtmf) +{ + zap_status_t status; + register zap_size_t len, inuse; + zap_size_t wr = 0; + const char *p; + + assert(zchan != NULL); + + zap_mutex_lock(zchan->mutex); + + inuse = zap_buffer_inuse(zchan->digit_buffer); + len = strlen(dtmf); + + if (len + inuse > zap_buffer_len(zchan->digit_buffer)) { + zap_buffer_toss(zchan->digit_buffer, strlen(dtmf)); + } + + p = dtmf; + while (wr < len && p) { + if (zap_is_dtmf(*p)) { + wr++; + } else { + break; + } + p++; + } + + status = zap_buffer_write(zchan->digit_buffer, dtmf, wr) ? ZAP_SUCCESS : ZAP_FAIL; + zap_mutex_unlock(zchan->mutex); + + return status; +} + zap_status_t zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *datalen) { zap_status_t status = ZAP_FAIL; @@ -895,11 +1064,15 @@ zap_status_t zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *data sln = sln_buf; } - + XX printf("WTF %d\n", (int) slen); teletone_dtmf_detect(&zchan->dtmf_detect, sln, (int)slen); teletone_dtmf_get(&zchan->dtmf_detect, digit_str, sizeof(digit_str)); - if(digit_str[0]) { + + if(*digit_str) { zio_event_cb_t event_callback = NULL; + + zap_channel_queue_dtmf(zchan, digit_str); + if (zchan->span->event_callback) { event_callback = zchan->span->event_callback; } else if (zchan->event_callback) { @@ -907,6 +1080,7 @@ zap_status_t zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *data } if (event_callback) { + zchan->event_header.channel = zchan; zchan->event_header.e_type = ZAP_EVENT_DTMF; zchan->event_header.data = digit_str; event_callback(zchan, &zchan->event_header); @@ -1012,6 +1186,7 @@ zap_status_t zap_global_init(void) uint32_t configured = 0; int modcount; + time_init(); zap_isdn_init(); memset(&interfaces, 0, sizeof(interfaces)); @@ -1047,6 +1222,7 @@ zap_status_t zap_global_init(void) } if (!zap_config_open_file(&cfg, "openzap.conf")) { + zap_log(ZAP_LOG_ERROR, "Cannot open openzap.conf!\n"); return ZAP_FAIL; } @@ -1082,6 +1258,8 @@ zap_status_t zap_global_init(void) zap_status_t zap_global_destroy(void) { + time_end(); + #ifdef ZAP_ZT_SUPPORT if (interfaces.zt_interface) { zt_destroy(); diff --git a/libs/freetdm/src/zap_wanpipe.c b/libs/freetdm/src/zap_wanpipe.c index 04dc44d507..9eab6fcc42 100644 --- a/libs/freetdm/src/zap_wanpipe.c +++ b/libs/freetdm/src/zap_wanpipe.c @@ -39,7 +39,9 @@ #include static struct { - unsigned codec_ms; + uint32_t codec_ms; + uint32_t wink_ms; + uint32_t flash_ms; } wp_globals; static zap_io_interface_t wanpipe_interface; @@ -70,6 +72,25 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start, if (sockfd != WP_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) { zap_log(ZAP_LOG_INFO, "configuring device s%dc%d as OpenZAP device %d:%d fd:%d\n", spanno, x, chan->span_id, chan->chan_id, sockfd); + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) { + wanpipe_tdm_api_t tdm_api; + + tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_ENABLE_RXHOOK_EVENTS; + wp_tdm_cmd_exec(chan, &tdm_api); + + if (type == ZAP_CHAN_TYPE_FXS) { + tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_ENABLE_RING_DETECT_EVENTS; + wp_tdm_cmd_exec(chan, &tdm_api); + } + + tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_GET_HW_CODING; + wp_tdm_cmd_exec(chan, &tdm_api); + if (tdm_api.wp_tdm_cmd.hw_tdm_coding) { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW; + } else { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW; + } + } configured++; } else { zap_log(ZAP_LOG_ERROR, "failure configuring device s%dc%d\n", spanno, x); @@ -206,6 +227,10 @@ static ZIO_CONFIGURE_FUNCTION(wanpipe_configure) } else if (!strcasecmp(var, "trunk_type")) { span->trunk_type = str2zap_trunk_type(val); zap_log(ZAP_LOG_DEBUG, "setting trunk type to '%s'\n", zap_trunk_type2str(span->trunk_type)); + } else if (!strcasecmp(var, "fxo-channel")) { + configured += wp_configure_channel(&cfg, val, span, ZAP_CHAN_TYPE_FXO); + } else if (!strcasecmp(var, "fxs-channel")) { + configured += wp_configure_channel(&cfg, val, span, ZAP_CHAN_TYPE_FXS); } else if (!strcasecmp(var, "b-channel")) { configured += wp_configure_channel(&cfg, val, span, ZAP_CHAN_TYPE_B); } else if (!strcasecmp(var, "d-channel")) { @@ -250,13 +275,8 @@ static ZIO_OPEN_FUNCTION(wanpipe_open) zap_channel_set_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL); zchan->effective_interval = zchan->native_interval = wp_globals.codec_ms; zchan->packet_len = zchan->native_interval * 8; - - tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_GET_HW_CODING; - if (tdm_api.wp_tdm_cmd.hw_tdm_coding) { - zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ULAW; - } else { - zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ALAW; - } + zchan->native_codec = zchan->effective_codec; + } return ZAP_SUCCESS; @@ -392,12 +412,140 @@ static ZIO_WAIT_FUNCTION(wanpipe_wait) return ZAP_SUCCESS; } +ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) +{ + struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN]; + int i, j = 0, k = 0, r; + + for(i = 1; i <= span->chan_count; i++) { + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i].sockfd; + pfds[j].events = POLLPRI; + //printf("set %d=%d\n", j, pfds[j].fd); + j++; + } + + r = poll(pfds, j, ms); + + if (r == 0) { + return ZAP_TIMEOUT; + } else if (r < 0) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + for(i = 1; i <= span->chan_count; i++) { + if (pfds[i-1].revents & POLLPRI) { + zap_set_flag((&span->channels[i]), ZAP_CHANNEL_EVENT); + span->channels[i].last_event_time = zap_current_time_in_ms(); + k++; + } + } + + return k ? ZAP_SUCCESS : ZAP_FAIL; +} + +ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event) +{ + int i; + zap_oob_event_t event_id; + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i].last_event_time && !zap_test_flag((&span->channels[i]), ZAP_CHANNEL_EVENT)) { + uint32_t diff = zap_current_time_in_ms() - span->channels[i].last_event_time; + XX printf("%u %u %u\n", diff, (unsigned)zap_current_time_in_ms(), (unsigned)span->channels[i].last_event_time); + if (zap_test_flag((&span->channels[i]), ZAP_CHANNEL_WINK)) { + if (diff > wp_globals.wink_ms) { + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_WINK); + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_FLASH); + event_id = ZAP_OOB_OFFHOOK; + goto event; + } + } + + if (zap_test_flag((&span->channels[i]), ZAP_CHANNEL_FLASH)) { + if (diff > wp_globals.flash_ms) { + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_FLASH); + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_WINK); + event_id = ZAP_OOB_ONHOOK; + goto event; + } + } + } + if (zap_test_flag((&span->channels[i]), ZAP_CHANNEL_EVENT)) { + wanpipe_tdm_api_t tdm_api; + zap_clear_flag((&span->channels[i]), ZAP_CHANNEL_EVENT); + + tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_READ_EVENT; + if (wp_tdm_cmd_exec(&span->channels[i], &tdm_api) != ZAP_SUCCESS) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { + case WP_TDM_EVENT_RXHOOK: + { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rxhook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? ZAP_OOB_OFFHOOK : ZAP_OOB_ONHOOK; + + if (event_id == ZAP_OOB_OFFHOOK) { + if (zap_test_flag((&span->channels[i]), ZAP_CHANNEL_FLASH)) { + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_FLASH); + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_WINK); + event_id = ZAP_OOB_FLASH; + goto event; + } else { + zap_set_flag_locked((&span->channels[i]), ZAP_CHANNEL_WINK); + } + } else { + if (zap_test_flag((&span->channels[i]), ZAP_CHANNEL_WINK)) { + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_WINK); + zap_clear_flag_locked((&span->channels[i]), ZAP_CHANNEL_FLASH); + event_id = ZAP_OOB_WINK; + goto event; + } else { + zap_set_flag_locked((&span->channels[i]), ZAP_CHANNEL_FLASH); + } + } + continue; + } + break; + case WP_TDM_EVENT_RING_DETECT: + { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state & WP_TDMAPI_EVENT_RING_PRESENT ? ZAP_OOB_RING_START : ZAP_OOB_RING_STOP; + tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_DISABLE_RING_DETECT_EVENTS; + wp_tdm_cmd_exec(&span->channels[i], &tdm_api); + } + break; + default: + { + zap_log(ZAP_LOG_WARNING, "Unhandled event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); + event_id = ZAP_OOB_INVALID; + } + break; + } + + event: + 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; + +} + zap_status_t wanpipe_init(zap_io_interface_t **zio) { assert(zio != NULL); memset(&wanpipe_interface, 0, sizeof(wanpipe_interface)); wp_globals.codec_ms = 20; + wp_globals.wink_ms = 150; + wp_globals.flash_ms = 750; wanpipe_interface.name = "wanpipe"; wanpipe_interface.configure = wanpipe_configure; wanpipe_interface.open = wanpipe_open; @@ -406,6 +554,8 @@ zap_status_t wanpipe_init(zap_io_interface_t **zio) wanpipe_interface.wait = wanpipe_wait; wanpipe_interface.read = wanpipe_read; wanpipe_interface.write = wanpipe_write; + wanpipe_interface.poll_event = wanpipe_poll_event; + wanpipe_interface.next_event = wanpipe_next_event; *zio = &wanpipe_interface; return ZAP_SUCCESS;