From: Sebastian Kemper Date: Thu, 14 Sep 2017 19:02:57 +0000 (+0200) Subject: asterisk-chan-lantiq: Move to separate package X-Git-Url: http://git.openwrt.org/?p=feed%2Ftelephony.git;a=commitdiff_plain;h=fe2f6f996bf0066f4462502926478aab1e4cce73 asterisk-chan-lantiq: Move to separate package Currently chan-lantiq doesn't build on the build bots and users end up having to build it themselves. The reason for this was explained by Mathias Kresin here: http://lists.infradead.org/pipermail/lede-dev/2017-September/008886.html. To fix this put chan-lantiq into its own package and set PKG_FLAGS:=nonshared. The new package uses variants already, so it is easily extendable in the future. Signed-off-by: Sebastian Kemper --- diff --git a/net/asterisk-13.x/Makefile b/net/asterisk-13.x/Makefile index 299faa4..b98ab9b 100644 --- a/net/asterisk-13.x/Makefile +++ b/net/asterisk-13.x/Makefile @@ -81,32 +81,6 @@ $(foreach b,$(8),$(call Package/asterisk13/install/sbin,$$(1),$(b));) $$(eval $$(call BuildPackage,asterisk13-$(1))) endef -define BuildAsterisk13NonSharedModule - define Package/asterisk13-$(1) - $$(call Package/asterisk13/Default) - TITLE:=$(2) support - DEPENDS:= asterisk13 $(patsubst +%,+PACKAGE_asterisk13-$(1):%,$(4)) - PKG_FLAGS:=nonshared - endef - - define Package/asterisk13-$(1)/conffiles -$(subst $(space),$(newline),$(foreach c,$(5),/etc/asterisk/$(c))) - endef - - define Package/asterisk13-$(1)/description -This package provides support for '$(3)' in Asterisk. - endef - - define Package/asterisk13-$(1)/install -$(foreach c,$(5),$(call Package/asterisk13/install/conffile,$$(1),$(c));) -$(foreach m,$(6),$(call Package/asterisk13/install/module,$$(1),$(m));) -$(foreach s,$(7),$(call Package/asterisk13/install/sounds,$$(1),$(s));) -$(foreach b,$(8),$(call Package/asterisk13/install/sbin,$$(1),$(b));) - endef - - $$(eval $$(call BuildPackage,asterisk13-$(1))) -endef - define Package/asterisk13/Default SUBMENU:=Telephony SECTION:=net @@ -276,13 +250,6 @@ AST_MENUSELECT_OPTS = \ --without-curses \ --with-libxml2="$(STAGING_DIR_HOSTPKG)/usr" -define Build/Prepare - $(call Build/Prepare/Default) -ifneq ($(CONFIG_TARGET_lantiq),) - $(CP) ./src-lantiq/* $(PKG_BUILD_DIR)/ -endif -endef - define Build/Configure cd $(PKG_BUILD_DIR); \ ./bootstrap.sh @@ -375,7 +342,6 @@ $(eval $(call BuildAsterisk13Module,cdr-sqlite3,Provides CDR SQLITE3,Call Detail $(eval $(call BuildAsterisk13Module,chan-alsa,ALSA channel,the channel chan_alsa,+alsa-lib,alsa.conf,chan_alsa,,)) $(eval $(call BuildAsterisk13Module,chan-dahdi,DAHDI channel,DAHDI channel support,+dahdi-tools-libtonezone +kmod-dahdi +libpri @!aarch64,chan_dahdi.conf,chan_dahdi,,)) $(eval $(call BuildAsterisk13Module,chan-iax2,IAX2 channel,IAX support,+asterisk13-res-timing-timerfd,iax.conf iaxprov.conf,chan_iax2,,)) -$(eval $(call BuildAsterisk13NonSharedModule,chan-lantiq,Lantiq TAPI channel,An implementation of Lantiq TAPI channel,@TARGET_lantiq +kmod-ltq-vmmc,lantiq.conf,chan_lantiq,,)) $(eval $(call BuildAsterisk13Module,chan-motif,Jingle channel,Motif Jingle Channel Driver,+asterisk13-res-xmpp,motif.conf,chan_motif,,)) $(eval $(call BuildAsterisk13Module,chan-oss,OSS channel,the channel chan_oss,,oss.conf,chan_oss,,)) $(eval $(call BuildAsterisk13Module,chan-sip,SIP channel,the channel chan_sip,+asterisk13-app-confbridge,sip.conf sip_notify.conf,chan_sip,,)) diff --git a/net/asterisk-13.x/src-lantiq/channels/chan_lantiq.c b/net/asterisk-13.x/src-lantiq/channels/chan_lantiq.c deleted file mode 100644 index 19d422e..0000000 --- a/net/asterisk-13.x/src-lantiq/channels/chan_lantiq.c +++ /dev/null @@ -1,2301 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2012, Luka Perkov - * Copyright (C) 2012, John Crispin - * Copyright (C) 2012, Andrej VlaÅ¡ić - * Copyright (C) 2012, Kaspar Schleiser for T-Labs - * (Deutsche Telekom Innovation Laboratories) - * Copyright (C) 2012, Mirko Vogt for T-Labs - * (Deutsche Telekom Innovation Laboratories) - * Copyright (c) 2015, Antonio Eugenio Burriel - * Copyright (C) 2017, Stefan Koch - * - * Luka Perkov - * John Crispin - * Andrej VlaÅ¡ić - * Kaspar Schleiser - * Mirko Vogt - * Antonio Eugenio Burriel - * Stefan Koch - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Asterisk channel line driver for Lantiq based TAPI boards - * - * \author Luka Perkov - * \author John Crispin - * \author Andrej VlaÅ¡ić - * \author Kaspar Schleiser - * \author Mirko Vogt - * \author Antonio Eugenio Burriel - * \author Stefan Koch - * - * \ingroup channel_drivers - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision: xxx $") - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_LINUX_COMPILER_H -#include -#endif -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Lantiq TAPI includes */ -#include -#include - -#define TAPI_AUDIO_PORT_NUM_MAX 2 - -/* Tapi predefined tones 0 to 31 */ -#define TAPI_TONE_LOCALE_NONE 0 -//#define TAPI_TONE_LOCALE_DIAL_CODE 25 -//#define TAPI_TONE_LOCALE_RINGING_CODE 26 -//#define TAPI_TONE_LOCALE_BUSY_CODE 27 -//#define TAPI_TONE_LOCALE_CONGESTION_CODE 27 - -/* Tapi custom tones 32 to 256 */ -#define TAPI_TONE_LOCALE_DIAL_CODE 32 -#define TAPI_TONE_LOCALE_RINGING_CODE 33 -#define TAPI_TONE_LOCALE_BUSY_CODE 34 -#define TAPI_TONE_LOCALE_CONGESTION_CODE 35 - -#define LANTIQ_CONTEXT_PREFIX "lantiq" -#define DEFAULT_INTERDIGIT_TIMEOUT 4000 -#define G723_HIGH_RATE 1 -#define LED_NAME_LENGTH 32 - -static const char config[] = "lantiq.conf"; - -static char firmware_filename[PATH_MAX] = "/lib/firmware/ifx_firmware.bin"; -static char bbd_filename[PATH_MAX] = "/lib/firmware/ifx_bbd_fxs.bin"; -static char base_path[PATH_MAX] = "/dev/vmmc"; -static int per_channel_context = 0; - -/* tone generator types */ -enum tone_generator_t { - TONE_INTEGRATED, /* tapi tone generator */ - TONE_ASTERISK, /* asterisk tone generator where possible */ - TONE_MEDIA /* media tone where possible */ -}; - -/* tone generator (default is integraded) */ -static enum tone_generator_t tone_generator = TONE_INTEGRATED; - -/* tone zones for dial, ring, busy and congestion */ -struct ast_tone_zone_sound *ts_dial; -struct ast_tone_zone_sound *ts_ring; -struct ast_tone_zone_sound *ts_busy; -struct ast_tone_zone_sound *ts_congestion; - -/* - * The private structures of the Phone Jack channels are linked for selecting - * outgoing channels. - */ -enum channel_state { - ONHOOK, - OFFHOOK, - DIALING, - INCALL, - CALL_ENDED, - RINGING, - UNKNOWN -}; - -static struct lantiq_pvt { - struct ast_channel *owner; /* Channel we belong to, possibly NULL */ - int port_id; /* Port number of this object, 0..n */ - int channel_state; - char context[AST_MAX_CONTEXT]; /* this port's dialplan context */ - int dial_timer; /* timer handle for autodial timeout */ - char dtmfbuf[AST_MAX_EXTENSION]; /* buffer holding dialed digits */ - int dtmfbuf_len; /* lenght of dtmfbuf */ - int rtp_timestamp; /* timestamp for RTP packets */ - int ptime; /* Codec base ptime */ - uint16_t rtp_seqno; /* Sequence nr for RTP packets */ - uint32_t call_setup_start; /* Start of dialling in ms */ - uint32_t call_setup_delay; /* time between ^ and 1st ring in ms */ - uint32_t call_start; /* time we started dialling / answered */ - uint32_t call_answer; /* time the callee answered our call */ - uint16_t jb_size; /* Jitter buffer size */ - uint32_t jb_underflow; /* Jitter buffer injected samples */ - uint32_t jb_overflow; /* Jitter buffer dropped samples */ - uint16_t jb_delay; /* Jitter buffer: playout delay */ - uint16_t jb_invalid; /* Jitter buffer: Nr. of invalid packets */ -} *iflist = NULL; - -static struct lantiq_ctx { - int dev_fd; - int channels; - int ch_fd[TAPI_AUDIO_PORT_NUM_MAX]; - char voip_led[LED_NAME_LENGTH]; /* VOIP LED name */ - char ch_led[TAPI_AUDIO_PORT_NUM_MAX][LED_NAME_LENGTH]; /* FXS LED names */ - int interdigit_timeout; /* Timeout in ms between dialed digits */ -} dev_ctx; - -static int ast_digit_begin(struct ast_channel *ast, char digit); -static int ast_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int ast_lantiq_call(struct ast_channel *ast, const char *dest, int timeout); -static int ast_lantiq_hangup(struct ast_channel *ast); -static int ast_lantiq_answer(struct ast_channel *ast); -static struct ast_frame *ast_lantiq_read(struct ast_channel *ast); -static int ast_lantiq_write(struct ast_channel *ast, struct ast_frame *frame); -static struct ast_frame *ast_lantiq_exception(struct ast_channel *ast); -static int ast_lantiq_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen); -static int ast_lantiq_fixup(struct ast_channel *old, struct ast_channel *new); -static struct ast_channel *ast_lantiq_requester(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assigned_ids, const struct ast_channel *requestor, const char *data, int *cause); -static int ast_lantiq_devicestate(const char *data); -static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *args, char *buf, size_t buflen); -static void lantiq_jb_get_stats(int c); -static struct ast_format *lantiq_map_rtptype_to_format(uint8_t rtptype); -static uint8_t lantiq_map_format_to_rtptype(const struct ast_format *format); -static int lantiq_conf_enc(int c, const struct ast_format *format); -static void lantiq_reset_dtmfbuf(struct lantiq_pvt *pvt); - -static struct ast_channel_tech lantiq_tech = { - .type = "TAPI", - .description = "Lantiq TAPI Telephony API Driver", - .send_digit_begin = ast_digit_begin, - .send_digit_end = ast_digit_end, - .call = ast_lantiq_call, - .hangup = ast_lantiq_hangup, - .answer = ast_lantiq_answer, - .read = ast_lantiq_read, - .write = ast_lantiq_write, - .exception = ast_lantiq_exception, - .indicate = ast_lantiq_indicate, - .fixup = ast_lantiq_fixup, - .requester = ast_lantiq_requester, - .devicestate = ast_lantiq_devicestate, - .func_channel_read = acf_channel_read -}; - -/* Protect the interface list (of lantiq_pvt's) */ -AST_MUTEX_DEFINE_STATIC(iflock); - -/* - * Protect the monitoring thread, so only one process can kill or start it, and - * not when it's doing something critical. - */ -AST_MUTEX_DEFINE_STATIC(monlock); - -/* The scheduling context */ -struct ast_sched_context *sched; - -/* - * This is the thread for the monitor which checks for input on the channels - * which are not currently in use. - */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; - - -#define WORDS_BIGENDIAN -/* struct taken from some GPLed code by Mike Borella */ -typedef struct rtp_header -{ -#if defined(WORDS_BIGENDIAN) - uint8_t version:2, padding:1, extension:1, csrc_count:4; - uint8_t marker:1, payload_type:7; -#else - uint8_t csrc_count:4, extension:1, padding:1, version:2; - uint8_t payload_type:7, marker:1; -#endif - uint16_t seqno; - uint32_t timestamp; - uint32_t ssrc; -} rtp_header_t; -#define RTP_HEADER_LEN 12 -#define RTP_BUFFER_LEN 512 -/* Internal RTP payload types - standard */ -#define RTP_PCMU 0 -#define RTP_G723_63 4 -#define RTP_PCMA 8 -#define RTP_G722 9 -#define RTP_CN 13 -#define RTP_G729 18 -/* Internal RTP payload types - custom */ -#define RTP_G7221 100 -#define RTP_G726 101 -#define RTP_ILBC 102 -#define RTP_SLIN8 103 -#define RTP_SLIN16 104 -#define RTP_SIREN7 105 -#define RTP_G723_53 106 - - -/* LED Control. Taken with modifications from SVD by Luca Olivetti */ -#define LED_SLOW_BLINK 1000 -#define LED_FAST_BLINK 100 -static FILE *led_open(const char *led, char* sub) -{ - char fname[100]; - - if (snprintf(fname, sizeof(fname), "/sys/class/leds/%s/%s", led, sub) >= sizeof(fname)) - return NULL; - return fopen(fname, "r+"); -} - -static FILE *led_trigger(const char *led) -{ - return led_open(led, "trigger"); -} - -static void led_delay(const char *led, int onoff, int msec) -{ - FILE *fp = led_open(led, onoff ? "delay_on" : "delay_off"); - if (fp) { - fprintf(fp,"%d\n",msec); - fclose(fp); - } -} - -static void led_on(const char *led) -{ - FILE *fp; - - fp = led_trigger(led); - if (fp) { - fprintf(fp,"default-on\n"); - fclose(fp); - } -} - -static void led_off(const char *led) -{ - FILE *fp; - - fp = led_trigger(led); - if (fp) { - fprintf(fp,"none\n"); - fclose(fp); - } -} - -static void led_blink(const char *led, int period) -{ - FILE *fp; - - fp = led_trigger(led); - if (fp) { - fprintf(fp, "timer\n"); - fclose(fp); - led_delay(led, 1, period/2); - led_delay(led, 0, period/2); - } -} - -static uint32_t now(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - uint64_t tmp = ts.tv_sec*1000 + (ts.tv_nsec/1000000); - return (uint32_t) tmp; -} - -static uint32_t epoch(void) { - struct timeval tv; - gettimeofday(&tv, NULL); - - return tv.tv_sec; -} - -static int lantiq_dev_open(const char *dev_path, const int32_t ch_num) -{ - char dev_name[PATH_MAX]; - memset(dev_name, 0, sizeof(dev_name)); - snprintf(dev_name, PATH_MAX, "%s%u%u", dev_path, 1, ch_num); - return open((const char*)dev_name, O_RDWR, 0644); -} - -static void lantiq_ring(int c, int r, const char *cid, const char *name) -{ - uint8_t status; - - if (r) { - led_blink(dev_ctx.ch_led[c], LED_FAST_BLINK); - if (!cid) { - status = (uint8_t) ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_START, 0); - } else { - IFX_TAPI_CID_MSG_t msg; - IFX_TAPI_CID_MSG_ELEMENT_t elements[3]; - int count = 0; - time_t timestamp; - struct tm *tm; - - elements[count].string.elementType = IFX_TAPI_CID_ST_CLI; - elements[count].string.len = strlen(cid); - if (elements[count].string.len > IFX_TAPI_CID_MSG_LEN_MAX) { - elements[count].string.len = IFX_TAPI_CID_MSG_LEN_MAX; - } - strncpy((char *)elements[count].string.element, cid, IFX_TAPI_CID_MSG_LEN_MAX); - elements[count].string.element[IFX_TAPI_CID_MSG_LEN_MAX-1] = '\0'; - count++; - - if (name) { - elements[count].string.elementType = IFX_TAPI_CID_ST_NAME; - elements[count].string.len = strlen(name); - if (elements[count].string.len > IFX_TAPI_CID_MSG_LEN_MAX) { - elements[count].string.len = IFX_TAPI_CID_MSG_LEN_MAX; - } - strncpy((char *)elements[count].string.element, name, IFX_TAPI_CID_MSG_LEN_MAX); - elements[count].string.element[IFX_TAPI_CID_MSG_LEN_MAX-1] = '\0'; - count++; - } - - if ((time(×tamp) != -1) && ((tm=localtime(×tamp)) != NULL)) { - elements[count].date.elementType = IFX_TAPI_CID_ST_DATE; - elements[count].date.day = tm->tm_mday; - elements[count].date.month = tm->tm_mon + 1; - elements[count].date.hour = tm->tm_hour; - elements[count].date.mn = tm->tm_min; - count++; - } - - msg.txMode = IFX_TAPI_CID_HM_ONHOOK; - msg.messageType = IFX_TAPI_CID_MT_CSUP; - msg.message = elements; - msg.nMsgElements = count; - - status = (uint8_t) ioctl(dev_ctx.ch_fd[c], IFX_TAPI_CID_TX_SEQ_START, (IFX_int32_t) &msg); - } - } else { - status = (uint8_t) ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_STOP, 0); - led_off(dev_ctx.ch_led[c]); - } - - if (status) { - ast_log(LOG_ERROR, "%s ioctl failed\n", - (r ? "IFX_TAPI_RING_START" : "IFX_TAPI_RING_STOP")); - } -} - -/* add a frequency to TAPE tone structure */ -/* returns the TAPI frequency ID */ -static int tapitone_add_freq (IFX_TAPI_TONE_t *tone, IFX_uint32_t freq) { - const int n=4; /* TAPI tone structure supports up to 4 frequencies */ - int error=0; - int ret; - int i; - - /* pointer array for freq's A, B, C, D */ - IFX_uint32_t *freqarr[] = { &(tone->simple.freqA), &(tone->simple.freqB), &(tone->simple.freqC), &(tone->simple.freqD) }; - - /* pointer array for level's A, B, C, D */ - IFX_int32_t *lvlarr[] = { &(tone->simple.levelA), &(tone->simple.levelB), &(tone->simple.levelC), &(tone->simple.levelD) }; - - /* array for freq IDs */ - IFX_uint32_t retarr[] = { IFX_TAPI_TONE_FREQA, IFX_TAPI_TONE_FREQB, IFX_TAPI_TONE_FREQC, IFX_TAPI_TONE_FREQD }; - - /* determine if freq already set */ - for (i = 0; i < n; i++) { - if(*freqarr[i] == freq) /* freq found */ - break; - else if (i == n-1) /* last iteration */ - error=1; /* not found */ - } - - /* write frequency if not already set */ - if(error) { - error=0; /* reset error flag */ - /* since freq is not set, write it into first free place */ - for (i = 0; i < n; i++) { - if(!*freqarr[i]) { /* free place */ - *freqarr[i] = freq; /* set freq */ - *lvlarr[i] = -150; /* set volume level */ - break; - } else if (i == n-1) /* last iteration */ - error=1; /* no free place becaus maximum count of freq's is set */ - } - } - - /* set freq ID return value */ - if (!freq || error) - ret = IFX_TAPI_TONE_FREQNONE; - else - ret = retarr[i]; - - return ret; /* freq ID */ -} - -/* convert asterisk playlist string to tapi tone structure */ -/* based on ast_playtones_start() from indications.c of asterisk 13 */ -static void playlist_to_tapitone (const char *playlst, IFX_uint32_t index, IFX_TAPI_TONE_t *tone) -{ - char *s, *data = ast_strdupa(playlst); - char *stringp; - char *separator; - int i; - - /* initialize tapi tone structure */ - memset(tone, 0, sizeof(IFX_TAPI_TONE_t)); - tone->simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; - tone->simple.index = index; - - stringp = data; - - /* check if the data is separated with '|' or with ',' by default */ - if (strchr(stringp,'|')) { - separator = "|"; - } else { - separator = ","; - } - - for ( i = 0; (s = strsep(&stringp, separator)) && !ast_strlen_zero(s) && i < IFX_TAPI_TONE_STEPS_MAX; i++) { - struct ast_tone_zone_part tone_data = { - .time = 0, - }; - - s = ast_strip(s); - if (s[0]=='!') { - s++; - } - - if (ast_tone_zone_part_parse(s, &tone_data)) { - ast_log(LOG_ERROR, "Failed to parse tone part '%s'\n", s); - continue; - } - - /* first tone must hava a cadence */ - if (i==0 && !tone_data.time) - tone->simple.cadence[i] = 1000; - else - tone->simple.cadence[i] = tone_data.time; - - /* check for modulation */ - if (tone_data.modulate) { - tone->simple.modulation[i] = IFX_TAPI_TONE_MODULATION_ON; - tone->simple.modulation_factor = IFX_TAPI_TONE_MODULATION_FACTOR_90; - } - - /* copy freq's to tapi tone structure */ - /* a freq will implicitly skipped if it is zero */ - tone->simple.frequencies[i] |= tapitone_add_freq(tone, tone_data.freq1); - tone->simple.frequencies[i] |= tapitone_add_freq(tone, tone_data.freq2); - } -} - -static int lantiq_play_tone(int c, int t) -{ - /* stop currently playing tone before starting new one */ - ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_LOCAL_PLAY, TAPI_TONE_LOCALE_NONE); - - /* do not handle stop tone twice */ - if (t != TAPI_TONE_LOCALE_NONE) { - /* start new tone */ - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_LOCAL_PLAY, t)) { - ast_log(LOG_ERROR, "IFX_TAPI_TONE_LOCAL_PLAY ioctl failed\n"); - return -1; - } - } - - return 0; -} - -static enum channel_state lantiq_get_hookstatus(int port) -{ - uint8_t status; - - if (ioctl(dev_ctx.ch_fd[port], IFX_TAPI_LINE_HOOK_STATUS_GET, &status)) { - ast_log(LOG_ERROR, "IFX_TAPI_LINE_HOOK_STATUS_GET ioctl failed\n"); - return UNKNOWN; - } - - if (status) { - return OFFHOOK; - } else { - return ONHOOK; - } -} - -static int -lantiq_dev_binary_buffer_create(const char *path, uint8_t **ppBuf, uint32_t *pBufSz) -{ - FILE *fd; - struct stat file_stat; - int status = -1; - - fd = fopen(path, "rb"); - if (fd == NULL) { - ast_log(LOG_ERROR, "binary file %s open failed\n", path); - goto on_exit; - } - - if (stat(path, &file_stat)) { - ast_log(LOG_ERROR, "file %s statistics get failed\n", path); - goto on_exit; - } - - *ppBuf = malloc(file_stat.st_size); - if (*ppBuf == NULL) { - ast_log(LOG_ERROR, "binary file %s memory allocation failed\n", path); - goto on_exit; - } - - if (fread (*ppBuf, sizeof(uint8_t), file_stat.st_size, fd) != file_stat.st_size) { - ast_log(LOG_ERROR, "file %s read failed\n", path); - status = -1; - goto on_exit; - } - - *pBufSz = file_stat.st_size; - status = 0; - -on_exit: - if (fd != NULL) - fclose(fd); - - if (*ppBuf != NULL && status) - free(*ppBuf); - - return status; -} - -static int32_t lantiq_dev_firmware_download(int32_t fd, const char *path) -{ - uint8_t *firmware = NULL; - uint32_t size = 0; - VMMC_IO_INIT vmmc_io_init; - - ast_log(LOG_DEBUG, "loading firmware: \"%s\".\n", path); - - if (lantiq_dev_binary_buffer_create(path, &firmware, &size)) - return -1; - - memset(&vmmc_io_init, 0, sizeof(VMMC_IO_INIT)); - vmmc_io_init.pPRAMfw = firmware; - vmmc_io_init.pram_size = size; - - if (ioctl(fd, FIO_FW_DOWNLOAD, &vmmc_io_init)) { - ast_log(LOG_ERROR, "FIO_FW_DOWNLOAD ioctl failed\n"); - return -1; - } - - if (firmware != NULL) - free(firmware); - - return 0; -} - -static const char *state_string(enum channel_state s) -{ - switch (s) { - case ONHOOK: return "ONHOOK"; - case OFFHOOK: return "OFFHOOK"; - case DIALING: return "DIALING"; - case INCALL: return "INCALL"; - case CALL_ENDED: return "CALL_ENDED"; - case RINGING: return "RINGING"; - default: return "UNKNOWN"; - } -} - -static const char *control_string(int c) -{ - switch (c) { - case AST_CONTROL_HANGUP: return "Other end has hungup"; - case AST_CONTROL_RING: return "Local ring"; - case AST_CONTROL_RINGING: return "Remote end is ringing"; - case AST_CONTROL_ANSWER: return "Remote end has answered"; - case AST_CONTROL_BUSY: return "Remote end is busy"; - case AST_CONTROL_TAKEOFFHOOK: return "Make it go off hook"; - case AST_CONTROL_OFFHOOK: return "Line is off hook"; - case AST_CONTROL_CONGESTION: return "Congestion (circuits busy)"; - case AST_CONTROL_FLASH: return "Flash hook"; - case AST_CONTROL_WINK: return "Wink"; - case AST_CONTROL_OPTION: return "Set a low-level option"; - case AST_CONTROL_RADIO_KEY: return "Key Radio"; - case AST_CONTROL_RADIO_UNKEY: return "Un-Key Radio"; - case AST_CONTROL_PROGRESS: return "Remote end is making Progress"; - case AST_CONTROL_PROCEEDING: return "Remote end is proceeding"; - case AST_CONTROL_HOLD: return "Hold"; - case AST_CONTROL_UNHOLD: return "Unhold"; - case AST_CONTROL_SRCUPDATE: return "Media Source Update"; - case AST_CONTROL_CONNECTED_LINE: return "Connected Line"; - case AST_CONTROL_REDIRECTING: return "Redirecting"; - case AST_CONTROL_INCOMPLETE: return "Incomplete"; - case -1: return "Stop tone"; - default: return "Unknown"; - } -} - -static int ast_lantiq_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen) -{ - struct lantiq_pvt *pvt = ast_channel_tech_pvt(chan); - - ast_verb(3, "phone indication \"%s\"\n", control_string(condition)); - - switch (condition) { - case -1: - { - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_NONE); - return 0; - } - case AST_CONTROL_CONGESTION: - { - if (tone_generator == TONE_INTEGRATED) - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_CONGESTION_CODE); - else - ast_playtones_start(chan, 0, ts_congestion->data, 1); - - return 0; - } - case AST_CONTROL_BUSY: - { - if (tone_generator == TONE_INTEGRATED) - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_BUSY_CODE); - else - ast_playtones_start(chan, 0, ts_busy->data, 1); - - return 0; - } - case AST_CONTROL_RINGING: - case AST_CONTROL_PROGRESS: - { - pvt->call_setup_delay = now() - pvt->call_setup_start; - - if (tone_generator == TONE_INTEGRATED) - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_RINGING_CODE); - else if (tone_generator == TONE_ASTERISK) /* do nothing if TONE_MEDIA is set */ - ast_playtones_start(chan, 0, ts_ring->data, 1); - - return 0; - } - default: - { - /* -1 lets asterisk generate the tone */ - return -1; - } - } -} - -static int ast_lantiq_fixup(struct ast_channel *old, struct ast_channel *new) -{ - ast_log(LOG_DEBUG, "entering... no code here...\n"); - return 0; -} - -static int ast_digit_begin(struct ast_channel *chan, char digit) -{ - /* TODO: Modify this callback to let Asterisk support controlling the length of DTMF */ - ast_log(LOG_DEBUG, "entering... no code here...\n"); - return 0; -} - -static int ast_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - ast_log(LOG_DEBUG, "entering... no code here...\n"); - return 0; -} - -static int ast_lantiq_call(struct ast_channel *ast, const char *dest, int timeout) -{ - int res = 0; - struct lantiq_pvt *pvt; - - /* lock to prevent simultaneous access with do_monitor thread processing */ - ast_mutex_lock(&iflock); - - pvt = ast_channel_tech_pvt(ast); - ast_log(LOG_DEBUG, "state: %s\n", state_string(pvt->channel_state)); - - if (pvt->channel_state == ONHOOK) { - struct ast_party_id connected_id = ast_channel_connected_effective_id(ast); - const char *cid = connected_id.number.valid ? connected_id.number.str : NULL; - const char *name = connected_id.name.valid ? connected_id.name.str : NULL; - - ast_log(LOG_DEBUG, "port %i is ringing\n", pvt->port_id); - ast_log(LOG_DEBUG, "port %i CID: %s\n", pvt->port_id, cid ? cid : "none"); - ast_log(LOG_DEBUG, "port %i NAME: %s\n", pvt->port_id, name ? name : "none"); - - lantiq_ring(pvt->port_id, 1, cid, name); - pvt->channel_state = RINGING; - - ast_setstate(ast, AST_STATE_RINGING); - ast_queue_control(ast, AST_CONTROL_RINGING); - } else { - ast_log(LOG_DEBUG, "port %i is busy\n", pvt->port_id); - ast_setstate(ast, AST_STATE_BUSY); - ast_queue_control(ast, AST_CONTROL_BUSY); - res = -1; - } - - ast_mutex_unlock(&iflock); - - return res; -} - -static int ast_lantiq_hangup(struct ast_channel *ast) -{ - struct lantiq_pvt *pvt; - - /* lock to prevent simultaneous access with do_monitor thread processing */ - ast_mutex_lock(&iflock); - - pvt = ast_channel_tech_pvt(ast); - ast_log(LOG_DEBUG, "state: %s\n", state_string(pvt->channel_state)); - - if (ast_channel_state(ast) == AST_STATE_RINGING) { - ast_debug(1, "channel state is RINGING\n"); - } - - switch (pvt->channel_state) { - case RINGING: - case ONHOOK: - lantiq_ring(pvt->port_id, 0, NULL, NULL); - pvt->channel_state = ONHOOK; - break; - default: - ast_log(LOG_DEBUG, "we were hung up, play busy tone\n"); - pvt->channel_state = CALL_ENDED; - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_BUSY_CODE); - } - - lantiq_jb_get_stats(pvt->port_id); - - ast_setstate(ast, AST_STATE_DOWN); - ast_module_unref(ast_module_info->self); - ast_channel_tech_pvt_set(ast, NULL); - pvt->owner = NULL; - - ast_mutex_unlock(&iflock); - - return 0; -} - -static int ast_lantiq_answer(struct ast_channel *ast) -{ - struct lantiq_pvt *pvt = ast_channel_tech_pvt(ast); - - ast_log(LOG_DEBUG, "Remote end has answered call.\n"); - - if (lantiq_conf_enc(pvt->port_id, ast_channel_writeformat(ast))) - return -1; - - pvt->call_answer = epoch(); - - return 0; -} - -static struct ast_frame * ast_lantiq_read(struct ast_channel *ast) -{ - ast_log(LOG_DEBUG, "entering... no code here...\n"); - return NULL; -} - -/* create asterisk format from rtp payload type */ -static struct ast_format *lantiq_map_rtptype_to_format(uint8_t rtptype) -{ - struct ast_format *format = NULL; - - switch (rtptype) { - case RTP_PCMU: format = ast_format_ulaw; break; - case RTP_PCMA: format = ast_format_alaw; break; - case RTP_G722: format = ast_format_g722; break; - case RTP_G726: format = ast_format_g726; break; - case RTP_SLIN8: format = ast_format_slin; break; - case RTP_SLIN16: format = ast_format_slin16; break; - case RTP_ILBC: format = ast_format_ilbc; break; - case RTP_SIREN7: format = ast_format_siren7; break; - case RTP_G723_63: format = ast_format_g723; break; - case RTP_G723_53: format = ast_format_g723; break; - case RTP_G729: format = ast_format_g729; break; - default: - { - ast_log(LOG_ERROR, "unsupported rtptype received is 0x%x, forcing ulaw\n", (unsigned) rtptype); - format = ast_format_ulaw; - } - } - - return format; -} - -/* create rtp payload type from asterisk format */ -static uint8_t lantiq_map_format_to_rtptype(const struct ast_format *format) -{ - uint8_t rtptype = 0; - - if (ast_format_cmp(format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_PCMU; - else if (ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_PCMA; - else if (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_G722; - else if (ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_G726; - else if (ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_SLIN8; - else if (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_SLIN16; - else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_ILBC; - else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_SIREN7; - else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) -#if defined G723_HIGH_RATE - rtptype = RTP_G723_63; -#else - rtptype = RTP_G723_53; -#endif - else if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) - rtptype = RTP_G729; - else { - ast_log(LOG_ERROR, "unsupported format %s, forcing ulaw\n", ast_format_get_name(format)); - rtptype = RTP_PCMU; - } - - return rtptype; -} - -static int lantiq_conf_enc(int c, const struct ast_format *format) -{ - /* Configure encoder before starting RTP session */ - IFX_TAPI_ENC_CFG_t enc_cfg; - - memset(&enc_cfg, 0, sizeof(IFX_TAPI_ENC_CFG_t)); - - if (ast_format_cmp(format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_MLAW; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 10; - } else if (ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_ALAW; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 10; - } else if (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G722_64; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 20; - } else if (ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G726_32; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 10; - } else if (ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_LIN16_8; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 10; - } else if (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_LIN16_16; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_10; - iflist[c].ptime = 10; - } else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) { - /* iLBC 15.2kbps is currently unsupported by Asterisk */ - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_ILBC_133; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_30; - iflist[c].ptime = 30; - } else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G7221_32; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 20; - } else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) { -#if defined G723_HIGH_RATE - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G723_63; -#else - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G723_53; -#endif - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_30; - iflist[c].ptime = 30; - } else if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G729; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 10; - } else { - ast_log(LOG_ERROR, "unsupported format %s, forcing ulaw\n", ast_format_get_name(format)); - enc_cfg.nEncType = IFX_TAPI_COD_TYPE_MLAW; - enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; - iflist[c].ptime = 10; - } - - ast_log(LOG_DEBUG, "Configuring encoder to use TAPI codec type %d (%s) on channel %i\n", enc_cfg.nEncType, ast_format_get_name(format), c); - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_CFG_SET, &enc_cfg)) { - ast_log(LOG_ERROR, "IFX_TAPI_ENC_CFG_SET %d failed\n", c); - } - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_START, 0)) { - ast_log(LOG_ERROR, "IFX_TAPI_ENC_START ioctl failed\n"); - } - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_DEC_START, 0)) { - ast_log(LOG_ERROR, "IFX_TAPI_DEC_START ioctl failed\n"); - } - - return 0; -} - -static int ast_lantiq_write(struct ast_channel *ast, struct ast_frame *frame) -{ - char buf[RTP_BUFFER_LEN]; - rtp_header_t *rtp_header = (rtp_header_t *) buf; - struct lantiq_pvt *pvt = ast_channel_tech_pvt(ast); - int ret; - uint8_t rtptype; - int subframes, subframes_rtp, length, samples; - char *head, *tail; - - if(frame->frametype != AST_FRAME_VOICE) { - ast_log(LOG_DEBUG, "unhandled frame type\n"); - return 0; - } - - if (frame->datalen == 0) { - ast_log(LOG_DEBUG, "we've been prodded\n"); - return 0; - } - - /* get rtp payload type */ - rtptype = lantiq_map_format_to_rtptype(frame->subclass.format); - - rtp_header->version = 2; - rtp_header->padding = 0; - rtp_header->extension = 0; - rtp_header->csrc_count = 0; - rtp_header->marker = 0; - rtp_header->ssrc = 0; - rtp_header->payload_type = rtptype; - - subframes = (iflist[pvt->port_id].ptime + frame->len - 1) / iflist[pvt->port_id].ptime; /* number of subframes in AST frame */ - if (subframes == 0) - subframes = 1; - - subframes_rtp = (RTP_BUFFER_LEN - RTP_HEADER_LEN) * subframes / frame->datalen; /* how many frames fit in a single RTP packet */ - - /* By default stick to the maximum multiple of native frame length */ - length = subframes_rtp * frame->datalen / subframes; - samples = length * frame->samples / frame->datalen; - - head = frame->data.ptr; - tail = frame->data.ptr + frame->datalen; - while (head < tail) { - rtp_header->seqno = pvt->rtp_seqno++; - rtp_header->timestamp = pvt->rtp_timestamp; - - if ((tail - head) < (RTP_BUFFER_LEN - RTP_HEADER_LEN)) { - length = tail - head; - samples = length * frame->samples / frame->datalen; - } - - if ( frame->datalen <= (sizeof(buf) - RTP_HEADER_LEN) ) - memcpy(buf + RTP_HEADER_LEN, head, length); - else { - ast_log(LOG_WARNING, "buffer is too small\n"); - return -1; - } - - head += length; - pvt->rtp_timestamp += (rtp_header->payload_type == RTP_G722) ? samples / 2 : samples; /* per RFC3551 */ - - ret = write(dev_ctx.ch_fd[pvt->port_id], buf, RTP_HEADER_LEN + length); - if (ret < 0) { - ast_debug(1, "TAPI: ast_lantiq_write(): error writing.\n"); - return -1; - } - if (ret != (RTP_HEADER_LEN + length)) { - ast_log(LOG_WARNING, "Short TAPI write of %d bytes, expected %d bytes\n", ret, RTP_HEADER_LEN + length); - continue; - } - } - - return 0; -} - -static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *args, char *buf, size_t buflen) -{ - struct lantiq_pvt *pvt; - int res = 0; - - if (!chan || ast_channel_tech(chan) != &lantiq_tech) { - ast_log(LOG_ERROR, "This function requires a valid Lantiq TAPI channel\n"); - return -1; - } - - ast_mutex_lock(&iflock); - - pvt = (struct lantiq_pvt*) ast_channel_tech_pvt(chan); - - if (!strcasecmp(args, "csd")) { - snprintf(buf, buflen, "%lu", (unsigned long int) pvt->call_setup_delay); - } else if (!strcasecmp(args, "jitter_stats")){ - lantiq_jb_get_stats(pvt->port_id); - snprintf(buf, buflen, "jbBufSize=%u,jbUnderflow=%u,jbOverflow=%u,jbDelay=%u,jbInvalid=%u", - (uint32_t) pvt->jb_size, - (uint32_t) pvt->jb_underflow, - (uint32_t) pvt->jb_overflow, - (uint32_t) pvt->jb_delay, - (uint32_t) pvt->jb_invalid); - } else if (!strcasecmp(args, "jbBufSize")) { - snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_size); - } else if (!strcasecmp(args, "jbUnderflow")) { - snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_underflow); - } else if (!strcasecmp(args, "jbOverflow")) { - snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_overflow); - } else if (!strcasecmp(args, "jbDelay")) { - snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_delay); - } else if (!strcasecmp(args, "jbInvalid")) { - snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_invalid); - } else if (!strcasecmp(args, "start")) { - struct tm *tm = gmtime((const time_t*)&pvt->call_start); - strftime(buf, buflen, "%F %T", tm); - } else if (!strcasecmp(args, "answer")) { - struct tm *tm = gmtime((const time_t*)&pvt->call_answer); - strftime(buf, buflen, "%F %T", tm); - } else { - res = -1; - } - - ast_mutex_unlock(&iflock); - - return res; -} - -static struct ast_frame * ast_lantiq_exception(struct ast_channel *ast) -{ - ast_log(LOG_DEBUG, "entering... no code here...\n"); - return NULL; -} - -static void lantiq_jb_get_stats(int c) { - struct lantiq_pvt *pvt = &iflist[c]; - - IFX_TAPI_JB_STATISTICS_t param; - memset (¶m, 0, sizeof (param)); - if (ioctl (dev_ctx.ch_fd[c], IFX_TAPI_JB_STATISTICS_GET, (IFX_int32_t) ¶m) != IFX_SUCCESS) { - ast_debug(1, "Error getting jitter buffer stats.\n"); - } else { -#if !defined (TAPI_VERSION3) && defined (TAPI_VERSION4) - ast_debug(1, "Jitter buffer stats: dev=%u, ch=%u, nType=%u, nBufSize=%u, nIsUnderflow=%u, nDsOverflow=%u, nPODelay=%u, nInvalid=%u\n", - (uint32_t) param.dev, - (uint32_t) param.ch, -#else - ast_debug(1, "Jitter buffer stats: nType=%u, nBufSize=%u, nIsUnderflow=%u, nDsOverflow=%u, nPODelay=%u, nInvalid=%u\n", -#endif - (uint32_t) param.nType, - (uint32_t) param.nBufSize, - (uint32_t) param.nIsUnderflow, - (uint32_t) param.nDsOverflow, - (uint32_t) param.nPODelay, - (uint32_t) param.nInvalid); - - pvt->jb_size = param.nBufSize; - pvt->jb_underflow = param.nIsUnderflow; - pvt->jb_overflow = param.nDsOverflow; - pvt->jb_invalid = param.nInvalid; - pvt->jb_delay = param.nPODelay; - } -} - - -static int lantiq_standby(int c) -{ - ast_debug(1, "Stopping line feed for channel %i\n", c); - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY)) { - ast_log(LOG_ERROR, "IFX_TAPI_LINE_FEED_SET ioctl failed\n"); - return -1; - } - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_STOP, 0)) { - ast_log(LOG_ERROR, "IFX_TAPI_ENC_STOP ioctl failed\n"); - return -1; - } - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_DEC_STOP, 0)) { - ast_log(LOG_ERROR, "IFX_TAPI_DEC_STOP ioctl failed\n"); - return -1; - } - - return lantiq_play_tone(c, TAPI_TONE_LOCALE_NONE); -} - -static int lantiq_end_dialing(int c) -{ - struct lantiq_pvt *pvt = &iflist[c]; - - ast_log(LOG_DEBUG, "end of dialing\n"); - - if (pvt->dial_timer != -1) { - AST_SCHED_DEL(sched, pvt->dial_timer); - pvt->dial_timer = -1; - } - - if(pvt->owner) { - ast_hangup(pvt->owner); - } - lantiq_reset_dtmfbuf(pvt); - - return 0; -} - -static int lantiq_end_call(int c) -{ - struct lantiq_pvt *pvt = &iflist[c]; - - ast_log(LOG_DEBUG, "end of call\n"); - - if(pvt->owner) { - lantiq_jb_get_stats(c); - ast_queue_hangup(pvt->owner); - } - - return 0; -} - -static struct ast_channel *lantiq_channel(int state, int c, char *ext, char *ctx, struct ast_format_cap *cap, const struct ast_assigned_ids *assigned_ids, const struct ast_channel *requestor) -{ - struct ast_channel *chan = NULL; - struct lantiq_pvt *pvt = &iflist[c]; - struct ast_format *format = NULL; - struct ast_format_cap *newcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); - struct ast_format_cap *formatcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); - - if (!newcap || !formatcap) { - ast_log(LOG_DEBUG, "Cannot allocate format capabilities!\n"); - return NULL; - } - - chan = ast_channel_alloc(1, state, NULL, NULL, "", ext, ctx, assigned_ids, requestor, c, "TAPI/%d", (c + 1)); - if (!chan) { - ast_log(LOG_DEBUG, "Cannot allocate channel!\n"); - ao2_ref(newcap, -1); - ao2_ref(formatcap, -1); - return NULL; - } - - ast_channel_tech_set(chan, &lantiq_tech); - ast_channel_tech_pvt_set(chan, pvt); - pvt->owner = chan; - - if (cap && ast_format_cap_iscompatible(cap, lantiq_tech.capabilities)) { /* compatible format capabilities given */ - ast_format_cap_get_compatible(lantiq_tech.capabilities, cap, newcap); - format = ast_format_cap_get_format(newcap, 0); /* choose format */ - } else { /* no or unsupported format capabilities given */ - format = ast_format_cap_get_format(lantiq_tech.capabilities, 0); /* choose format from capabilities */ - } - - /* set choosed format */ - ast_format_cap_append(formatcap, format, 0); - ast_channel_nativeformats_set(chan, formatcap); - ast_channel_set_readformat(chan, format); - ast_channel_set_writeformat(chan, format); - ast_channel_set_rawreadformat(chan, format); - ast_channel_set_rawwriteformat(chan, format); - - ao2_ref(newcap, -1); - ao2_ref(formatcap, -1); - - /* configuring encoder */ - if (format != 0) - if (lantiq_conf_enc(c, format) < 0) - return NULL; - - return chan; -} - -static struct ast_channel *ast_lantiq_requester(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assigned_ids, const struct ast_channel *requestor, const char *data, int *cause) -{ - struct ast_str *buf = ast_str_alloca(64); - struct ast_channel *chan = NULL; - int port_id = -1; - - ast_mutex_lock(&iflock); - - ast_debug(1, "Asked to create a TAPI channel with formats: %s\n", ast_format_cap_get_names(cap, &buf)); - - /* check for correct data argument */ - if (ast_strlen_zero(data)) { - ast_log(LOG_ERROR, "Unable to create channel with empty destination.\n"); - *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE; - goto bailout; - } - - /* get our port number */ - port_id = atoi((char*) data); - if (port_id < 1 || port_id > dev_ctx.channels) { - ast_log(LOG_ERROR, "Unknown channel ID: \"%s\"\n", data); - *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE; - goto bailout; - } - - /* on asterisk user's side, we're using port 1-2. - * Here in non normal human's world, we begin - * counting at 0. - */ - port_id -= 1; - - - /* Bail out if channel is already in use */ - struct lantiq_pvt *pvt = &iflist[port_id]; - if (! pvt->channel_state == ONHOOK) { - ast_debug(1, "TAPI channel %i alread in use.\n", port_id+1); - } else { - chan = lantiq_channel(AST_STATE_DOWN, port_id, NULL, NULL, cap, assigned_ids, requestor); - ast_channel_unlock(chan); /* it's essential to unlock channel */ - } - -bailout: - ast_mutex_unlock(&iflock); - return chan; -} - -static int ast_lantiq_devicestate(const char *data) -{ - int port = atoi(data) - 1; - if ((port < 1) || (port > dev_ctx.channels)) { - return AST_DEVICE_INVALID; - } - - switch (iflist[port].channel_state) { - case ONHOOK: - return AST_DEVICE_NOT_INUSE; - case OFFHOOK: - case DIALING: - case INCALL: - case CALL_ENDED: - return AST_DEVICE_INUSE; - case RINGING: - return AST_DEVICE_RINGING; - case UNKNOWN: - default: - return AST_DEVICE_UNKNOWN; - } -} - -static int lantiq_dev_data_handler(int c) -{ - char buf[BUFSIZ]; - struct ast_frame frame = {0}; - struct ast_format *format = NULL; - - int res = read(dev_ctx.ch_fd[c], buf, sizeof(buf)); - if (res <= 0) { - ast_log(LOG_ERROR, "we got read error %i\n", res); - return -1; - } else if (res < RTP_HEADER_LEN) { - ast_log(LOG_ERROR, "we got data smaller than header size\n"); - return -1; - } - - rtp_header_t *rtp = (rtp_header_t*) buf; - struct lantiq_pvt *pvt = (struct lantiq_pvt *) &iflist[c]; - if ((!pvt->owner) || (ast_channel_state(pvt->owner) != AST_STATE_UP)) { - return 0; - } - - if (rtp->payload_type == RTP_CN) { - /* TODO: Handle Comfort Noise frames */ - ast_debug(1, "Dropping Comfort Noise frame\n"); - } - - format = lantiq_map_rtptype_to_format(rtp->payload_type); - frame.src = "TAPI"; - frame.frametype = AST_FRAME_VOICE; - frame.subclass.format = format; - frame.datalen = res - RTP_HEADER_LEN; - frame.data.ptr = buf + RTP_HEADER_LEN; - frame.samples = ast_codec_samples_count(&frame); - - if(!ast_channel_trylock(pvt->owner)) { - ast_queue_frame(pvt->owner, &frame); - ast_channel_unlock(pvt->owner); - } - - return 0; -} - -static int accept_call(int c) -{ - struct lantiq_pvt *pvt = &iflist[c]; - - ast_log(LOG_DEBUG, "accept call\n"); - - if (pvt->owner) { - struct ast_channel *chan = pvt->owner; - - switch (ast_channel_state(chan)) { - case AST_STATE_RINGING: - lantiq_play_tone(c, TAPI_TONE_LOCALE_NONE); - ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); - pvt->channel_state = INCALL; - pvt->call_start = epoch(); - pvt->call_answer = pvt->call_start; - break; - default: - ast_log(LOG_WARNING, "entered unhandled state %s\n", ast_state2str(ast_channel_state(chan))); - } - } - - return 0; -} - -static int lantiq_dev_event_hook(int c, int state) -{ - ast_mutex_lock(&iflock); - - ast_log(LOG_DEBUG, "on port %i detected event %s hook\n", c, state ? "on" : "off"); - - struct lantiq_pvt *pvt = &iflist[c]; - - int ret = -1; - if (state) { /* going onhook */ - switch (iflist[c].channel_state) { - case DIALING: - ret = lantiq_end_dialing(c); - break; - case INCALL: - ret = lantiq_end_call(c); - break; - } - - iflist[c].channel_state = ONHOOK; - - /* stop DSP data feed */ - lantiq_standby(c); - led_off(dev_ctx.ch_led[c]); - - } else { /* going offhook */ - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_ACTIVE)) { - ast_log(LOG_ERROR, "IFX_TAPI_LINE_FEED_SET ioctl failed\n"); - goto out; - } - - switch (iflist[c].channel_state) { - case RINGING: - ret = accept_call(c); - led_blink(dev_ctx.ch_led[c], LED_SLOW_BLINK); - break; - default: - iflist[c].channel_state = OFFHOOK; - lantiq_play_tone(c, TAPI_TONE_LOCALE_DIAL_CODE); - lantiq_reset_dtmfbuf(pvt); - ret = 0; - led_on(dev_ctx.ch_led[c]); - break; - } - - } - -out: - ast_mutex_unlock(&iflock); - - return ret; -} - -static void lantiq_reset_dtmfbuf(struct lantiq_pvt *pvt) -{ - pvt->dtmfbuf[0] = '\0'; - pvt->dtmfbuf_len = 0; -} - -static void lantiq_dial(struct lantiq_pvt *pvt) -{ - struct ast_channel *chan = NULL; - - ast_mutex_lock(&iflock); - ast_log(LOG_DEBUG, "user want's to dial %s.\n", pvt->dtmfbuf); - - if (ast_exists_extension(NULL, pvt->context, pvt->dtmfbuf, 1, NULL)) { - ast_debug(1, "found extension %s, dialing\n", pvt->dtmfbuf); - - ast_verbose(VERBOSE_PREFIX_3 " extension exists, starting PBX %s\n", pvt->dtmfbuf); - - chan = lantiq_channel(AST_STATE_UP, pvt->port_id, pvt->dtmfbuf, pvt->context, NULL, 0, NULL); - if (!chan) { - ast_log(LOG_ERROR, "couldn't create channel\n"); - goto bailout; - } - ast_channel_tech_pvt_set(chan, pvt); - pvt->owner = chan; - - ast_setstate(chan, AST_STATE_RING); - pvt->channel_state = INCALL; - - pvt->call_setup_start = now(); - pvt->call_start = epoch(); - - if (ast_pbx_start(chan)) { - ast_log(LOG_WARNING, " unable to start PBX on %s\n", ast_channel_name(chan)); - ast_hangup(chan); - } - - ast_channel_unlock(chan); /* it's essential to unlock channel */ - } else { - ast_log(LOG_DEBUG, "no extension found\n"); - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_CONGESTION_CODE); - pvt->channel_state = CALL_ENDED; - } - - lantiq_reset_dtmfbuf(pvt); -bailout: - ast_mutex_unlock(&iflock); -} - -static int lantiq_event_dial_timeout(const void* data) -{ - ast_debug(1, "TAPI: lantiq_event_dial_timeout()\n"); - - struct lantiq_pvt *pvt = (struct lantiq_pvt *) data; - pvt->dial_timer = -1; - - if (! pvt->channel_state == ONHOOK) { - lantiq_dial(pvt); - } else { - ast_debug(1, "TAPI: lantiq_event_dial_timeout(): dial timeout in state ONHOOK.\n"); - } - - return 0; -} - -static int lantiq_send_digit(int c, char digit) -{ - struct lantiq_pvt *pvt = &iflist[c]; - - struct ast_frame f = { .frametype = AST_FRAME_DTMF, .subclass.integer = digit }; - - if (pvt->owner) { - ast_log(LOG_DEBUG, "Port %i transmitting digit \"%c\"\n", c, digit); - return ast_queue_frame(pvt->owner, &f); - } else { - ast_debug(1, "Warning: lantiq_send_digit() without owner!\n"); - return -1; - } -} - -static void lantiq_dev_event_digit(int c, char digit) -{ - ast_mutex_lock(&iflock); - - ast_log(LOG_DEBUG, "on port %i detected digit \"%c\"\n", c, digit); - - struct lantiq_pvt *pvt = &iflist[c]; - - switch (pvt->channel_state) { - case INCALL: - lantiq_send_digit(c, digit); - break; - case OFFHOOK: - pvt->channel_state = DIALING; - - lantiq_play_tone(c, TAPI_TONE_LOCALE_NONE); - led_blink(dev_ctx.ch_led[c], LED_SLOW_BLINK); - - /* fall through */ - case DIALING: - if (pvt->dtmfbuf_len < AST_MAX_EXTENSION - 1) { - pvt->dtmfbuf[pvt->dtmfbuf_len] = digit; - pvt->dtmfbuf[++pvt->dtmfbuf_len] = '\0'; - } else { - /* No more room for another digit */ - lantiq_end_dialing(c); - lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_BUSY_CODE); - pvt->channel_state = CALL_ENDED; - break; - } - - /* setup autodial timer */ - if (pvt->dial_timer == -1) { - ast_log(LOG_DEBUG, "setting new timer\n"); - pvt->dial_timer = ast_sched_add(sched, dev_ctx.interdigit_timeout, lantiq_event_dial_timeout, (const void*) pvt); - } else { - ast_log(LOG_DEBUG, "replacing timer\n"); - AST_SCHED_REPLACE(pvt->dial_timer, sched, dev_ctx.interdigit_timeout, lantiq_event_dial_timeout, (const void*) pvt); - } - break; - default: - ast_log(LOG_ERROR, "don't know what to do in unhandled state\n"); - break; - } - - ast_mutex_unlock(&iflock); - return; -} - -static void lantiq_dev_event_handler(void) -{ - IFX_TAPI_EVENT_t event; - unsigned int i; - - for (i = 0; i < dev_ctx.channels ; i++) { - ast_mutex_lock(&iflock); - - memset (&event, 0, sizeof(event)); - event.ch = i; - if (ioctl(dev_ctx.dev_fd, IFX_TAPI_EVENT_GET, &event)) { - ast_mutex_unlock(&iflock); - continue; - } - if (event.id == IFX_TAPI_EVENT_NONE) { - ast_mutex_unlock(&iflock); - continue; - } - - ast_mutex_unlock(&iflock); - - switch(event.id) { - case IFX_TAPI_EVENT_FXS_ONHOOK: - lantiq_dev_event_hook(i, 1); - break; - case IFX_TAPI_EVENT_FXS_OFFHOOK: - lantiq_dev_event_hook(i, 0); - break; - case IFX_TAPI_EVENT_DTMF_DIGIT: - lantiq_dev_event_digit(i, (char)event.data.dtmf.ascii); - break; - case IFX_TAPI_EVENT_PULSE_DIGIT: - if (event.data.pulse.digit == 0xB) { - lantiq_dev_event_digit(i, '0'); - } else { - lantiq_dev_event_digit(i, '0' + (char)event.data.pulse.digit); - } - break; - case IFX_TAPI_EVENT_COD_DEC_CHG: - case IFX_TAPI_EVENT_TONE_GEN_END: - case IFX_TAPI_EVENT_CID_TX_SEQ_END: - break; - default: - ast_log(LOG_ERROR, "Unknown TAPI event %08X. Restarting Asterisk...\n", event.id); - sleep(1); - ast_cli_command(-1, "core restart now"); - break; - } - } -} - -static void * lantiq_events_monitor(void *data) -{ - ast_verbose("TAPI thread started\n"); - - struct pollfd fds[TAPI_AUDIO_PORT_NUM_MAX + 1]; - int c; - - fds[0].fd = dev_ctx.dev_fd; - fds[0].events = POLLIN; - for (c = 0; c < dev_ctx.channels; c++) { - fds[c + 1].fd = dev_ctx.ch_fd[c]; - fds[c + 1].events = POLLIN; - } - - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - for (;;) { - if (poll(fds, dev_ctx.channels + 1, 2000) <= 0) { - continue; - } - - ast_mutex_lock(&monlock); - if (fds[0].revents & POLLIN) { - lantiq_dev_event_handler(); - } - - for (c = 0; c < dev_ctx.channels; c++) { - if ((fds[c + 1].revents & POLLIN) && (lantiq_dev_data_handler(c))) { - ast_log(LOG_ERROR, "data handler %d failed\n", c); - break; - } - } - ast_mutex_unlock(&monlock); - } - - return NULL; -} - -static int restart_monitor(void) -{ - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - - ast_mutex_lock(&monlock); - - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - - if (monitor_thread != AST_PTHREADT_NULL) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, lantiq_events_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - } - ast_mutex_unlock(&monlock); - - return 0; -} - -static void lantiq_cleanup(void) -{ - int c; - - if (dev_ctx.dev_fd < 0) { - return; - } - - for (c = 0; c < dev_ctx.channels ; c++) { - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY)) { - ast_log(LOG_WARNING, "IFX_TAPI_LINE_FEED_SET ioctl failed\n"); - } - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_STOP, 0)) { - ast_log(LOG_WARNING, "IFX_TAPI_ENC_STOP ioctl failed\n"); - } - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_DEC_STOP, 0)) { - ast_log(LOG_WARNING, "IFX_TAPI_DEC_STOP ioctl failed\n"); - } - led_off(dev_ctx.ch_led[c]); - } - - if (ioctl(dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0)) { - ast_log(LOG_WARNING, "IFX_TAPI_DEV_STOP ioctl failed\n"); - } - - close(dev_ctx.dev_fd); - dev_ctx.dev_fd = -1; - led_off(dev_ctx.voip_led); -} - -static int unload_module(void) -{ - int c; - - ast_channel_unregister(&lantiq_tech); - - if (ast_mutex_lock(&iflock)) { - ast_log(LOG_WARNING, "Unable to lock the interface list\n"); - return -1; - } - for (c = 0; c < dev_ctx.channels ; c++) { - if (iflist[c].owner) - ast_softhangup(iflist[c].owner, AST_SOFTHANGUP_APPUNLOAD); - } - ast_mutex_unlock(&iflock); - - if (ast_mutex_lock(&monlock)) { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { - pthread_t th = monitor_thread; - monitor_thread = AST_PTHREADT_STOP; - pthread_cancel(th); - pthread_kill(th, SIGURG); - ast_mutex_unlock(&monlock); - pthread_join(th, NULL); - } else { - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - } - - ast_sched_context_destroy(sched); - ast_mutex_destroy(&iflock); - ast_mutex_destroy(&monlock); - - lantiq_cleanup(); - ast_free(iflist); - - return 0; -} - -static struct lantiq_pvt *lantiq_init_pvt(struct lantiq_pvt *pvt) -{ - if (pvt) { - pvt->owner = NULL; - pvt->port_id = -1; - pvt->channel_state = UNKNOWN; - pvt->context[0] = '\0'; - pvt->dial_timer = -1; - pvt->dtmfbuf[0] = '\0'; - pvt->dtmfbuf_len = 0; - pvt->call_setup_start = 0; - pvt->call_setup_delay = 0; - pvt->call_answer = 0; - pvt->jb_size = 0; - pvt->jb_underflow = 0; - pvt->jb_overflow = 0; - pvt->jb_delay = 0; - pvt->jb_invalid = 0; - } else { - ast_log(LOG_ERROR, "unable to clear pvt structure\n"); - } - - return pvt; -} - -static int lantiq_create_pvts(void) -{ - int i; - - iflist = ast_calloc(1, sizeof(struct lantiq_pvt) * dev_ctx.channels); - - if (!iflist) { - ast_log(LOG_ERROR, "unable to allocate memory\n"); - return -1; - } - - for (i = 0; i < dev_ctx.channels; i++) { - lantiq_init_pvt(&iflist[i]); - iflist[i].port_id = i; - if (per_channel_context) { - snprintf(iflist[i].context, AST_MAX_CONTEXT, "%s%i", LANTIQ_CONTEXT_PREFIX, i + 1); - ast_debug(1, "Context for channel %i: %s\n", i, iflist[i].context); - } else { - snprintf(iflist[i].context, AST_MAX_CONTEXT, "default"); - } - } - return 0; -} - -static int lantiq_setup_rtp(int c) -{ - /* Configure RTP payload type tables */ - IFX_TAPI_PKT_RTP_PT_CFG_t rtpPTConf; - - memset((char*)&rtpPTConf, '\0', sizeof(rtpPTConf)); - - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G723_63] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G723_63] = RTP_G723_63; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G723_53] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G723_53] = RTP_G723_53; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G729] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G729] = RTP_G729; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_MLAW] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_MLAW] = RTP_PCMU; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_ALAW] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_ALAW] = RTP_PCMA; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G726_32] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G726_32] = RTP_G726; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_ILBC_152] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_ILBC_152] = RTP_ILBC; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_LIN16_8] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_LIN16_8] = RTP_SLIN8; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_LIN16_16] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_LIN16_16] = RTP_SLIN16; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G722_64] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G722_64] = RTP_G722; - rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G7221_32] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G7221_32] = RTP_G7221; - - int ret; - if ((ret = ioctl(dev_ctx.ch_fd[c], IFX_TAPI_PKT_RTP_PT_CFG_SET, (IFX_int32_t) &rtpPTConf))) { - ast_log(LOG_ERROR, "IFX_TAPI_PKT_RTP_PT_CFG_SET failed: ret=%i\n", ret); - return -1; - } - - return 0; -} - -static int load_module(void) -{ - struct ast_config *cfg; - struct ast_variable *v; - int txgain = 0; - int rxgain = 0; - int wlec_type = 0; - int wlec_nlp = 0; - int wlec_nbfe = 0; - int wlec_nbne = 0; - int wlec_wbne = 0; - int jb_type = IFX_TAPI_JB_TYPE_ADAPTIVE; - int jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_VOICE; - int jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_DEFAULT; - int jb_scaling = 0x10; - int jb_initialsize = 0x2d0; - int jb_minsize = 0x50; - int jb_maxsize = 0x5a0; - int cid_type = IFX_TAPI_CID_STD_TELCORDIA; - int vad_type = IFX_TAPI_ENC_VAD_NOVAD; - dev_ctx.dev_fd = -1; - dev_ctx.channels = TAPI_AUDIO_PORT_NUM_MAX; - dev_ctx.interdigit_timeout = DEFAULT_INTERDIGIT_TIMEOUT; - struct ast_tone_zone *tz; - struct ast_flags config_flags = { 0 }; - int c; - - if(!(lantiq_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { - ast_log(LOG_ERROR, "Unable to allocate format capabilities.\n"); - return AST_MODULE_LOAD_DECLINE; - } - - /* channel format capabilities */ - ast_format_cap_append(lantiq_tech.capabilities, ast_format_ulaw, 0); - ast_format_cap_append(lantiq_tech.capabilities, ast_format_alaw, 0); - ast_format_cap_append(lantiq_tech.capabilities, ast_format_g722, 0); - ast_format_cap_append(lantiq_tech.capabilities, ast_format_g726, 0); - ast_format_cap_append(lantiq_tech.capabilities, ast_format_slin, 0); - - /* Turn off the LEDs, just in case */ - led_off(dev_ctx.voip_led); - for(c = 0; c < TAPI_AUDIO_PORT_NUM_MAX; c++) - led_off(dev_ctx.ch_led[c]); - - if ((cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) { - ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", config); - return AST_MODULE_LOAD_DECLINE; - } - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_ERROR, "Unable to load config %s\n", config); - return AST_MODULE_LOAD_DECLINE; - } - - if (ast_mutex_lock(&iflock)) { - ast_log(LOG_ERROR, "Unable to lock interface list.\n"); - goto cfg_error; - } - - for (v = ast_variable_browse(cfg, "interfaces"); v; v = v->next) { - if (!strcasecmp(v->name, "channels")) { - dev_ctx.channels = atoi(v->value); - if (!dev_ctx.channels) { - ast_log(LOG_ERROR, "Invalid value for channels in config %s\n", config); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "firmwarefilename")) { - ast_copy_string(firmware_filename, v->value, sizeof(firmware_filename)); - } else if (!strcasecmp(v->name, "bbdfilename")) { - ast_copy_string(bbd_filename, v->value, sizeof(bbd_filename)); - } else if (!strcasecmp(v->name, "basepath")) { - ast_copy_string(base_path, v->value, sizeof(base_path)); - } else if (!strcasecmp(v->name, "per_channel_context")) { - if (!strcasecmp(v->value, "on")) { - per_channel_context = 1; - } else if (!strcasecmp(v->value, "off")) { - per_channel_context = 0; - } else { - ast_log(LOG_ERROR, "Unknown per_channel_context value '%s'. Try 'on' or 'off'.\n", v->value); - goto cfg_error_il; - } - } - } - - for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { - if (!strcasecmp(v->name, "rxgain")) { - rxgain = atoi(v->value); - if (!rxgain) { - rxgain = 0; - ast_log(LOG_WARNING, "Invalid rxgain: %s, using default.\n", v->value); - } - } else if (!strcasecmp(v->name, "txgain")) { - txgain = atoi(v->value); - if (!txgain) { - txgain = 0; - ast_log(LOG_WARNING, "Invalid txgain: %s, using default.\n", v->value); - } - } else if (!strcasecmp(v->name, "echocancel")) { - if (!strcasecmp(v->value, "off")) { - wlec_type = IFX_TAPI_WLEC_TYPE_OFF; - } else if (!strcasecmp(v->value, "nlec")) { - wlec_type = IFX_TAPI_WLEC_TYPE_NE; - if (!strcasecmp(v->name, "echocancelfixedwindowsize")) { - wlec_nbne = atoi(v->value); - } - } else if (!strcasecmp(v->value, "wlec")) { - wlec_type = IFX_TAPI_WLEC_TYPE_NFE; - if (!strcasecmp(v->name, "echocancelnfemovingwindowsize")) { - wlec_nbfe = atoi(v->value); - } else if (!strcasecmp(v->name, "echocancelfixedwindowsize")) { - wlec_nbne = atoi(v->value); - } else if (!strcasecmp(v->name, "echocancelwidefixedwindowsize")) { - wlec_wbne = atoi(v->value); - } - } else if (!strcasecmp(v->value, "nees")) { - wlec_type = IFX_TAPI_WLEC_TYPE_NE_ES; - } else if (!strcasecmp(v->value, "nfees")) { - wlec_type = IFX_TAPI_WLEC_TYPE_NFE_ES; - } else if (!strcasecmp(v->value, "es")) { - wlec_type = IFX_TAPI_WLEC_TYPE_ES; - } else { - wlec_type = IFX_TAPI_WLEC_TYPE_OFF; - ast_log(LOG_ERROR, "Unknown echo cancellation type '%s'\n", v->value); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "echocancelnlp")) { - if (!strcasecmp(v->value, "on")) { - wlec_nlp = IFX_TAPI_WLEC_NLP_ON; - } else if (!strcasecmp(v->value, "off")) { - wlec_nlp = IFX_TAPI_WLEC_NLP_OFF; - } else { - ast_log(LOG_ERROR, "Unknown echo cancellation nlp '%s'\n", v->value); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "jitterbuffertype")) { - if (!strcasecmp(v->value, "fixed")) { - jb_type = IFX_TAPI_JB_TYPE_FIXED; - } else if (!strcasecmp(v->value, "adaptive")) { - jb_type = IFX_TAPI_JB_TYPE_ADAPTIVE; - jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_DEFAULT; - if (!strcasecmp(v->name, "jitterbufferadaptation")) { - if (!strcasecmp(v->value, "on")) { - jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_ON; - } else if (!strcasecmp(v->value, "off")) { - jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_OFF; - } - } else if (!strcasecmp(v->name, "jitterbufferscalling")) { - jb_scaling = atoi(v->value); - } else if (!strcasecmp(v->name, "jitterbufferinitialsize")) { - jb_initialsize = atoi(v->value); - } else if (!strcasecmp(v->name, "jitterbufferminsize")) { - jb_minsize = atoi(v->value); - } else if (!strcasecmp(v->name, "jitterbuffermaxsize")) { - jb_maxsize = atoi(v->value); - } - } else { - ast_log(LOG_ERROR, "Unknown jitter buffer type '%s'\n", v->value); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "jitterbufferpackettype")) { - if (!strcasecmp(v->value, "voice")) { - jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_VOICE; - } else if (!strcasecmp(v->value, "data")) { - jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_DATA; - } else if (!strcasecmp(v->value, "datanorep")) { - jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_DATA_NO_REP; - } else { - ast_log(LOG_ERROR, "Unknown jitter buffer packet adaptation type '%s'\n", v->value); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "calleridtype")) { - ast_log(LOG_DEBUG, "Setting CID type to %s.\n", v->value); - if (!strcasecmp(v->value, "telecordia")) { - cid_type = IFX_TAPI_CID_STD_TELCORDIA; - } else if (!strcasecmp(v->value, "etsifsk")) { - cid_type = IFX_TAPI_CID_STD_ETSI_FSK; - } else if (!strcasecmp(v->value, "etsidtmf")) { - cid_type = IFX_TAPI_CID_STD_ETSI_DTMF; - } else if (!strcasecmp(v->value, "sin")) { - cid_type = IFX_TAPI_CID_STD_SIN; - } else if (!strcasecmp(v->value, "ntt")) { - cid_type = IFX_TAPI_CID_STD_NTT; - } else if (!strcasecmp(v->value, "kpndtmf")) { - cid_type = IFX_TAPI_CID_STD_KPN_DTMF; - } else if (!strcasecmp(v->value, "kpndtmffsk")) { - cid_type = IFX_TAPI_CID_STD_KPN_DTMF_FSK; - } else { - ast_log(LOG_ERROR, "Unknown caller id type '%s'\n", v->value); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "voiceactivitydetection")) { - if (!strcasecmp(v->value, "on")) { - vad_type = IFX_TAPI_ENC_VAD_ON; - } else if (!strcasecmp(v->value, "g711")) { - vad_type = IFX_TAPI_ENC_VAD_G711; - } else if (!strcasecmp(v->value, "cng")) { - vad_type = IFX_TAPI_ENC_VAD_CNG_ONLY; - } else if (!strcasecmp(v->value, "sc")) { - vad_type = IFX_TAPI_ENC_VAD_SC_ONLY; - } else { - ast_log(LOG_ERROR, "Unknown voice activity detection value '%s'\n", v->value); - goto cfg_error_il; - } - } else if (!strcasecmp(v->name, "interdigit")) { - dev_ctx.interdigit_timeout = atoi(v->value); - ast_log(LOG_DEBUG, "Setting interdigit timeout to %s.\n", v->value); - if (!dev_ctx.interdigit_timeout) { - dev_ctx.interdigit_timeout = DEFAULT_INTERDIGIT_TIMEOUT; - ast_log(LOG_WARNING, "Invalid interdigit timeout: %s, using default.\n", v->value); - } - } else if (!strcasecmp(v->name, "tone_generator")) { - if (!strcasecmp(v->value, "integrated")) { - tone_generator = TONE_INTEGRATED; - } else if (!strcasecmp(v->value, "asterisk")) { - tone_generator = TONE_ASTERISK; - } else if (!strcasecmp(v->value, "media")) { - tone_generator = TONE_MEDIA; - } else { - ast_log(LOG_ERROR, "Unknown tone_generator value '%s'. Try 'integrated', 'asterisk' or 'media'.\n", v->value); - goto cfg_error_il; - } - } - } - - lantiq_create_pvts(); - - ast_mutex_unlock(&iflock); - ast_config_destroy(cfg); - - if (!(sched = ast_sched_context_create())) { - ast_log(LOG_ERROR, "Unable to create scheduler context\n"); - goto load_error; - } - - if (ast_sched_start_thread(sched)) { - ast_log(LOG_ERROR, "Unable to create scheduler context thread\n"); - goto load_error_st; - } - - if (ast_channel_register(&lantiq_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Phone'\n"); - goto load_error_st; - } - - /* tapi */ - IFX_TAPI_TONE_t tone; - IFX_TAPI_DEV_START_CFG_t dev_start; - IFX_TAPI_MAP_DATA_t map_data; - IFX_TAPI_LINE_TYPE_CFG_t line_type; - IFX_TAPI_LINE_VOLUME_t line_vol; - IFX_TAPI_WLEC_CFG_t wlec_cfg; - IFX_TAPI_JB_CFG_t jb_cfg; - IFX_TAPI_CID_CFG_t cid_cfg; - - /* open device */ - dev_ctx.dev_fd = lantiq_dev_open(base_path, 0); - - if (dev_ctx.dev_fd < 0) { - ast_log(LOG_ERROR, "lantiq TAPI device open function failed\n"); - goto load_error_st; - } - - snprintf(dev_ctx.voip_led, LED_NAME_LENGTH, "voice"); - for (c = 0; c < dev_ctx.channels ; c++) { - dev_ctx.ch_fd[c] = lantiq_dev_open(base_path, c + 1); - - if (dev_ctx.ch_fd[c] < 0) { - ast_log(LOG_ERROR, "lantiq TAPI channel %d open function failed\n", c); - goto load_error_st; - } - snprintf(dev_ctx.ch_led[c], LED_NAME_LENGTH, "fxs%d", c + 1); - } - - if (lantiq_dev_firmware_download(dev_ctx.dev_fd, firmware_filename)) { - ast_log(LOG_ERROR, "voice firmware download failed\n"); - goto load_error_st; - } - - if (ioctl(dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0)) { - ast_log(LOG_ERROR, "IFX_TAPI_DEV_STOP ioctl failed\n"); - goto load_error_st; - } - - memset(&dev_start, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t)); - dev_start.nMode = IFX_TAPI_INIT_MODE_VOICE_CODER; - - /* Start TAPI */ - if (ioctl(dev_ctx.dev_fd, IFX_TAPI_DEV_START, &dev_start)) { - ast_log(LOG_ERROR, "IFX_TAPI_DEV_START ioctl failed\n"); - goto load_error_st; - } - - tz = ast_get_indication_zone(NULL); - - if (!tz) { - ast_log(LOG_ERROR, "Unable to alloc tone zone\n"); - goto load_error_st; - } - - ts_dial = ast_get_indication_tone(tz, "dial"); - ts_ring = ast_get_indication_tone(tz, "ring"); - ts_busy = ast_get_indication_tone(tz, "busy"); - ts_congestion = ast_get_indication_tone(tz, "congestion"); - - if (!ts_dial || !ts_dial->data || !ts_ring || !ts_ring->data || !ts_busy || !ts_busy->data || !ts_congestion || !ts_congestion->data) { - ast_log(LOG_ERROR, "Unable to get indication tones\n"); - goto load_error_st; - } - - for (c = 0; c < dev_ctx.channels ; c++) { - /* We're a FXS and want to switch between narrow & wide band automatically */ - memset(&line_type, 0, sizeof(IFX_TAPI_LINE_TYPE_CFG_t)); - line_type.lineType = IFX_TAPI_LINE_TYPE_FXS_AUTO; - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_TYPE_SET, &line_type)) { - ast_log(LOG_ERROR, "IFX_TAPI_LINE_TYPE_SET %d failed\n", c); - goto load_error_st; - } - - /* tones */ - memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); - tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; - playlist_to_tapitone(ts_dial->data, TAPI_TONE_LOCALE_DIAL_CODE, &tone); - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { - ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); - goto load_error_st; - } - - memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); - tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; - playlist_to_tapitone(ts_ring->data, TAPI_TONE_LOCALE_RINGING_CODE, &tone); - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { - ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); - goto load_error_st; - } - - memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); - tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; - playlist_to_tapitone(ts_busy->data, TAPI_TONE_LOCALE_BUSY_CODE, &tone); - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { - ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); - goto load_error_st; - } - - memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); - tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; - playlist_to_tapitone(ts_congestion->data, TAPI_TONE_LOCALE_CONGESTION_CODE, &tone); - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { - ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); - goto load_error_st; - } - - /* ringing type */ - IFX_TAPI_RING_CFG_t ringingType; - memset(&ringingType, 0, sizeof(IFX_TAPI_RING_CFG_t)); - ringingType.nMode = IFX_TAPI_RING_CFG_MODE_INTERNAL_BALANCED; - ringingType.nSubmode = IFX_TAPI_RING_CFG_SUBMODE_DC_RNG_TRIP_FAST; - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_CFG_SET, (IFX_int32_t) &ringingType)) { - ast_log(LOG_ERROR, "IFX_TAPI_RING_CFG_SET failed\n"); - goto load_error_st; - } - - /* ring cadence */ - IFX_char_t data[15] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00 }; - - IFX_TAPI_RING_CADENCE_t ringCadence; - memset(&ringCadence, 0, sizeof(IFX_TAPI_RING_CADENCE_t)); - memcpy(&ringCadence.data, data, sizeof(data)); - ringCadence.nr = sizeof(data) * 8; - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_CADENCE_HR_SET, &ringCadence)) { - ast_log(LOG_ERROR, "IFX_TAPI_RING_CADENCE_HR_SET failed\n"); - goto load_error_st; - } - - /* perform mapping */ - memset(&map_data, 0x0, sizeof(IFX_TAPI_MAP_DATA_t)); - map_data.nDstCh = c; - map_data.nChType = IFX_TAPI_MAP_TYPE_PHONE; - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_MAP_DATA_ADD, &map_data)) { - ast_log(LOG_ERROR, "IFX_TAPI_MAP_DATA_ADD %d failed\n", c); - goto load_error_st; - } - - /* set line feed */ - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY)) { - ast_log(LOG_ERROR, "IFX_TAPI_LINE_FEED_SET %d failed\n", c); - goto load_error_st; - } - - /* set volume */ - memset(&line_vol, 0, sizeof(line_vol)); - line_vol.nGainRx = rxgain; - line_vol.nGainTx = txgain; - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_PHONE_VOLUME_SET, &line_vol)) { - ast_log(LOG_ERROR, "IFX_TAPI_PHONE_VOLUME_SET %d failed\n", c); - goto load_error_st; - } - - /* Configure line echo canceller */ - memset(&wlec_cfg, 0, sizeof(wlec_cfg)); - wlec_cfg.nType = wlec_type; - wlec_cfg.bNlp = wlec_nlp; - wlec_cfg.nNBFEwindow = wlec_nbfe; - wlec_cfg.nNBNEwindow = wlec_nbne; - wlec_cfg.nWBNEwindow = wlec_wbne; - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_WLEC_PHONE_CFG_SET, &wlec_cfg)) { - ast_log(LOG_ERROR, "IFX_TAPI_WLEC_PHONE_CFG_SET %d failed\n", c); - goto load_error_st; - } - - /* Configure jitter buffer */ - memset(&jb_cfg, 0, sizeof(jb_cfg)); - jb_cfg.nJbType = jb_type; - jb_cfg.nPckAdpt = jb_pckadpt; - jb_cfg.nLocalAdpt = jb_localadpt; - jb_cfg.nScaling = jb_scaling; - jb_cfg.nInitialSize = jb_initialsize; - jb_cfg.nMinSize = jb_minsize; - jb_cfg.nMaxSize = jb_maxsize; - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_JB_CFG_SET, &jb_cfg)) { - ast_log(LOG_ERROR, "IFX_TAPI_JB_CFG_SET %d failed\n", c); - goto load_error_st; - } - - /* Configure Caller ID type */ - memset(&cid_cfg, 0, sizeof(cid_cfg)); - cid_cfg.nStandard = cid_type; - - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_CID_CFG_SET, &cid_cfg)) { - ast_log(LOG_ERROR, "IIFX_TAPI_CID_CFG_SET %d failed\n", c); - goto load_error_st; - } - - /* Configure voice activity detection */ - if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_VAD_CFG_SET, vad_type)) { - ast_log(LOG_ERROR, "IFX_TAPI_ENC_VAD_CFG_SET %d failed\n", c); - goto load_error_st; - } - - /* Setup TAPI <-> internal RTP codec type mapping */ - if (lantiq_setup_rtp(c)) { - goto load_error_st; - } - - /* Set initial hook status */ - iflist[c].channel_state = lantiq_get_hookstatus(c); - - if (iflist[c].channel_state == UNKNOWN) { - goto load_error_st; - } - } - - /* make sure our device will be closed properly */ - ast_register_atexit(lantiq_cleanup); - - restart_monitor(); - led_on(dev_ctx.voip_led); - return AST_MODULE_LOAD_SUCCESS; - -cfg_error_il: - ast_mutex_unlock(&iflock); -cfg_error: - ast_config_destroy(cfg); - return AST_MODULE_LOAD_DECLINE; - -load_error_st: - ast_sched_context_destroy(sched); -load_error: - unload_module(); - ast_free(iflist); - return AST_MODULE_LOAD_FAILURE; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Lantiq TAPI Telephony API Support", - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_CHANNEL_DRIVER -); diff --git a/net/asterisk-13.x/src-lantiq/configs/samples/lantiq.conf.sample b/net/asterisk-13.x/src-lantiq/configs/samples/lantiq.conf.sample deleted file mode 100644 index 74c6f3f..0000000 --- a/net/asterisk-13.x/src-lantiq/configs/samples/lantiq.conf.sample +++ /dev/null @@ -1,153 +0,0 @@ -; -; TAPI Telephony Interface -; -; Configuration file - -[interfaces] -; -; Number of FXS ports (default: 2) -channels = 2 -; -; Create contexts per channel (default: off) -;per_channel_context = off -; -; Set tapi firmware file path -;firmwarefilename = /lib/firmware/danube_firmware.bin -; -; Set tapi bbd file path -;bbdfilename = /lib/firmware/danube_bbd_fxs.bin -; -; Set vmmc device path -;basepath = /dev/vmmc -; -[general] -; -; Gain setting for the receive and transmit path. -; The value is given in dB within the range (-24dB to +12dB), in 1 dB steps. -; -;rxgain = 1 -;txgain = 1 -; -; -; -; Line echo cancller valid types: -; -; off LEC and echo suppressor turned off. -; nlec LEC using fixed window; no echo suppressor. -; wlec LEC using fixed and moving window; no echo suppressor. -; nees LEC using fixed window and echo suppressor. -; nfees LEC using fixed and moving window and echo suppressor. -; es Echo suppressor -; -;echocancel = off -; -; If nlec or wlec is selected than size of the fixed window in narrowband (8 kHz) sampling mode -; can be defined with: -; A value of 0 defaults to: 16 ms if type is nlec or 8 ms if type is wlec: -; -;echocancelfixedwindowsize = 0 -; -; If wlec is selected than size of the moving window in narrowband (8 kHz) sampling mode -; can be defined with: -; A value of 0 defaults to 8 ms. -; -;echocancelnfemovingwindowsize = 0 -; -; If wlec is selected than size of the moving window in wideband (16 kHz) sampling mode -; can be defined with: -; A value of 0 defaults to 8 ms. -; -;echocancelwidefixedwindowsize = 0 -; -; Activate or deactivate line echo cancller NLP (Non Linear Processor) if the LEC is active, -; valid is on or off: -; -;echocancelnlp = off -; -; -; -; Jitter buffer valid types: -; -; fixed Fixed jitter buffer. -; adaptive Adaptive jitter buffer. -; -;jitterbuffertype = fixed -; -; Jitter buffer packet adaptation valid types: -; -; voice Jitter buffer optimized for voice. -; data Jitter buffer optimized for data. -; datanorep Jitter buffer optimized for data but without doing packet repetition. -; -;jitterbufferpackettype = voice -; -; Following jitter buffer values can only be used with jitter buffer adaptive type: -; Turns on or off jitter buffer adaptation: -; -;jitterbufferadaptation = off -; -; Scaling factor multiplied by 16; in adaptive jitter buffer mode, the target average playout delay is -; equal to the estimated jitter multiplied by the scaling factor. The default value for the scaling factor -; is about 1.4 (scaling=22), meaning that the target average playout delay is equal to the estimated -; jitter. If less packets should be dropped because of jitter, the scaling factor has to be increased. An -; increase in the scaling factor will eventually lead to an increased playout delay. -; The supported range is 1 to 16 (16 up to 256). -; -;jitterbufferscalling = 1 -; -; Initial size of the jitter buffer in time stamps of 125 us: -; -;jitterbufferinitialsize = 125 -; -; Minimum size of the jitter buffer in time stamps of 125 us -; -;jitterbufferminsize = 125 -; -; Maximum size of the jitter buffer in time stamps of 125 us -; -;jitterbuffermaxsize = 125 -; -; -; -; Caller id valid standards: -; -; telecordia Bellcore/Telcordia GR-30-CORE; use Bell202 FSK coding of CID information. (default) -; etsifsk ETSI 300-659-1/2/3 V1.3.1; use V.23 FSK coding to transmit CID information. -; etsidtmf ETSI 300-659-1/2/3 V1.3.1; use DTMF transmission of CID information. -; sin SIN 227 Issue 3.4; use V.23 FSK coding of CID information. -; ntt NTT standard: TELEPHONE SERVICE INTERFACES, edition 5; use a modified V.23 FSK coding of -; CID information. -; kpndtmf KPN; use DTMF transmission of CID information. -; kpndtmffsk KPN; use DTMF and FSK transmission of CID information. -; -;calleridtype = telecordia -; -; -; -; Voice activity detection: -; -; on Voice activity detection on; in this case also comfort noise and spectral -; information (nicer noise) is switched on. -; g711 Voice activity detection on with comfort noise generation, -; but without spectral information. -; cng Voice activity detection on with comfort noise generation, but without silence compression -; sc Voice activity detection on with silence compression, -; but without comfort noise generation. -; -;voiceactivitydetection = on -; -; -; -; Timeout between dialed digits, in milliseconds, before placing the call. -; -;interdigit = 4000 -; -; -; -; Tone generator type (default: integrated) -; integrated Use tapi tone generator -; asterisk Use asterisk tone generator where possible -; media Use media tone where possible -; -;tone_generator = integrated -; diff --git a/net/asterisk-chan-lantiq/Makefile b/net/asterisk-chan-lantiq/Makefile new file mode 100644 index 0000000..9ab35e3 --- /dev/null +++ b/net/asterisk-chan-lantiq/Makefile @@ -0,0 +1,87 @@ +# +# Copyright (C) 2017 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=chan-lantiq +PKG_VERSION:=1.0 +PKG_RELEASE:=1 + +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=Jiri Slachta + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION) + +PKG_FLAGS:=nonshared + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME)/Default + SUBMENU:=Telephony + SECTION:=net + CATEGORY:=Network + TITLE:=Lantiq channel driver + URL:=https://github.com/kochstefan/asterisk_channel_lantiq + DEPENDS:=@(TARGET_lantiq_falcon||TARGET_lantiq_xway||TARGET_lantiq_xrx200) +kmod-ltq-vmmc +endef + +define Package/asterisk13-$(PKG_NAME) +$(call Package/$(PKG_NAME)/Default) + DEPENDS+=asterisk13 + VARIANT:=asterisk13 +endef + +define Package/description/Default +An implementation of a Lantiq TAPI channel driver for Asterisk. +endef + +Package/asterisk13-$(PKG_NAME)/description = $(Package/description/Default) + +define Package/conffiles/Default +/etc/asterisk/lantiq.conf +endef + +Package/asterisk13-$(PKG_NAME)/conffiles = $(Package/conffiles/Default) + +define Package/Install/Default + $(INSTALL_DIR) $(1)/etc/asterisk + $(INSTALL_CONF) $(SOURCE)/files/lantiq-$(BUILD_VARIANT).conf.sample \ + $(1)/etc/asterisk/lantiq.conf + $(INSTALL_DIR) $(1)/usr/lib/asterisk/modules + $(INSTALL_BIN) $(PKG_BUILD_DIR)/chan_lantiq.so \ + $(1)/usr/lib/asterisk/modules +endef + +Package/asterisk13-$(PKG_NAME)/install = $(Package/Install/Default) + +ifeq ($(BUILD_VARIANT),asterisk13) +CHAN_LANTIQ_AST_INCLUDES:=-I$(STAGING_DIR)/usr/include/asterisk-13/include +endif + +define Build/Prepare + $(CP) $(SOURCE)/files/chan_lantiq-$(BUILD_VARIANT).c $(PKG_BUILD_DIR)/chan_lantiq.c + $(CP) $(SOURCE)/files/default.exports $(PKG_BUILD_DIR)/chan_lantiq.exports +endef + +define Build/Configure +endef + +define Build/Compile + cd $(PKG_BUILD_DIR) && \ + $(TARGET_CC) -o chan_lantiq.o -c chan_lantiq.c -MD -MT chan_lantiq.o \ + -MF .chan_lantiq.o.d -MP -pthread \ + $(CHAN_LANTIQ_AST_INCLUDES) \ + $(TARGET_CFLAGS) \ + $(TARGET_CPPFLAGS) \ + -Wall -Wstrict-prototypes -Wmissing-prototypes \ + -Wmissing-declarations $(FPIC) -DAST_MODULE=\"chan_lantiq\" && \ + $(TARGET_CC) -o chan_lantiq.so -pthread $(TARGET_LDFLAGS) -shared \ + -Wl,--version-script,chan_lantiq.exports,--warn-common \ + chan_lantiq.o +endef + +$(eval $(call BuildPackage,asterisk13-$(PKG_NAME))) diff --git a/net/asterisk-chan-lantiq/files/chan_lantiq-asterisk13.c b/net/asterisk-chan-lantiq/files/chan_lantiq-asterisk13.c new file mode 100644 index 0000000..19d422e --- /dev/null +++ b/net/asterisk-chan-lantiq/files/chan_lantiq-asterisk13.c @@ -0,0 +1,2301 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2012, Luka Perkov + * Copyright (C) 2012, John Crispin + * Copyright (C) 2012, Andrej VlaÅ¡ić + * Copyright (C) 2012, Kaspar Schleiser for T-Labs + * (Deutsche Telekom Innovation Laboratories) + * Copyright (C) 2012, Mirko Vogt for T-Labs + * (Deutsche Telekom Innovation Laboratories) + * Copyright (c) 2015, Antonio Eugenio Burriel + * Copyright (C) 2017, Stefan Koch + * + * Luka Perkov + * John Crispin + * Andrej VlaÅ¡ić + * Kaspar Schleiser + * Mirko Vogt + * Antonio Eugenio Burriel + * Stefan Koch + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Asterisk channel line driver for Lantiq based TAPI boards + * + * \author Luka Perkov + * \author John Crispin + * \author Andrej VlaÅ¡ić + * \author Kaspar Schleiser + * \author Mirko Vogt + * \author Antonio Eugenio Burriel + * \author Stefan Koch + * + * \ingroup channel_drivers + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: xxx $") + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LINUX_COMPILER_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Lantiq TAPI includes */ +#include +#include + +#define TAPI_AUDIO_PORT_NUM_MAX 2 + +/* Tapi predefined tones 0 to 31 */ +#define TAPI_TONE_LOCALE_NONE 0 +//#define TAPI_TONE_LOCALE_DIAL_CODE 25 +//#define TAPI_TONE_LOCALE_RINGING_CODE 26 +//#define TAPI_TONE_LOCALE_BUSY_CODE 27 +//#define TAPI_TONE_LOCALE_CONGESTION_CODE 27 + +/* Tapi custom tones 32 to 256 */ +#define TAPI_TONE_LOCALE_DIAL_CODE 32 +#define TAPI_TONE_LOCALE_RINGING_CODE 33 +#define TAPI_TONE_LOCALE_BUSY_CODE 34 +#define TAPI_TONE_LOCALE_CONGESTION_CODE 35 + +#define LANTIQ_CONTEXT_PREFIX "lantiq" +#define DEFAULT_INTERDIGIT_TIMEOUT 4000 +#define G723_HIGH_RATE 1 +#define LED_NAME_LENGTH 32 + +static const char config[] = "lantiq.conf"; + +static char firmware_filename[PATH_MAX] = "/lib/firmware/ifx_firmware.bin"; +static char bbd_filename[PATH_MAX] = "/lib/firmware/ifx_bbd_fxs.bin"; +static char base_path[PATH_MAX] = "/dev/vmmc"; +static int per_channel_context = 0; + +/* tone generator types */ +enum tone_generator_t { + TONE_INTEGRATED, /* tapi tone generator */ + TONE_ASTERISK, /* asterisk tone generator where possible */ + TONE_MEDIA /* media tone where possible */ +}; + +/* tone generator (default is integraded) */ +static enum tone_generator_t tone_generator = TONE_INTEGRATED; + +/* tone zones for dial, ring, busy and congestion */ +struct ast_tone_zone_sound *ts_dial; +struct ast_tone_zone_sound *ts_ring; +struct ast_tone_zone_sound *ts_busy; +struct ast_tone_zone_sound *ts_congestion; + +/* + * The private structures of the Phone Jack channels are linked for selecting + * outgoing channels. + */ +enum channel_state { + ONHOOK, + OFFHOOK, + DIALING, + INCALL, + CALL_ENDED, + RINGING, + UNKNOWN +}; + +static struct lantiq_pvt { + struct ast_channel *owner; /* Channel we belong to, possibly NULL */ + int port_id; /* Port number of this object, 0..n */ + int channel_state; + char context[AST_MAX_CONTEXT]; /* this port's dialplan context */ + int dial_timer; /* timer handle for autodial timeout */ + char dtmfbuf[AST_MAX_EXTENSION]; /* buffer holding dialed digits */ + int dtmfbuf_len; /* lenght of dtmfbuf */ + int rtp_timestamp; /* timestamp for RTP packets */ + int ptime; /* Codec base ptime */ + uint16_t rtp_seqno; /* Sequence nr for RTP packets */ + uint32_t call_setup_start; /* Start of dialling in ms */ + uint32_t call_setup_delay; /* time between ^ and 1st ring in ms */ + uint32_t call_start; /* time we started dialling / answered */ + uint32_t call_answer; /* time the callee answered our call */ + uint16_t jb_size; /* Jitter buffer size */ + uint32_t jb_underflow; /* Jitter buffer injected samples */ + uint32_t jb_overflow; /* Jitter buffer dropped samples */ + uint16_t jb_delay; /* Jitter buffer: playout delay */ + uint16_t jb_invalid; /* Jitter buffer: Nr. of invalid packets */ +} *iflist = NULL; + +static struct lantiq_ctx { + int dev_fd; + int channels; + int ch_fd[TAPI_AUDIO_PORT_NUM_MAX]; + char voip_led[LED_NAME_LENGTH]; /* VOIP LED name */ + char ch_led[TAPI_AUDIO_PORT_NUM_MAX][LED_NAME_LENGTH]; /* FXS LED names */ + int interdigit_timeout; /* Timeout in ms between dialed digits */ +} dev_ctx; + +static int ast_digit_begin(struct ast_channel *ast, char digit); +static int ast_digit_end(struct ast_channel *ast, char digit, unsigned int duration); +static int ast_lantiq_call(struct ast_channel *ast, const char *dest, int timeout); +static int ast_lantiq_hangup(struct ast_channel *ast); +static int ast_lantiq_answer(struct ast_channel *ast); +static struct ast_frame *ast_lantiq_read(struct ast_channel *ast); +static int ast_lantiq_write(struct ast_channel *ast, struct ast_frame *frame); +static struct ast_frame *ast_lantiq_exception(struct ast_channel *ast); +static int ast_lantiq_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen); +static int ast_lantiq_fixup(struct ast_channel *old, struct ast_channel *new); +static struct ast_channel *ast_lantiq_requester(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assigned_ids, const struct ast_channel *requestor, const char *data, int *cause); +static int ast_lantiq_devicestate(const char *data); +static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *args, char *buf, size_t buflen); +static void lantiq_jb_get_stats(int c); +static struct ast_format *lantiq_map_rtptype_to_format(uint8_t rtptype); +static uint8_t lantiq_map_format_to_rtptype(const struct ast_format *format); +static int lantiq_conf_enc(int c, const struct ast_format *format); +static void lantiq_reset_dtmfbuf(struct lantiq_pvt *pvt); + +static struct ast_channel_tech lantiq_tech = { + .type = "TAPI", + .description = "Lantiq TAPI Telephony API Driver", + .send_digit_begin = ast_digit_begin, + .send_digit_end = ast_digit_end, + .call = ast_lantiq_call, + .hangup = ast_lantiq_hangup, + .answer = ast_lantiq_answer, + .read = ast_lantiq_read, + .write = ast_lantiq_write, + .exception = ast_lantiq_exception, + .indicate = ast_lantiq_indicate, + .fixup = ast_lantiq_fixup, + .requester = ast_lantiq_requester, + .devicestate = ast_lantiq_devicestate, + .func_channel_read = acf_channel_read +}; + +/* Protect the interface list (of lantiq_pvt's) */ +AST_MUTEX_DEFINE_STATIC(iflock); + +/* + * Protect the monitoring thread, so only one process can kill or start it, and + * not when it's doing something critical. + */ +AST_MUTEX_DEFINE_STATIC(monlock); + +/* The scheduling context */ +struct ast_sched_context *sched; + +/* + * This is the thread for the monitor which checks for input on the channels + * which are not currently in use. + */ +static pthread_t monitor_thread = AST_PTHREADT_NULL; + + +#define WORDS_BIGENDIAN +/* struct taken from some GPLed code by Mike Borella */ +typedef struct rtp_header +{ +#if defined(WORDS_BIGENDIAN) + uint8_t version:2, padding:1, extension:1, csrc_count:4; + uint8_t marker:1, payload_type:7; +#else + uint8_t csrc_count:4, extension:1, padding:1, version:2; + uint8_t payload_type:7, marker:1; +#endif + uint16_t seqno; + uint32_t timestamp; + uint32_t ssrc; +} rtp_header_t; +#define RTP_HEADER_LEN 12 +#define RTP_BUFFER_LEN 512 +/* Internal RTP payload types - standard */ +#define RTP_PCMU 0 +#define RTP_G723_63 4 +#define RTP_PCMA 8 +#define RTP_G722 9 +#define RTP_CN 13 +#define RTP_G729 18 +/* Internal RTP payload types - custom */ +#define RTP_G7221 100 +#define RTP_G726 101 +#define RTP_ILBC 102 +#define RTP_SLIN8 103 +#define RTP_SLIN16 104 +#define RTP_SIREN7 105 +#define RTP_G723_53 106 + + +/* LED Control. Taken with modifications from SVD by Luca Olivetti */ +#define LED_SLOW_BLINK 1000 +#define LED_FAST_BLINK 100 +static FILE *led_open(const char *led, char* sub) +{ + char fname[100]; + + if (snprintf(fname, sizeof(fname), "/sys/class/leds/%s/%s", led, sub) >= sizeof(fname)) + return NULL; + return fopen(fname, "r+"); +} + +static FILE *led_trigger(const char *led) +{ + return led_open(led, "trigger"); +} + +static void led_delay(const char *led, int onoff, int msec) +{ + FILE *fp = led_open(led, onoff ? "delay_on" : "delay_off"); + if (fp) { + fprintf(fp,"%d\n",msec); + fclose(fp); + } +} + +static void led_on(const char *led) +{ + FILE *fp; + + fp = led_trigger(led); + if (fp) { + fprintf(fp,"default-on\n"); + fclose(fp); + } +} + +static void led_off(const char *led) +{ + FILE *fp; + + fp = led_trigger(led); + if (fp) { + fprintf(fp,"none\n"); + fclose(fp); + } +} + +static void led_blink(const char *led, int period) +{ + FILE *fp; + + fp = led_trigger(led); + if (fp) { + fprintf(fp, "timer\n"); + fclose(fp); + led_delay(led, 1, period/2); + led_delay(led, 0, period/2); + } +} + +static uint32_t now(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + uint64_t tmp = ts.tv_sec*1000 + (ts.tv_nsec/1000000); + return (uint32_t) tmp; +} + +static uint32_t epoch(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + + return tv.tv_sec; +} + +static int lantiq_dev_open(const char *dev_path, const int32_t ch_num) +{ + char dev_name[PATH_MAX]; + memset(dev_name, 0, sizeof(dev_name)); + snprintf(dev_name, PATH_MAX, "%s%u%u", dev_path, 1, ch_num); + return open((const char*)dev_name, O_RDWR, 0644); +} + +static void lantiq_ring(int c, int r, const char *cid, const char *name) +{ + uint8_t status; + + if (r) { + led_blink(dev_ctx.ch_led[c], LED_FAST_BLINK); + if (!cid) { + status = (uint8_t) ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_START, 0); + } else { + IFX_TAPI_CID_MSG_t msg; + IFX_TAPI_CID_MSG_ELEMENT_t elements[3]; + int count = 0; + time_t timestamp; + struct tm *tm; + + elements[count].string.elementType = IFX_TAPI_CID_ST_CLI; + elements[count].string.len = strlen(cid); + if (elements[count].string.len > IFX_TAPI_CID_MSG_LEN_MAX) { + elements[count].string.len = IFX_TAPI_CID_MSG_LEN_MAX; + } + strncpy((char *)elements[count].string.element, cid, IFX_TAPI_CID_MSG_LEN_MAX); + elements[count].string.element[IFX_TAPI_CID_MSG_LEN_MAX-1] = '\0'; + count++; + + if (name) { + elements[count].string.elementType = IFX_TAPI_CID_ST_NAME; + elements[count].string.len = strlen(name); + if (elements[count].string.len > IFX_TAPI_CID_MSG_LEN_MAX) { + elements[count].string.len = IFX_TAPI_CID_MSG_LEN_MAX; + } + strncpy((char *)elements[count].string.element, name, IFX_TAPI_CID_MSG_LEN_MAX); + elements[count].string.element[IFX_TAPI_CID_MSG_LEN_MAX-1] = '\0'; + count++; + } + + if ((time(×tamp) != -1) && ((tm=localtime(×tamp)) != NULL)) { + elements[count].date.elementType = IFX_TAPI_CID_ST_DATE; + elements[count].date.day = tm->tm_mday; + elements[count].date.month = tm->tm_mon + 1; + elements[count].date.hour = tm->tm_hour; + elements[count].date.mn = tm->tm_min; + count++; + } + + msg.txMode = IFX_TAPI_CID_HM_ONHOOK; + msg.messageType = IFX_TAPI_CID_MT_CSUP; + msg.message = elements; + msg.nMsgElements = count; + + status = (uint8_t) ioctl(dev_ctx.ch_fd[c], IFX_TAPI_CID_TX_SEQ_START, (IFX_int32_t) &msg); + } + } else { + status = (uint8_t) ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_STOP, 0); + led_off(dev_ctx.ch_led[c]); + } + + if (status) { + ast_log(LOG_ERROR, "%s ioctl failed\n", + (r ? "IFX_TAPI_RING_START" : "IFX_TAPI_RING_STOP")); + } +} + +/* add a frequency to TAPE tone structure */ +/* returns the TAPI frequency ID */ +static int tapitone_add_freq (IFX_TAPI_TONE_t *tone, IFX_uint32_t freq) { + const int n=4; /* TAPI tone structure supports up to 4 frequencies */ + int error=0; + int ret; + int i; + + /* pointer array for freq's A, B, C, D */ + IFX_uint32_t *freqarr[] = { &(tone->simple.freqA), &(tone->simple.freqB), &(tone->simple.freqC), &(tone->simple.freqD) }; + + /* pointer array for level's A, B, C, D */ + IFX_int32_t *lvlarr[] = { &(tone->simple.levelA), &(tone->simple.levelB), &(tone->simple.levelC), &(tone->simple.levelD) }; + + /* array for freq IDs */ + IFX_uint32_t retarr[] = { IFX_TAPI_TONE_FREQA, IFX_TAPI_TONE_FREQB, IFX_TAPI_TONE_FREQC, IFX_TAPI_TONE_FREQD }; + + /* determine if freq already set */ + for (i = 0; i < n; i++) { + if(*freqarr[i] == freq) /* freq found */ + break; + else if (i == n-1) /* last iteration */ + error=1; /* not found */ + } + + /* write frequency if not already set */ + if(error) { + error=0; /* reset error flag */ + /* since freq is not set, write it into first free place */ + for (i = 0; i < n; i++) { + if(!*freqarr[i]) { /* free place */ + *freqarr[i] = freq; /* set freq */ + *lvlarr[i] = -150; /* set volume level */ + break; + } else if (i == n-1) /* last iteration */ + error=1; /* no free place becaus maximum count of freq's is set */ + } + } + + /* set freq ID return value */ + if (!freq || error) + ret = IFX_TAPI_TONE_FREQNONE; + else + ret = retarr[i]; + + return ret; /* freq ID */ +} + +/* convert asterisk playlist string to tapi tone structure */ +/* based on ast_playtones_start() from indications.c of asterisk 13 */ +static void playlist_to_tapitone (const char *playlst, IFX_uint32_t index, IFX_TAPI_TONE_t *tone) +{ + char *s, *data = ast_strdupa(playlst); + char *stringp; + char *separator; + int i; + + /* initialize tapi tone structure */ + memset(tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone->simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone->simple.index = index; + + stringp = data; + + /* check if the data is separated with '|' or with ',' by default */ + if (strchr(stringp,'|')) { + separator = "|"; + } else { + separator = ","; + } + + for ( i = 0; (s = strsep(&stringp, separator)) && !ast_strlen_zero(s) && i < IFX_TAPI_TONE_STEPS_MAX; i++) { + struct ast_tone_zone_part tone_data = { + .time = 0, + }; + + s = ast_strip(s); + if (s[0]=='!') { + s++; + } + + if (ast_tone_zone_part_parse(s, &tone_data)) { + ast_log(LOG_ERROR, "Failed to parse tone part '%s'\n", s); + continue; + } + + /* first tone must hava a cadence */ + if (i==0 && !tone_data.time) + tone->simple.cadence[i] = 1000; + else + tone->simple.cadence[i] = tone_data.time; + + /* check for modulation */ + if (tone_data.modulate) { + tone->simple.modulation[i] = IFX_TAPI_TONE_MODULATION_ON; + tone->simple.modulation_factor = IFX_TAPI_TONE_MODULATION_FACTOR_90; + } + + /* copy freq's to tapi tone structure */ + /* a freq will implicitly skipped if it is zero */ + tone->simple.frequencies[i] |= tapitone_add_freq(tone, tone_data.freq1); + tone->simple.frequencies[i] |= tapitone_add_freq(tone, tone_data.freq2); + } +} + +static int lantiq_play_tone(int c, int t) +{ + /* stop currently playing tone before starting new one */ + ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_LOCAL_PLAY, TAPI_TONE_LOCALE_NONE); + + /* do not handle stop tone twice */ + if (t != TAPI_TONE_LOCALE_NONE) { + /* start new tone */ + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_LOCAL_PLAY, t)) { + ast_log(LOG_ERROR, "IFX_TAPI_TONE_LOCAL_PLAY ioctl failed\n"); + return -1; + } + } + + return 0; +} + +static enum channel_state lantiq_get_hookstatus(int port) +{ + uint8_t status; + + if (ioctl(dev_ctx.ch_fd[port], IFX_TAPI_LINE_HOOK_STATUS_GET, &status)) { + ast_log(LOG_ERROR, "IFX_TAPI_LINE_HOOK_STATUS_GET ioctl failed\n"); + return UNKNOWN; + } + + if (status) { + return OFFHOOK; + } else { + return ONHOOK; + } +} + +static int +lantiq_dev_binary_buffer_create(const char *path, uint8_t **ppBuf, uint32_t *pBufSz) +{ + FILE *fd; + struct stat file_stat; + int status = -1; + + fd = fopen(path, "rb"); + if (fd == NULL) { + ast_log(LOG_ERROR, "binary file %s open failed\n", path); + goto on_exit; + } + + if (stat(path, &file_stat)) { + ast_log(LOG_ERROR, "file %s statistics get failed\n", path); + goto on_exit; + } + + *ppBuf = malloc(file_stat.st_size); + if (*ppBuf == NULL) { + ast_log(LOG_ERROR, "binary file %s memory allocation failed\n", path); + goto on_exit; + } + + if (fread (*ppBuf, sizeof(uint8_t), file_stat.st_size, fd) != file_stat.st_size) { + ast_log(LOG_ERROR, "file %s read failed\n", path); + status = -1; + goto on_exit; + } + + *pBufSz = file_stat.st_size; + status = 0; + +on_exit: + if (fd != NULL) + fclose(fd); + + if (*ppBuf != NULL && status) + free(*ppBuf); + + return status; +} + +static int32_t lantiq_dev_firmware_download(int32_t fd, const char *path) +{ + uint8_t *firmware = NULL; + uint32_t size = 0; + VMMC_IO_INIT vmmc_io_init; + + ast_log(LOG_DEBUG, "loading firmware: \"%s\".\n", path); + + if (lantiq_dev_binary_buffer_create(path, &firmware, &size)) + return -1; + + memset(&vmmc_io_init, 0, sizeof(VMMC_IO_INIT)); + vmmc_io_init.pPRAMfw = firmware; + vmmc_io_init.pram_size = size; + + if (ioctl(fd, FIO_FW_DOWNLOAD, &vmmc_io_init)) { + ast_log(LOG_ERROR, "FIO_FW_DOWNLOAD ioctl failed\n"); + return -1; + } + + if (firmware != NULL) + free(firmware); + + return 0; +} + +static const char *state_string(enum channel_state s) +{ + switch (s) { + case ONHOOK: return "ONHOOK"; + case OFFHOOK: return "OFFHOOK"; + case DIALING: return "DIALING"; + case INCALL: return "INCALL"; + case CALL_ENDED: return "CALL_ENDED"; + case RINGING: return "RINGING"; + default: return "UNKNOWN"; + } +} + +static const char *control_string(int c) +{ + switch (c) { + case AST_CONTROL_HANGUP: return "Other end has hungup"; + case AST_CONTROL_RING: return "Local ring"; + case AST_CONTROL_RINGING: return "Remote end is ringing"; + case AST_CONTROL_ANSWER: return "Remote end has answered"; + case AST_CONTROL_BUSY: return "Remote end is busy"; + case AST_CONTROL_TAKEOFFHOOK: return "Make it go off hook"; + case AST_CONTROL_OFFHOOK: return "Line is off hook"; + case AST_CONTROL_CONGESTION: return "Congestion (circuits busy)"; + case AST_CONTROL_FLASH: return "Flash hook"; + case AST_CONTROL_WINK: return "Wink"; + case AST_CONTROL_OPTION: return "Set a low-level option"; + case AST_CONTROL_RADIO_KEY: return "Key Radio"; + case AST_CONTROL_RADIO_UNKEY: return "Un-Key Radio"; + case AST_CONTROL_PROGRESS: return "Remote end is making Progress"; + case AST_CONTROL_PROCEEDING: return "Remote end is proceeding"; + case AST_CONTROL_HOLD: return "Hold"; + case AST_CONTROL_UNHOLD: return "Unhold"; + case AST_CONTROL_SRCUPDATE: return "Media Source Update"; + case AST_CONTROL_CONNECTED_LINE: return "Connected Line"; + case AST_CONTROL_REDIRECTING: return "Redirecting"; + case AST_CONTROL_INCOMPLETE: return "Incomplete"; + case -1: return "Stop tone"; + default: return "Unknown"; + } +} + +static int ast_lantiq_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen) +{ + struct lantiq_pvt *pvt = ast_channel_tech_pvt(chan); + + ast_verb(3, "phone indication \"%s\"\n", control_string(condition)); + + switch (condition) { + case -1: + { + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_NONE); + return 0; + } + case AST_CONTROL_CONGESTION: + { + if (tone_generator == TONE_INTEGRATED) + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_CONGESTION_CODE); + else + ast_playtones_start(chan, 0, ts_congestion->data, 1); + + return 0; + } + case AST_CONTROL_BUSY: + { + if (tone_generator == TONE_INTEGRATED) + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_BUSY_CODE); + else + ast_playtones_start(chan, 0, ts_busy->data, 1); + + return 0; + } + case AST_CONTROL_RINGING: + case AST_CONTROL_PROGRESS: + { + pvt->call_setup_delay = now() - pvt->call_setup_start; + + if (tone_generator == TONE_INTEGRATED) + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_RINGING_CODE); + else if (tone_generator == TONE_ASTERISK) /* do nothing if TONE_MEDIA is set */ + ast_playtones_start(chan, 0, ts_ring->data, 1); + + return 0; + } + default: + { + /* -1 lets asterisk generate the tone */ + return -1; + } + } +} + +static int ast_lantiq_fixup(struct ast_channel *old, struct ast_channel *new) +{ + ast_log(LOG_DEBUG, "entering... no code here...\n"); + return 0; +} + +static int ast_digit_begin(struct ast_channel *chan, char digit) +{ + /* TODO: Modify this callback to let Asterisk support controlling the length of DTMF */ + ast_log(LOG_DEBUG, "entering... no code here...\n"); + return 0; +} + +static int ast_digit_end(struct ast_channel *ast, char digit, unsigned int duration) +{ + ast_log(LOG_DEBUG, "entering... no code here...\n"); + return 0; +} + +static int ast_lantiq_call(struct ast_channel *ast, const char *dest, int timeout) +{ + int res = 0; + struct lantiq_pvt *pvt; + + /* lock to prevent simultaneous access with do_monitor thread processing */ + ast_mutex_lock(&iflock); + + pvt = ast_channel_tech_pvt(ast); + ast_log(LOG_DEBUG, "state: %s\n", state_string(pvt->channel_state)); + + if (pvt->channel_state == ONHOOK) { + struct ast_party_id connected_id = ast_channel_connected_effective_id(ast); + const char *cid = connected_id.number.valid ? connected_id.number.str : NULL; + const char *name = connected_id.name.valid ? connected_id.name.str : NULL; + + ast_log(LOG_DEBUG, "port %i is ringing\n", pvt->port_id); + ast_log(LOG_DEBUG, "port %i CID: %s\n", pvt->port_id, cid ? cid : "none"); + ast_log(LOG_DEBUG, "port %i NAME: %s\n", pvt->port_id, name ? name : "none"); + + lantiq_ring(pvt->port_id, 1, cid, name); + pvt->channel_state = RINGING; + + ast_setstate(ast, AST_STATE_RINGING); + ast_queue_control(ast, AST_CONTROL_RINGING); + } else { + ast_log(LOG_DEBUG, "port %i is busy\n", pvt->port_id); + ast_setstate(ast, AST_STATE_BUSY); + ast_queue_control(ast, AST_CONTROL_BUSY); + res = -1; + } + + ast_mutex_unlock(&iflock); + + return res; +} + +static int ast_lantiq_hangup(struct ast_channel *ast) +{ + struct lantiq_pvt *pvt; + + /* lock to prevent simultaneous access with do_monitor thread processing */ + ast_mutex_lock(&iflock); + + pvt = ast_channel_tech_pvt(ast); + ast_log(LOG_DEBUG, "state: %s\n", state_string(pvt->channel_state)); + + if (ast_channel_state(ast) == AST_STATE_RINGING) { + ast_debug(1, "channel state is RINGING\n"); + } + + switch (pvt->channel_state) { + case RINGING: + case ONHOOK: + lantiq_ring(pvt->port_id, 0, NULL, NULL); + pvt->channel_state = ONHOOK; + break; + default: + ast_log(LOG_DEBUG, "we were hung up, play busy tone\n"); + pvt->channel_state = CALL_ENDED; + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_BUSY_CODE); + } + + lantiq_jb_get_stats(pvt->port_id); + + ast_setstate(ast, AST_STATE_DOWN); + ast_module_unref(ast_module_info->self); + ast_channel_tech_pvt_set(ast, NULL); + pvt->owner = NULL; + + ast_mutex_unlock(&iflock); + + return 0; +} + +static int ast_lantiq_answer(struct ast_channel *ast) +{ + struct lantiq_pvt *pvt = ast_channel_tech_pvt(ast); + + ast_log(LOG_DEBUG, "Remote end has answered call.\n"); + + if (lantiq_conf_enc(pvt->port_id, ast_channel_writeformat(ast))) + return -1; + + pvt->call_answer = epoch(); + + return 0; +} + +static struct ast_frame * ast_lantiq_read(struct ast_channel *ast) +{ + ast_log(LOG_DEBUG, "entering... no code here...\n"); + return NULL; +} + +/* create asterisk format from rtp payload type */ +static struct ast_format *lantiq_map_rtptype_to_format(uint8_t rtptype) +{ + struct ast_format *format = NULL; + + switch (rtptype) { + case RTP_PCMU: format = ast_format_ulaw; break; + case RTP_PCMA: format = ast_format_alaw; break; + case RTP_G722: format = ast_format_g722; break; + case RTP_G726: format = ast_format_g726; break; + case RTP_SLIN8: format = ast_format_slin; break; + case RTP_SLIN16: format = ast_format_slin16; break; + case RTP_ILBC: format = ast_format_ilbc; break; + case RTP_SIREN7: format = ast_format_siren7; break; + case RTP_G723_63: format = ast_format_g723; break; + case RTP_G723_53: format = ast_format_g723; break; + case RTP_G729: format = ast_format_g729; break; + default: + { + ast_log(LOG_ERROR, "unsupported rtptype received is 0x%x, forcing ulaw\n", (unsigned) rtptype); + format = ast_format_ulaw; + } + } + + return format; +} + +/* create rtp payload type from asterisk format */ +static uint8_t lantiq_map_format_to_rtptype(const struct ast_format *format) +{ + uint8_t rtptype = 0; + + if (ast_format_cmp(format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_PCMU; + else if (ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_PCMA; + else if (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_G722; + else if (ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_G726; + else if (ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_SLIN8; + else if (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_SLIN16; + else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_ILBC; + else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_SIREN7; + else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) +#if defined G723_HIGH_RATE + rtptype = RTP_G723_63; +#else + rtptype = RTP_G723_53; +#endif + else if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) + rtptype = RTP_G729; + else { + ast_log(LOG_ERROR, "unsupported format %s, forcing ulaw\n", ast_format_get_name(format)); + rtptype = RTP_PCMU; + } + + return rtptype; +} + +static int lantiq_conf_enc(int c, const struct ast_format *format) +{ + /* Configure encoder before starting RTP session */ + IFX_TAPI_ENC_CFG_t enc_cfg; + + memset(&enc_cfg, 0, sizeof(IFX_TAPI_ENC_CFG_t)); + + if (ast_format_cmp(format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_MLAW; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 10; + } else if (ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_ALAW; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 10; + } else if (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G722_64; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 20; + } else if (ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G726_32; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 10; + } else if (ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_LIN16_8; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 10; + } else if (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_LIN16_16; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_10; + iflist[c].ptime = 10; + } else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) { + /* iLBC 15.2kbps is currently unsupported by Asterisk */ + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_ILBC_133; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_30; + iflist[c].ptime = 30; + } else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G7221_32; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 20; + } else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) { +#if defined G723_HIGH_RATE + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G723_63; +#else + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G723_53; +#endif + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_30; + iflist[c].ptime = 30; + } else if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_G729; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 10; + } else { + ast_log(LOG_ERROR, "unsupported format %s, forcing ulaw\n", ast_format_get_name(format)); + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_MLAW; + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + iflist[c].ptime = 10; + } + + ast_log(LOG_DEBUG, "Configuring encoder to use TAPI codec type %d (%s) on channel %i\n", enc_cfg.nEncType, ast_format_get_name(format), c); + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_CFG_SET, &enc_cfg)) { + ast_log(LOG_ERROR, "IFX_TAPI_ENC_CFG_SET %d failed\n", c); + } + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_START, 0)) { + ast_log(LOG_ERROR, "IFX_TAPI_ENC_START ioctl failed\n"); + } + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_DEC_START, 0)) { + ast_log(LOG_ERROR, "IFX_TAPI_DEC_START ioctl failed\n"); + } + + return 0; +} + +static int ast_lantiq_write(struct ast_channel *ast, struct ast_frame *frame) +{ + char buf[RTP_BUFFER_LEN]; + rtp_header_t *rtp_header = (rtp_header_t *) buf; + struct lantiq_pvt *pvt = ast_channel_tech_pvt(ast); + int ret; + uint8_t rtptype; + int subframes, subframes_rtp, length, samples; + char *head, *tail; + + if(frame->frametype != AST_FRAME_VOICE) { + ast_log(LOG_DEBUG, "unhandled frame type\n"); + return 0; + } + + if (frame->datalen == 0) { + ast_log(LOG_DEBUG, "we've been prodded\n"); + return 0; + } + + /* get rtp payload type */ + rtptype = lantiq_map_format_to_rtptype(frame->subclass.format); + + rtp_header->version = 2; + rtp_header->padding = 0; + rtp_header->extension = 0; + rtp_header->csrc_count = 0; + rtp_header->marker = 0; + rtp_header->ssrc = 0; + rtp_header->payload_type = rtptype; + + subframes = (iflist[pvt->port_id].ptime + frame->len - 1) / iflist[pvt->port_id].ptime; /* number of subframes in AST frame */ + if (subframes == 0) + subframes = 1; + + subframes_rtp = (RTP_BUFFER_LEN - RTP_HEADER_LEN) * subframes / frame->datalen; /* how many frames fit in a single RTP packet */ + + /* By default stick to the maximum multiple of native frame length */ + length = subframes_rtp * frame->datalen / subframes; + samples = length * frame->samples / frame->datalen; + + head = frame->data.ptr; + tail = frame->data.ptr + frame->datalen; + while (head < tail) { + rtp_header->seqno = pvt->rtp_seqno++; + rtp_header->timestamp = pvt->rtp_timestamp; + + if ((tail - head) < (RTP_BUFFER_LEN - RTP_HEADER_LEN)) { + length = tail - head; + samples = length * frame->samples / frame->datalen; + } + + if ( frame->datalen <= (sizeof(buf) - RTP_HEADER_LEN) ) + memcpy(buf + RTP_HEADER_LEN, head, length); + else { + ast_log(LOG_WARNING, "buffer is too small\n"); + return -1; + } + + head += length; + pvt->rtp_timestamp += (rtp_header->payload_type == RTP_G722) ? samples / 2 : samples; /* per RFC3551 */ + + ret = write(dev_ctx.ch_fd[pvt->port_id], buf, RTP_HEADER_LEN + length); + if (ret < 0) { + ast_debug(1, "TAPI: ast_lantiq_write(): error writing.\n"); + return -1; + } + if (ret != (RTP_HEADER_LEN + length)) { + ast_log(LOG_WARNING, "Short TAPI write of %d bytes, expected %d bytes\n", ret, RTP_HEADER_LEN + length); + continue; + } + } + + return 0; +} + +static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *args, char *buf, size_t buflen) +{ + struct lantiq_pvt *pvt; + int res = 0; + + if (!chan || ast_channel_tech(chan) != &lantiq_tech) { + ast_log(LOG_ERROR, "This function requires a valid Lantiq TAPI channel\n"); + return -1; + } + + ast_mutex_lock(&iflock); + + pvt = (struct lantiq_pvt*) ast_channel_tech_pvt(chan); + + if (!strcasecmp(args, "csd")) { + snprintf(buf, buflen, "%lu", (unsigned long int) pvt->call_setup_delay); + } else if (!strcasecmp(args, "jitter_stats")){ + lantiq_jb_get_stats(pvt->port_id); + snprintf(buf, buflen, "jbBufSize=%u,jbUnderflow=%u,jbOverflow=%u,jbDelay=%u,jbInvalid=%u", + (uint32_t) pvt->jb_size, + (uint32_t) pvt->jb_underflow, + (uint32_t) pvt->jb_overflow, + (uint32_t) pvt->jb_delay, + (uint32_t) pvt->jb_invalid); + } else if (!strcasecmp(args, "jbBufSize")) { + snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_size); + } else if (!strcasecmp(args, "jbUnderflow")) { + snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_underflow); + } else if (!strcasecmp(args, "jbOverflow")) { + snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_overflow); + } else if (!strcasecmp(args, "jbDelay")) { + snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_delay); + } else if (!strcasecmp(args, "jbInvalid")) { + snprintf(buf, buflen, "%u", (uint32_t) pvt->jb_invalid); + } else if (!strcasecmp(args, "start")) { + struct tm *tm = gmtime((const time_t*)&pvt->call_start); + strftime(buf, buflen, "%F %T", tm); + } else if (!strcasecmp(args, "answer")) { + struct tm *tm = gmtime((const time_t*)&pvt->call_answer); + strftime(buf, buflen, "%F %T", tm); + } else { + res = -1; + } + + ast_mutex_unlock(&iflock); + + return res; +} + +static struct ast_frame * ast_lantiq_exception(struct ast_channel *ast) +{ + ast_log(LOG_DEBUG, "entering... no code here...\n"); + return NULL; +} + +static void lantiq_jb_get_stats(int c) { + struct lantiq_pvt *pvt = &iflist[c]; + + IFX_TAPI_JB_STATISTICS_t param; + memset (¶m, 0, sizeof (param)); + if (ioctl (dev_ctx.ch_fd[c], IFX_TAPI_JB_STATISTICS_GET, (IFX_int32_t) ¶m) != IFX_SUCCESS) { + ast_debug(1, "Error getting jitter buffer stats.\n"); + } else { +#if !defined (TAPI_VERSION3) && defined (TAPI_VERSION4) + ast_debug(1, "Jitter buffer stats: dev=%u, ch=%u, nType=%u, nBufSize=%u, nIsUnderflow=%u, nDsOverflow=%u, nPODelay=%u, nInvalid=%u\n", + (uint32_t) param.dev, + (uint32_t) param.ch, +#else + ast_debug(1, "Jitter buffer stats: nType=%u, nBufSize=%u, nIsUnderflow=%u, nDsOverflow=%u, nPODelay=%u, nInvalid=%u\n", +#endif + (uint32_t) param.nType, + (uint32_t) param.nBufSize, + (uint32_t) param.nIsUnderflow, + (uint32_t) param.nDsOverflow, + (uint32_t) param.nPODelay, + (uint32_t) param.nInvalid); + + pvt->jb_size = param.nBufSize; + pvt->jb_underflow = param.nIsUnderflow; + pvt->jb_overflow = param.nDsOverflow; + pvt->jb_invalid = param.nInvalid; + pvt->jb_delay = param.nPODelay; + } +} + + +static int lantiq_standby(int c) +{ + ast_debug(1, "Stopping line feed for channel %i\n", c); + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY)) { + ast_log(LOG_ERROR, "IFX_TAPI_LINE_FEED_SET ioctl failed\n"); + return -1; + } + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_STOP, 0)) { + ast_log(LOG_ERROR, "IFX_TAPI_ENC_STOP ioctl failed\n"); + return -1; + } + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_DEC_STOP, 0)) { + ast_log(LOG_ERROR, "IFX_TAPI_DEC_STOP ioctl failed\n"); + return -1; + } + + return lantiq_play_tone(c, TAPI_TONE_LOCALE_NONE); +} + +static int lantiq_end_dialing(int c) +{ + struct lantiq_pvt *pvt = &iflist[c]; + + ast_log(LOG_DEBUG, "end of dialing\n"); + + if (pvt->dial_timer != -1) { + AST_SCHED_DEL(sched, pvt->dial_timer); + pvt->dial_timer = -1; + } + + if(pvt->owner) { + ast_hangup(pvt->owner); + } + lantiq_reset_dtmfbuf(pvt); + + return 0; +} + +static int lantiq_end_call(int c) +{ + struct lantiq_pvt *pvt = &iflist[c]; + + ast_log(LOG_DEBUG, "end of call\n"); + + if(pvt->owner) { + lantiq_jb_get_stats(c); + ast_queue_hangup(pvt->owner); + } + + return 0; +} + +static struct ast_channel *lantiq_channel(int state, int c, char *ext, char *ctx, struct ast_format_cap *cap, const struct ast_assigned_ids *assigned_ids, const struct ast_channel *requestor) +{ + struct ast_channel *chan = NULL; + struct lantiq_pvt *pvt = &iflist[c]; + struct ast_format *format = NULL; + struct ast_format_cap *newcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + struct ast_format_cap *formatcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + + if (!newcap || !formatcap) { + ast_log(LOG_DEBUG, "Cannot allocate format capabilities!\n"); + return NULL; + } + + chan = ast_channel_alloc(1, state, NULL, NULL, "", ext, ctx, assigned_ids, requestor, c, "TAPI/%d", (c + 1)); + if (!chan) { + ast_log(LOG_DEBUG, "Cannot allocate channel!\n"); + ao2_ref(newcap, -1); + ao2_ref(formatcap, -1); + return NULL; + } + + ast_channel_tech_set(chan, &lantiq_tech); + ast_channel_tech_pvt_set(chan, pvt); + pvt->owner = chan; + + if (cap && ast_format_cap_iscompatible(cap, lantiq_tech.capabilities)) { /* compatible format capabilities given */ + ast_format_cap_get_compatible(lantiq_tech.capabilities, cap, newcap); + format = ast_format_cap_get_format(newcap, 0); /* choose format */ + } else { /* no or unsupported format capabilities given */ + format = ast_format_cap_get_format(lantiq_tech.capabilities, 0); /* choose format from capabilities */ + } + + /* set choosed format */ + ast_format_cap_append(formatcap, format, 0); + ast_channel_nativeformats_set(chan, formatcap); + ast_channel_set_readformat(chan, format); + ast_channel_set_writeformat(chan, format); + ast_channel_set_rawreadformat(chan, format); + ast_channel_set_rawwriteformat(chan, format); + + ao2_ref(newcap, -1); + ao2_ref(formatcap, -1); + + /* configuring encoder */ + if (format != 0) + if (lantiq_conf_enc(c, format) < 0) + return NULL; + + return chan; +} + +static struct ast_channel *ast_lantiq_requester(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assigned_ids, const struct ast_channel *requestor, const char *data, int *cause) +{ + struct ast_str *buf = ast_str_alloca(64); + struct ast_channel *chan = NULL; + int port_id = -1; + + ast_mutex_lock(&iflock); + + ast_debug(1, "Asked to create a TAPI channel with formats: %s\n", ast_format_cap_get_names(cap, &buf)); + + /* check for correct data argument */ + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "Unable to create channel with empty destination.\n"); + *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE; + goto bailout; + } + + /* get our port number */ + port_id = atoi((char*) data); + if (port_id < 1 || port_id > dev_ctx.channels) { + ast_log(LOG_ERROR, "Unknown channel ID: \"%s\"\n", data); + *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE; + goto bailout; + } + + /* on asterisk user's side, we're using port 1-2. + * Here in non normal human's world, we begin + * counting at 0. + */ + port_id -= 1; + + + /* Bail out if channel is already in use */ + struct lantiq_pvt *pvt = &iflist[port_id]; + if (! pvt->channel_state == ONHOOK) { + ast_debug(1, "TAPI channel %i alread in use.\n", port_id+1); + } else { + chan = lantiq_channel(AST_STATE_DOWN, port_id, NULL, NULL, cap, assigned_ids, requestor); + ast_channel_unlock(chan); /* it's essential to unlock channel */ + } + +bailout: + ast_mutex_unlock(&iflock); + return chan; +} + +static int ast_lantiq_devicestate(const char *data) +{ + int port = atoi(data) - 1; + if ((port < 1) || (port > dev_ctx.channels)) { + return AST_DEVICE_INVALID; + } + + switch (iflist[port].channel_state) { + case ONHOOK: + return AST_DEVICE_NOT_INUSE; + case OFFHOOK: + case DIALING: + case INCALL: + case CALL_ENDED: + return AST_DEVICE_INUSE; + case RINGING: + return AST_DEVICE_RINGING; + case UNKNOWN: + default: + return AST_DEVICE_UNKNOWN; + } +} + +static int lantiq_dev_data_handler(int c) +{ + char buf[BUFSIZ]; + struct ast_frame frame = {0}; + struct ast_format *format = NULL; + + int res = read(dev_ctx.ch_fd[c], buf, sizeof(buf)); + if (res <= 0) { + ast_log(LOG_ERROR, "we got read error %i\n", res); + return -1; + } else if (res < RTP_HEADER_LEN) { + ast_log(LOG_ERROR, "we got data smaller than header size\n"); + return -1; + } + + rtp_header_t *rtp = (rtp_header_t*) buf; + struct lantiq_pvt *pvt = (struct lantiq_pvt *) &iflist[c]; + if ((!pvt->owner) || (ast_channel_state(pvt->owner) != AST_STATE_UP)) { + return 0; + } + + if (rtp->payload_type == RTP_CN) { + /* TODO: Handle Comfort Noise frames */ + ast_debug(1, "Dropping Comfort Noise frame\n"); + } + + format = lantiq_map_rtptype_to_format(rtp->payload_type); + frame.src = "TAPI"; + frame.frametype = AST_FRAME_VOICE; + frame.subclass.format = format; + frame.datalen = res - RTP_HEADER_LEN; + frame.data.ptr = buf + RTP_HEADER_LEN; + frame.samples = ast_codec_samples_count(&frame); + + if(!ast_channel_trylock(pvt->owner)) { + ast_queue_frame(pvt->owner, &frame); + ast_channel_unlock(pvt->owner); + } + + return 0; +} + +static int accept_call(int c) +{ + struct lantiq_pvt *pvt = &iflist[c]; + + ast_log(LOG_DEBUG, "accept call\n"); + + if (pvt->owner) { + struct ast_channel *chan = pvt->owner; + + switch (ast_channel_state(chan)) { + case AST_STATE_RINGING: + lantiq_play_tone(c, TAPI_TONE_LOCALE_NONE); + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); + pvt->channel_state = INCALL; + pvt->call_start = epoch(); + pvt->call_answer = pvt->call_start; + break; + default: + ast_log(LOG_WARNING, "entered unhandled state %s\n", ast_state2str(ast_channel_state(chan))); + } + } + + return 0; +} + +static int lantiq_dev_event_hook(int c, int state) +{ + ast_mutex_lock(&iflock); + + ast_log(LOG_DEBUG, "on port %i detected event %s hook\n", c, state ? "on" : "off"); + + struct lantiq_pvt *pvt = &iflist[c]; + + int ret = -1; + if (state) { /* going onhook */ + switch (iflist[c].channel_state) { + case DIALING: + ret = lantiq_end_dialing(c); + break; + case INCALL: + ret = lantiq_end_call(c); + break; + } + + iflist[c].channel_state = ONHOOK; + + /* stop DSP data feed */ + lantiq_standby(c); + led_off(dev_ctx.ch_led[c]); + + } else { /* going offhook */ + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_ACTIVE)) { + ast_log(LOG_ERROR, "IFX_TAPI_LINE_FEED_SET ioctl failed\n"); + goto out; + } + + switch (iflist[c].channel_state) { + case RINGING: + ret = accept_call(c); + led_blink(dev_ctx.ch_led[c], LED_SLOW_BLINK); + break; + default: + iflist[c].channel_state = OFFHOOK; + lantiq_play_tone(c, TAPI_TONE_LOCALE_DIAL_CODE); + lantiq_reset_dtmfbuf(pvt); + ret = 0; + led_on(dev_ctx.ch_led[c]); + break; + } + + } + +out: + ast_mutex_unlock(&iflock); + + return ret; +} + +static void lantiq_reset_dtmfbuf(struct lantiq_pvt *pvt) +{ + pvt->dtmfbuf[0] = '\0'; + pvt->dtmfbuf_len = 0; +} + +static void lantiq_dial(struct lantiq_pvt *pvt) +{ + struct ast_channel *chan = NULL; + + ast_mutex_lock(&iflock); + ast_log(LOG_DEBUG, "user want's to dial %s.\n", pvt->dtmfbuf); + + if (ast_exists_extension(NULL, pvt->context, pvt->dtmfbuf, 1, NULL)) { + ast_debug(1, "found extension %s, dialing\n", pvt->dtmfbuf); + + ast_verbose(VERBOSE_PREFIX_3 " extension exists, starting PBX %s\n", pvt->dtmfbuf); + + chan = lantiq_channel(AST_STATE_UP, pvt->port_id, pvt->dtmfbuf, pvt->context, NULL, 0, NULL); + if (!chan) { + ast_log(LOG_ERROR, "couldn't create channel\n"); + goto bailout; + } + ast_channel_tech_pvt_set(chan, pvt); + pvt->owner = chan; + + ast_setstate(chan, AST_STATE_RING); + pvt->channel_state = INCALL; + + pvt->call_setup_start = now(); + pvt->call_start = epoch(); + + if (ast_pbx_start(chan)) { + ast_log(LOG_WARNING, " unable to start PBX on %s\n", ast_channel_name(chan)); + ast_hangup(chan); + } + + ast_channel_unlock(chan); /* it's essential to unlock channel */ + } else { + ast_log(LOG_DEBUG, "no extension found\n"); + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_CONGESTION_CODE); + pvt->channel_state = CALL_ENDED; + } + + lantiq_reset_dtmfbuf(pvt); +bailout: + ast_mutex_unlock(&iflock); +} + +static int lantiq_event_dial_timeout(const void* data) +{ + ast_debug(1, "TAPI: lantiq_event_dial_timeout()\n"); + + struct lantiq_pvt *pvt = (struct lantiq_pvt *) data; + pvt->dial_timer = -1; + + if (! pvt->channel_state == ONHOOK) { + lantiq_dial(pvt); + } else { + ast_debug(1, "TAPI: lantiq_event_dial_timeout(): dial timeout in state ONHOOK.\n"); + } + + return 0; +} + +static int lantiq_send_digit(int c, char digit) +{ + struct lantiq_pvt *pvt = &iflist[c]; + + struct ast_frame f = { .frametype = AST_FRAME_DTMF, .subclass.integer = digit }; + + if (pvt->owner) { + ast_log(LOG_DEBUG, "Port %i transmitting digit \"%c\"\n", c, digit); + return ast_queue_frame(pvt->owner, &f); + } else { + ast_debug(1, "Warning: lantiq_send_digit() without owner!\n"); + return -1; + } +} + +static void lantiq_dev_event_digit(int c, char digit) +{ + ast_mutex_lock(&iflock); + + ast_log(LOG_DEBUG, "on port %i detected digit \"%c\"\n", c, digit); + + struct lantiq_pvt *pvt = &iflist[c]; + + switch (pvt->channel_state) { + case INCALL: + lantiq_send_digit(c, digit); + break; + case OFFHOOK: + pvt->channel_state = DIALING; + + lantiq_play_tone(c, TAPI_TONE_LOCALE_NONE); + led_blink(dev_ctx.ch_led[c], LED_SLOW_BLINK); + + /* fall through */ + case DIALING: + if (pvt->dtmfbuf_len < AST_MAX_EXTENSION - 1) { + pvt->dtmfbuf[pvt->dtmfbuf_len] = digit; + pvt->dtmfbuf[++pvt->dtmfbuf_len] = '\0'; + } else { + /* No more room for another digit */ + lantiq_end_dialing(c); + lantiq_play_tone(pvt->port_id, TAPI_TONE_LOCALE_BUSY_CODE); + pvt->channel_state = CALL_ENDED; + break; + } + + /* setup autodial timer */ + if (pvt->dial_timer == -1) { + ast_log(LOG_DEBUG, "setting new timer\n"); + pvt->dial_timer = ast_sched_add(sched, dev_ctx.interdigit_timeout, lantiq_event_dial_timeout, (const void*) pvt); + } else { + ast_log(LOG_DEBUG, "replacing timer\n"); + AST_SCHED_REPLACE(pvt->dial_timer, sched, dev_ctx.interdigit_timeout, lantiq_event_dial_timeout, (const void*) pvt); + } + break; + default: + ast_log(LOG_ERROR, "don't know what to do in unhandled state\n"); + break; + } + + ast_mutex_unlock(&iflock); + return; +} + +static void lantiq_dev_event_handler(void) +{ + IFX_TAPI_EVENT_t event; + unsigned int i; + + for (i = 0; i < dev_ctx.channels ; i++) { + ast_mutex_lock(&iflock); + + memset (&event, 0, sizeof(event)); + event.ch = i; + if (ioctl(dev_ctx.dev_fd, IFX_TAPI_EVENT_GET, &event)) { + ast_mutex_unlock(&iflock); + continue; + } + if (event.id == IFX_TAPI_EVENT_NONE) { + ast_mutex_unlock(&iflock); + continue; + } + + ast_mutex_unlock(&iflock); + + switch(event.id) { + case IFX_TAPI_EVENT_FXS_ONHOOK: + lantiq_dev_event_hook(i, 1); + break; + case IFX_TAPI_EVENT_FXS_OFFHOOK: + lantiq_dev_event_hook(i, 0); + break; + case IFX_TAPI_EVENT_DTMF_DIGIT: + lantiq_dev_event_digit(i, (char)event.data.dtmf.ascii); + break; + case IFX_TAPI_EVENT_PULSE_DIGIT: + if (event.data.pulse.digit == 0xB) { + lantiq_dev_event_digit(i, '0'); + } else { + lantiq_dev_event_digit(i, '0' + (char)event.data.pulse.digit); + } + break; + case IFX_TAPI_EVENT_COD_DEC_CHG: + case IFX_TAPI_EVENT_TONE_GEN_END: + case IFX_TAPI_EVENT_CID_TX_SEQ_END: + break; + default: + ast_log(LOG_ERROR, "Unknown TAPI event %08X. Restarting Asterisk...\n", event.id); + sleep(1); + ast_cli_command(-1, "core restart now"); + break; + } + } +} + +static void * lantiq_events_monitor(void *data) +{ + ast_verbose("TAPI thread started\n"); + + struct pollfd fds[TAPI_AUDIO_PORT_NUM_MAX + 1]; + int c; + + fds[0].fd = dev_ctx.dev_fd; + fds[0].events = POLLIN; + for (c = 0; c < dev_ctx.channels; c++) { + fds[c + 1].fd = dev_ctx.ch_fd[c]; + fds[c + 1].events = POLLIN; + } + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + for (;;) { + if (poll(fds, dev_ctx.channels + 1, 2000) <= 0) { + continue; + } + + ast_mutex_lock(&monlock); + if (fds[0].revents & POLLIN) { + lantiq_dev_event_handler(); + } + + for (c = 0; c < dev_ctx.channels; c++) { + if ((fds[c + 1].revents & POLLIN) && (lantiq_dev_data_handler(c))) { + ast_log(LOG_ERROR, "data handler %d failed\n", c); + break; + } + } + ast_mutex_unlock(&monlock); + } + + return NULL; +} + +static int restart_monitor(void) +{ + /* If we're supposed to be stopped -- stay stopped */ + if (monitor_thread == AST_PTHREADT_STOP) + return 0; + + ast_mutex_lock(&monlock); + + if (monitor_thread == pthread_self()) { + ast_mutex_unlock(&monlock); + ast_log(LOG_WARNING, "Cannot kill myself\n"); + return -1; + } + + if (monitor_thread != AST_PTHREADT_NULL) { + /* Wake up the thread */ + pthread_kill(monitor_thread, SIGURG); + } else { + /* Start a new monitor */ + if (ast_pthread_create_background(&monitor_thread, NULL, lantiq_events_monitor, NULL) < 0) { + ast_mutex_unlock(&monlock); + ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); + return -1; + } + } + ast_mutex_unlock(&monlock); + + return 0; +} + +static void lantiq_cleanup(void) +{ + int c; + + if (dev_ctx.dev_fd < 0) { + return; + } + + for (c = 0; c < dev_ctx.channels ; c++) { + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY)) { + ast_log(LOG_WARNING, "IFX_TAPI_LINE_FEED_SET ioctl failed\n"); + } + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_STOP, 0)) { + ast_log(LOG_WARNING, "IFX_TAPI_ENC_STOP ioctl failed\n"); + } + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_DEC_STOP, 0)) { + ast_log(LOG_WARNING, "IFX_TAPI_DEC_STOP ioctl failed\n"); + } + led_off(dev_ctx.ch_led[c]); + } + + if (ioctl(dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0)) { + ast_log(LOG_WARNING, "IFX_TAPI_DEV_STOP ioctl failed\n"); + } + + close(dev_ctx.dev_fd); + dev_ctx.dev_fd = -1; + led_off(dev_ctx.voip_led); +} + +static int unload_module(void) +{ + int c; + + ast_channel_unregister(&lantiq_tech); + + if (ast_mutex_lock(&iflock)) { + ast_log(LOG_WARNING, "Unable to lock the interface list\n"); + return -1; + } + for (c = 0; c < dev_ctx.channels ; c++) { + if (iflist[c].owner) + ast_softhangup(iflist[c].owner, AST_SOFTHANGUP_APPUNLOAD); + } + ast_mutex_unlock(&iflock); + + if (ast_mutex_lock(&monlock)) { + ast_log(LOG_WARNING, "Unable to lock the monitor\n"); + return -1; + } + if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { + pthread_t th = monitor_thread; + monitor_thread = AST_PTHREADT_STOP; + pthread_cancel(th); + pthread_kill(th, SIGURG); + ast_mutex_unlock(&monlock); + pthread_join(th, NULL); + } else { + monitor_thread = AST_PTHREADT_STOP; + ast_mutex_unlock(&monlock); + } + + ast_sched_context_destroy(sched); + ast_mutex_destroy(&iflock); + ast_mutex_destroy(&monlock); + + lantiq_cleanup(); + ast_free(iflist); + + return 0; +} + +static struct lantiq_pvt *lantiq_init_pvt(struct lantiq_pvt *pvt) +{ + if (pvt) { + pvt->owner = NULL; + pvt->port_id = -1; + pvt->channel_state = UNKNOWN; + pvt->context[0] = '\0'; + pvt->dial_timer = -1; + pvt->dtmfbuf[0] = '\0'; + pvt->dtmfbuf_len = 0; + pvt->call_setup_start = 0; + pvt->call_setup_delay = 0; + pvt->call_answer = 0; + pvt->jb_size = 0; + pvt->jb_underflow = 0; + pvt->jb_overflow = 0; + pvt->jb_delay = 0; + pvt->jb_invalid = 0; + } else { + ast_log(LOG_ERROR, "unable to clear pvt structure\n"); + } + + return pvt; +} + +static int lantiq_create_pvts(void) +{ + int i; + + iflist = ast_calloc(1, sizeof(struct lantiq_pvt) * dev_ctx.channels); + + if (!iflist) { + ast_log(LOG_ERROR, "unable to allocate memory\n"); + return -1; + } + + for (i = 0; i < dev_ctx.channels; i++) { + lantiq_init_pvt(&iflist[i]); + iflist[i].port_id = i; + if (per_channel_context) { + snprintf(iflist[i].context, AST_MAX_CONTEXT, "%s%i", LANTIQ_CONTEXT_PREFIX, i + 1); + ast_debug(1, "Context for channel %i: %s\n", i, iflist[i].context); + } else { + snprintf(iflist[i].context, AST_MAX_CONTEXT, "default"); + } + } + return 0; +} + +static int lantiq_setup_rtp(int c) +{ + /* Configure RTP payload type tables */ + IFX_TAPI_PKT_RTP_PT_CFG_t rtpPTConf; + + memset((char*)&rtpPTConf, '\0', sizeof(rtpPTConf)); + + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G723_63] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G723_63] = RTP_G723_63; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G723_53] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G723_53] = RTP_G723_53; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G729] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G729] = RTP_G729; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_MLAW] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_MLAW] = RTP_PCMU; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_ALAW] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_ALAW] = RTP_PCMA; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G726_32] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G726_32] = RTP_G726; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_ILBC_152] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_ILBC_152] = RTP_ILBC; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_LIN16_8] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_LIN16_8] = RTP_SLIN8; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_LIN16_16] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_LIN16_16] = RTP_SLIN16; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G722_64] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G722_64] = RTP_G722; + rtpPTConf.nPTup[IFX_TAPI_COD_TYPE_G7221_32] = rtpPTConf.nPTdown[IFX_TAPI_COD_TYPE_G7221_32] = RTP_G7221; + + int ret; + if ((ret = ioctl(dev_ctx.ch_fd[c], IFX_TAPI_PKT_RTP_PT_CFG_SET, (IFX_int32_t) &rtpPTConf))) { + ast_log(LOG_ERROR, "IFX_TAPI_PKT_RTP_PT_CFG_SET failed: ret=%i\n", ret); + return -1; + } + + return 0; +} + +static int load_module(void) +{ + struct ast_config *cfg; + struct ast_variable *v; + int txgain = 0; + int rxgain = 0; + int wlec_type = 0; + int wlec_nlp = 0; + int wlec_nbfe = 0; + int wlec_nbne = 0; + int wlec_wbne = 0; + int jb_type = IFX_TAPI_JB_TYPE_ADAPTIVE; + int jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_VOICE; + int jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_DEFAULT; + int jb_scaling = 0x10; + int jb_initialsize = 0x2d0; + int jb_minsize = 0x50; + int jb_maxsize = 0x5a0; + int cid_type = IFX_TAPI_CID_STD_TELCORDIA; + int vad_type = IFX_TAPI_ENC_VAD_NOVAD; + dev_ctx.dev_fd = -1; + dev_ctx.channels = TAPI_AUDIO_PORT_NUM_MAX; + dev_ctx.interdigit_timeout = DEFAULT_INTERDIGIT_TIMEOUT; + struct ast_tone_zone *tz; + struct ast_flags config_flags = { 0 }; + int c; + + if(!(lantiq_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + ast_log(LOG_ERROR, "Unable to allocate format capabilities.\n"); + return AST_MODULE_LOAD_DECLINE; + } + + /* channel format capabilities */ + ast_format_cap_append(lantiq_tech.capabilities, ast_format_ulaw, 0); + ast_format_cap_append(lantiq_tech.capabilities, ast_format_alaw, 0); + ast_format_cap_append(lantiq_tech.capabilities, ast_format_g722, 0); + ast_format_cap_append(lantiq_tech.capabilities, ast_format_g726, 0); + ast_format_cap_append(lantiq_tech.capabilities, ast_format_slin, 0); + + /* Turn off the LEDs, just in case */ + led_off(dev_ctx.voip_led); + for(c = 0; c < TAPI_AUDIO_PORT_NUM_MAX; c++) + led_off(dev_ctx.ch_led[c]); + + if ((cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", config); + return AST_MODULE_LOAD_DECLINE; + } + + /* We *must* have a config file otherwise stop immediately */ + if (!cfg) { + ast_log(LOG_ERROR, "Unable to load config %s\n", config); + return AST_MODULE_LOAD_DECLINE; + } + + if (ast_mutex_lock(&iflock)) { + ast_log(LOG_ERROR, "Unable to lock interface list.\n"); + goto cfg_error; + } + + for (v = ast_variable_browse(cfg, "interfaces"); v; v = v->next) { + if (!strcasecmp(v->name, "channels")) { + dev_ctx.channels = atoi(v->value); + if (!dev_ctx.channels) { + ast_log(LOG_ERROR, "Invalid value for channels in config %s\n", config); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "firmwarefilename")) { + ast_copy_string(firmware_filename, v->value, sizeof(firmware_filename)); + } else if (!strcasecmp(v->name, "bbdfilename")) { + ast_copy_string(bbd_filename, v->value, sizeof(bbd_filename)); + } else if (!strcasecmp(v->name, "basepath")) { + ast_copy_string(base_path, v->value, sizeof(base_path)); + } else if (!strcasecmp(v->name, "per_channel_context")) { + if (!strcasecmp(v->value, "on")) { + per_channel_context = 1; + } else if (!strcasecmp(v->value, "off")) { + per_channel_context = 0; + } else { + ast_log(LOG_ERROR, "Unknown per_channel_context value '%s'. Try 'on' or 'off'.\n", v->value); + goto cfg_error_il; + } + } + } + + for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { + if (!strcasecmp(v->name, "rxgain")) { + rxgain = atoi(v->value); + if (!rxgain) { + rxgain = 0; + ast_log(LOG_WARNING, "Invalid rxgain: %s, using default.\n", v->value); + } + } else if (!strcasecmp(v->name, "txgain")) { + txgain = atoi(v->value); + if (!txgain) { + txgain = 0; + ast_log(LOG_WARNING, "Invalid txgain: %s, using default.\n", v->value); + } + } else if (!strcasecmp(v->name, "echocancel")) { + if (!strcasecmp(v->value, "off")) { + wlec_type = IFX_TAPI_WLEC_TYPE_OFF; + } else if (!strcasecmp(v->value, "nlec")) { + wlec_type = IFX_TAPI_WLEC_TYPE_NE; + if (!strcasecmp(v->name, "echocancelfixedwindowsize")) { + wlec_nbne = atoi(v->value); + } + } else if (!strcasecmp(v->value, "wlec")) { + wlec_type = IFX_TAPI_WLEC_TYPE_NFE; + if (!strcasecmp(v->name, "echocancelnfemovingwindowsize")) { + wlec_nbfe = atoi(v->value); + } else if (!strcasecmp(v->name, "echocancelfixedwindowsize")) { + wlec_nbne = atoi(v->value); + } else if (!strcasecmp(v->name, "echocancelwidefixedwindowsize")) { + wlec_wbne = atoi(v->value); + } + } else if (!strcasecmp(v->value, "nees")) { + wlec_type = IFX_TAPI_WLEC_TYPE_NE_ES; + } else if (!strcasecmp(v->value, "nfees")) { + wlec_type = IFX_TAPI_WLEC_TYPE_NFE_ES; + } else if (!strcasecmp(v->value, "es")) { + wlec_type = IFX_TAPI_WLEC_TYPE_ES; + } else { + wlec_type = IFX_TAPI_WLEC_TYPE_OFF; + ast_log(LOG_ERROR, "Unknown echo cancellation type '%s'\n", v->value); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "echocancelnlp")) { + if (!strcasecmp(v->value, "on")) { + wlec_nlp = IFX_TAPI_WLEC_NLP_ON; + } else if (!strcasecmp(v->value, "off")) { + wlec_nlp = IFX_TAPI_WLEC_NLP_OFF; + } else { + ast_log(LOG_ERROR, "Unknown echo cancellation nlp '%s'\n", v->value); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "jitterbuffertype")) { + if (!strcasecmp(v->value, "fixed")) { + jb_type = IFX_TAPI_JB_TYPE_FIXED; + } else if (!strcasecmp(v->value, "adaptive")) { + jb_type = IFX_TAPI_JB_TYPE_ADAPTIVE; + jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_DEFAULT; + if (!strcasecmp(v->name, "jitterbufferadaptation")) { + if (!strcasecmp(v->value, "on")) { + jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_ON; + } else if (!strcasecmp(v->value, "off")) { + jb_localadpt = IFX_TAPI_JB_LOCAL_ADAPT_OFF; + } + } else if (!strcasecmp(v->name, "jitterbufferscalling")) { + jb_scaling = atoi(v->value); + } else if (!strcasecmp(v->name, "jitterbufferinitialsize")) { + jb_initialsize = atoi(v->value); + } else if (!strcasecmp(v->name, "jitterbufferminsize")) { + jb_minsize = atoi(v->value); + } else if (!strcasecmp(v->name, "jitterbuffermaxsize")) { + jb_maxsize = atoi(v->value); + } + } else { + ast_log(LOG_ERROR, "Unknown jitter buffer type '%s'\n", v->value); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "jitterbufferpackettype")) { + if (!strcasecmp(v->value, "voice")) { + jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_VOICE; + } else if (!strcasecmp(v->value, "data")) { + jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_DATA; + } else if (!strcasecmp(v->value, "datanorep")) { + jb_pckadpt = IFX_TAPI_JB_PKT_ADAPT_DATA_NO_REP; + } else { + ast_log(LOG_ERROR, "Unknown jitter buffer packet adaptation type '%s'\n", v->value); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "calleridtype")) { + ast_log(LOG_DEBUG, "Setting CID type to %s.\n", v->value); + if (!strcasecmp(v->value, "telecordia")) { + cid_type = IFX_TAPI_CID_STD_TELCORDIA; + } else if (!strcasecmp(v->value, "etsifsk")) { + cid_type = IFX_TAPI_CID_STD_ETSI_FSK; + } else if (!strcasecmp(v->value, "etsidtmf")) { + cid_type = IFX_TAPI_CID_STD_ETSI_DTMF; + } else if (!strcasecmp(v->value, "sin")) { + cid_type = IFX_TAPI_CID_STD_SIN; + } else if (!strcasecmp(v->value, "ntt")) { + cid_type = IFX_TAPI_CID_STD_NTT; + } else if (!strcasecmp(v->value, "kpndtmf")) { + cid_type = IFX_TAPI_CID_STD_KPN_DTMF; + } else if (!strcasecmp(v->value, "kpndtmffsk")) { + cid_type = IFX_TAPI_CID_STD_KPN_DTMF_FSK; + } else { + ast_log(LOG_ERROR, "Unknown caller id type '%s'\n", v->value); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "voiceactivitydetection")) { + if (!strcasecmp(v->value, "on")) { + vad_type = IFX_TAPI_ENC_VAD_ON; + } else if (!strcasecmp(v->value, "g711")) { + vad_type = IFX_TAPI_ENC_VAD_G711; + } else if (!strcasecmp(v->value, "cng")) { + vad_type = IFX_TAPI_ENC_VAD_CNG_ONLY; + } else if (!strcasecmp(v->value, "sc")) { + vad_type = IFX_TAPI_ENC_VAD_SC_ONLY; + } else { + ast_log(LOG_ERROR, "Unknown voice activity detection value '%s'\n", v->value); + goto cfg_error_il; + } + } else if (!strcasecmp(v->name, "interdigit")) { + dev_ctx.interdigit_timeout = atoi(v->value); + ast_log(LOG_DEBUG, "Setting interdigit timeout to %s.\n", v->value); + if (!dev_ctx.interdigit_timeout) { + dev_ctx.interdigit_timeout = DEFAULT_INTERDIGIT_TIMEOUT; + ast_log(LOG_WARNING, "Invalid interdigit timeout: %s, using default.\n", v->value); + } + } else if (!strcasecmp(v->name, "tone_generator")) { + if (!strcasecmp(v->value, "integrated")) { + tone_generator = TONE_INTEGRATED; + } else if (!strcasecmp(v->value, "asterisk")) { + tone_generator = TONE_ASTERISK; + } else if (!strcasecmp(v->value, "media")) { + tone_generator = TONE_MEDIA; + } else { + ast_log(LOG_ERROR, "Unknown tone_generator value '%s'. Try 'integrated', 'asterisk' or 'media'.\n", v->value); + goto cfg_error_il; + } + } + } + + lantiq_create_pvts(); + + ast_mutex_unlock(&iflock); + ast_config_destroy(cfg); + + if (!(sched = ast_sched_context_create())) { + ast_log(LOG_ERROR, "Unable to create scheduler context\n"); + goto load_error; + } + + if (ast_sched_start_thread(sched)) { + ast_log(LOG_ERROR, "Unable to create scheduler context thread\n"); + goto load_error_st; + } + + if (ast_channel_register(&lantiq_tech)) { + ast_log(LOG_ERROR, "Unable to register channel class 'Phone'\n"); + goto load_error_st; + } + + /* tapi */ + IFX_TAPI_TONE_t tone; + IFX_TAPI_DEV_START_CFG_t dev_start; + IFX_TAPI_MAP_DATA_t map_data; + IFX_TAPI_LINE_TYPE_CFG_t line_type; + IFX_TAPI_LINE_VOLUME_t line_vol; + IFX_TAPI_WLEC_CFG_t wlec_cfg; + IFX_TAPI_JB_CFG_t jb_cfg; + IFX_TAPI_CID_CFG_t cid_cfg; + + /* open device */ + dev_ctx.dev_fd = lantiq_dev_open(base_path, 0); + + if (dev_ctx.dev_fd < 0) { + ast_log(LOG_ERROR, "lantiq TAPI device open function failed\n"); + goto load_error_st; + } + + snprintf(dev_ctx.voip_led, LED_NAME_LENGTH, "voice"); + for (c = 0; c < dev_ctx.channels ; c++) { + dev_ctx.ch_fd[c] = lantiq_dev_open(base_path, c + 1); + + if (dev_ctx.ch_fd[c] < 0) { + ast_log(LOG_ERROR, "lantiq TAPI channel %d open function failed\n", c); + goto load_error_st; + } + snprintf(dev_ctx.ch_led[c], LED_NAME_LENGTH, "fxs%d", c + 1); + } + + if (lantiq_dev_firmware_download(dev_ctx.dev_fd, firmware_filename)) { + ast_log(LOG_ERROR, "voice firmware download failed\n"); + goto load_error_st; + } + + if (ioctl(dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0)) { + ast_log(LOG_ERROR, "IFX_TAPI_DEV_STOP ioctl failed\n"); + goto load_error_st; + } + + memset(&dev_start, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t)); + dev_start.nMode = IFX_TAPI_INIT_MODE_VOICE_CODER; + + /* Start TAPI */ + if (ioctl(dev_ctx.dev_fd, IFX_TAPI_DEV_START, &dev_start)) { + ast_log(LOG_ERROR, "IFX_TAPI_DEV_START ioctl failed\n"); + goto load_error_st; + } + + tz = ast_get_indication_zone(NULL); + + if (!tz) { + ast_log(LOG_ERROR, "Unable to alloc tone zone\n"); + goto load_error_st; + } + + ts_dial = ast_get_indication_tone(tz, "dial"); + ts_ring = ast_get_indication_tone(tz, "ring"); + ts_busy = ast_get_indication_tone(tz, "busy"); + ts_congestion = ast_get_indication_tone(tz, "congestion"); + + if (!ts_dial || !ts_dial->data || !ts_ring || !ts_ring->data || !ts_busy || !ts_busy->data || !ts_congestion || !ts_congestion->data) { + ast_log(LOG_ERROR, "Unable to get indication tones\n"); + goto load_error_st; + } + + for (c = 0; c < dev_ctx.channels ; c++) { + /* We're a FXS and want to switch between narrow & wide band automatically */ + memset(&line_type, 0, sizeof(IFX_TAPI_LINE_TYPE_CFG_t)); + line_type.lineType = IFX_TAPI_LINE_TYPE_FXS_AUTO; + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_TYPE_SET, &line_type)) { + ast_log(LOG_ERROR, "IFX_TAPI_LINE_TYPE_SET %d failed\n", c); + goto load_error_st; + } + + /* tones */ + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + playlist_to_tapitone(ts_dial->data, TAPI_TONE_LOCALE_DIAL_CODE, &tone); + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { + ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); + goto load_error_st; + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + playlist_to_tapitone(ts_ring->data, TAPI_TONE_LOCALE_RINGING_CODE, &tone); + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { + ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); + goto load_error_st; + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + playlist_to_tapitone(ts_busy->data, TAPI_TONE_LOCALE_BUSY_CODE, &tone); + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { + ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); + goto load_error_st; + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + playlist_to_tapitone(ts_congestion->data, TAPI_TONE_LOCALE_CONGESTION_CODE, &tone); + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone)) { + ast_log(LOG_ERROR, "IFX_TAPI_TONE_TABLE_CFG_SET %d failed\n", c); + goto load_error_st; + } + + /* ringing type */ + IFX_TAPI_RING_CFG_t ringingType; + memset(&ringingType, 0, sizeof(IFX_TAPI_RING_CFG_t)); + ringingType.nMode = IFX_TAPI_RING_CFG_MODE_INTERNAL_BALANCED; + ringingType.nSubmode = IFX_TAPI_RING_CFG_SUBMODE_DC_RNG_TRIP_FAST; + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_CFG_SET, (IFX_int32_t) &ringingType)) { + ast_log(LOG_ERROR, "IFX_TAPI_RING_CFG_SET failed\n"); + goto load_error_st; + } + + /* ring cadence */ + IFX_char_t data[15] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 }; + + IFX_TAPI_RING_CADENCE_t ringCadence; + memset(&ringCadence, 0, sizeof(IFX_TAPI_RING_CADENCE_t)); + memcpy(&ringCadence.data, data, sizeof(data)); + ringCadence.nr = sizeof(data) * 8; + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_RING_CADENCE_HR_SET, &ringCadence)) { + ast_log(LOG_ERROR, "IFX_TAPI_RING_CADENCE_HR_SET failed\n"); + goto load_error_st; + } + + /* perform mapping */ + memset(&map_data, 0x0, sizeof(IFX_TAPI_MAP_DATA_t)); + map_data.nDstCh = c; + map_data.nChType = IFX_TAPI_MAP_TYPE_PHONE; + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_MAP_DATA_ADD, &map_data)) { + ast_log(LOG_ERROR, "IFX_TAPI_MAP_DATA_ADD %d failed\n", c); + goto load_error_st; + } + + /* set line feed */ + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY)) { + ast_log(LOG_ERROR, "IFX_TAPI_LINE_FEED_SET %d failed\n", c); + goto load_error_st; + } + + /* set volume */ + memset(&line_vol, 0, sizeof(line_vol)); + line_vol.nGainRx = rxgain; + line_vol.nGainTx = txgain; + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_PHONE_VOLUME_SET, &line_vol)) { + ast_log(LOG_ERROR, "IFX_TAPI_PHONE_VOLUME_SET %d failed\n", c); + goto load_error_st; + } + + /* Configure line echo canceller */ + memset(&wlec_cfg, 0, sizeof(wlec_cfg)); + wlec_cfg.nType = wlec_type; + wlec_cfg.bNlp = wlec_nlp; + wlec_cfg.nNBFEwindow = wlec_nbfe; + wlec_cfg.nNBNEwindow = wlec_nbne; + wlec_cfg.nWBNEwindow = wlec_wbne; + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_WLEC_PHONE_CFG_SET, &wlec_cfg)) { + ast_log(LOG_ERROR, "IFX_TAPI_WLEC_PHONE_CFG_SET %d failed\n", c); + goto load_error_st; + } + + /* Configure jitter buffer */ + memset(&jb_cfg, 0, sizeof(jb_cfg)); + jb_cfg.nJbType = jb_type; + jb_cfg.nPckAdpt = jb_pckadpt; + jb_cfg.nLocalAdpt = jb_localadpt; + jb_cfg.nScaling = jb_scaling; + jb_cfg.nInitialSize = jb_initialsize; + jb_cfg.nMinSize = jb_minsize; + jb_cfg.nMaxSize = jb_maxsize; + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_JB_CFG_SET, &jb_cfg)) { + ast_log(LOG_ERROR, "IFX_TAPI_JB_CFG_SET %d failed\n", c); + goto load_error_st; + } + + /* Configure Caller ID type */ + memset(&cid_cfg, 0, sizeof(cid_cfg)); + cid_cfg.nStandard = cid_type; + + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_CID_CFG_SET, &cid_cfg)) { + ast_log(LOG_ERROR, "IIFX_TAPI_CID_CFG_SET %d failed\n", c); + goto load_error_st; + } + + /* Configure voice activity detection */ + if (ioctl(dev_ctx.ch_fd[c], IFX_TAPI_ENC_VAD_CFG_SET, vad_type)) { + ast_log(LOG_ERROR, "IFX_TAPI_ENC_VAD_CFG_SET %d failed\n", c); + goto load_error_st; + } + + /* Setup TAPI <-> internal RTP codec type mapping */ + if (lantiq_setup_rtp(c)) { + goto load_error_st; + } + + /* Set initial hook status */ + iflist[c].channel_state = lantiq_get_hookstatus(c); + + if (iflist[c].channel_state == UNKNOWN) { + goto load_error_st; + } + } + + /* make sure our device will be closed properly */ + ast_register_atexit(lantiq_cleanup); + + restart_monitor(); + led_on(dev_ctx.voip_led); + return AST_MODULE_LOAD_SUCCESS; + +cfg_error_il: + ast_mutex_unlock(&iflock); +cfg_error: + ast_config_destroy(cfg); + return AST_MODULE_LOAD_DECLINE; + +load_error_st: + ast_sched_context_destroy(sched); +load_error: + unload_module(); + ast_free(iflist); + return AST_MODULE_LOAD_FAILURE; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Lantiq TAPI Telephony API Support", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DRIVER +); diff --git a/net/asterisk-chan-lantiq/files/default.exports b/net/asterisk-chan-lantiq/files/default.exports new file mode 100644 index 0000000..6d9344d --- /dev/null +++ b/net/asterisk-chan-lantiq/files/default.exports @@ -0,0 +1,8 @@ +{ + global: + /* See main/asterisk.exports.in for an explanation why this is + * needed. */ + _IO_stdin_used; + local: + *; +}; diff --git a/net/asterisk-chan-lantiq/files/lantiq-asterisk13.conf.sample b/net/asterisk-chan-lantiq/files/lantiq-asterisk13.conf.sample new file mode 100644 index 0000000..74c6f3f --- /dev/null +++ b/net/asterisk-chan-lantiq/files/lantiq-asterisk13.conf.sample @@ -0,0 +1,153 @@ +; +; TAPI Telephony Interface +; +; Configuration file + +[interfaces] +; +; Number of FXS ports (default: 2) +channels = 2 +; +; Create contexts per channel (default: off) +;per_channel_context = off +; +; Set tapi firmware file path +;firmwarefilename = /lib/firmware/danube_firmware.bin +; +; Set tapi bbd file path +;bbdfilename = /lib/firmware/danube_bbd_fxs.bin +; +; Set vmmc device path +;basepath = /dev/vmmc +; +[general] +; +; Gain setting for the receive and transmit path. +; The value is given in dB within the range (-24dB to +12dB), in 1 dB steps. +; +;rxgain = 1 +;txgain = 1 +; +; +; +; Line echo cancller valid types: +; +; off LEC and echo suppressor turned off. +; nlec LEC using fixed window; no echo suppressor. +; wlec LEC using fixed and moving window; no echo suppressor. +; nees LEC using fixed window and echo suppressor. +; nfees LEC using fixed and moving window and echo suppressor. +; es Echo suppressor +; +;echocancel = off +; +; If nlec or wlec is selected than size of the fixed window in narrowband (8 kHz) sampling mode +; can be defined with: +; A value of 0 defaults to: 16 ms if type is nlec or 8 ms if type is wlec: +; +;echocancelfixedwindowsize = 0 +; +; If wlec is selected than size of the moving window in narrowband (8 kHz) sampling mode +; can be defined with: +; A value of 0 defaults to 8 ms. +; +;echocancelnfemovingwindowsize = 0 +; +; If wlec is selected than size of the moving window in wideband (16 kHz) sampling mode +; can be defined with: +; A value of 0 defaults to 8 ms. +; +;echocancelwidefixedwindowsize = 0 +; +; Activate or deactivate line echo cancller NLP (Non Linear Processor) if the LEC is active, +; valid is on or off: +; +;echocancelnlp = off +; +; +; +; Jitter buffer valid types: +; +; fixed Fixed jitter buffer. +; adaptive Adaptive jitter buffer. +; +;jitterbuffertype = fixed +; +; Jitter buffer packet adaptation valid types: +; +; voice Jitter buffer optimized for voice. +; data Jitter buffer optimized for data. +; datanorep Jitter buffer optimized for data but without doing packet repetition. +; +;jitterbufferpackettype = voice +; +; Following jitter buffer values can only be used with jitter buffer adaptive type: +; Turns on or off jitter buffer adaptation: +; +;jitterbufferadaptation = off +; +; Scaling factor multiplied by 16; in adaptive jitter buffer mode, the target average playout delay is +; equal to the estimated jitter multiplied by the scaling factor. The default value for the scaling factor +; is about 1.4 (scaling=22), meaning that the target average playout delay is equal to the estimated +; jitter. If less packets should be dropped because of jitter, the scaling factor has to be increased. An +; increase in the scaling factor will eventually lead to an increased playout delay. +; The supported range is 1 to 16 (16 up to 256). +; +;jitterbufferscalling = 1 +; +; Initial size of the jitter buffer in time stamps of 125 us: +; +;jitterbufferinitialsize = 125 +; +; Minimum size of the jitter buffer in time stamps of 125 us +; +;jitterbufferminsize = 125 +; +; Maximum size of the jitter buffer in time stamps of 125 us +; +;jitterbuffermaxsize = 125 +; +; +; +; Caller id valid standards: +; +; telecordia Bellcore/Telcordia GR-30-CORE; use Bell202 FSK coding of CID information. (default) +; etsifsk ETSI 300-659-1/2/3 V1.3.1; use V.23 FSK coding to transmit CID information. +; etsidtmf ETSI 300-659-1/2/3 V1.3.1; use DTMF transmission of CID information. +; sin SIN 227 Issue 3.4; use V.23 FSK coding of CID information. +; ntt NTT standard: TELEPHONE SERVICE INTERFACES, edition 5; use a modified V.23 FSK coding of +; CID information. +; kpndtmf KPN; use DTMF transmission of CID information. +; kpndtmffsk KPN; use DTMF and FSK transmission of CID information. +; +;calleridtype = telecordia +; +; +; +; Voice activity detection: +; +; on Voice activity detection on; in this case also comfort noise and spectral +; information (nicer noise) is switched on. +; g711 Voice activity detection on with comfort noise generation, +; but without spectral information. +; cng Voice activity detection on with comfort noise generation, but without silence compression +; sc Voice activity detection on with silence compression, +; but without comfort noise generation. +; +;voiceactivitydetection = on +; +; +; +; Timeout between dialed digits, in milliseconds, before placing the call. +; +;interdigit = 4000 +; +; +; +; Tone generator type (default: integrated) +; integrated Use tapi tone generator +; asterisk Use asterisk tone generator where possible +; media Use media tone where possible +; +;tone_generator = integrated +;