--- channels/misdn_config.c (.../https://fsn.dnsalias.com/svn/comma/asterisk/tags/asterisk-1.8.3-import) (revision 5261) +++ channels/misdn_config.c (working copy) @@ -377,6 +377,14 @@ "\tHere you can give a comma separated list, or simply an '*' for any msn." }, { "cc_request_retention", MISDN_CFG_CC_REQUEST_RETENTION, MISDN_CTYPE_BOOL, "yes", NONE, "Enable/Disable call-completion request retention support (ptp)." }, +#ifdef ISDNOIP_BYP + { "bypass", MISDN_CFG_BYPASS, MISDN_CTYPE_BOOL, "no", NONE, + "Set this to yes to enable B-channel bypass for ISDNoIP" }, + { "bypass_chunksize", MISDN_CFG_BYPASS_SIZE, MISDN_CTYPE_INT, "1", NONE, + "Number of milliseconds of audio data per I/O chunk to ISDNoIP" }, + { "bypass_jb", MISDN_CFG_BYPASS_JB, MISDN_CTYPE_INT, "4", NONE, + "ISDNoIP transmit jitter buffer threshold (chunks)" }, +#endif }; static const struct misdn_cfg_spec gen_spec[] = { --- channels/chan_misdn.c (.../https://fsn.dnsalias.com/svn/comma/asterisk/tags/asterisk-1.8.3-import) (revision 5261) +++ channels/chan_misdn.c (working copy) @@ -58,6 +58,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 296582 $") +#define ISDNOIP_BYP 1 + #include #include #include @@ -94,6 +96,26 @@ #include "chan_misdn_config.h" #include "isdn_lib.h" +#ifdef ISDNOIP_BYP + #define ISDNOIP_IOC_MAGIC 'm' + #define ISDNOIP_IOC_MAXNR 7 + #define ISDNOIP_IOC_SET_CHAN _IOW(ISDNOIP_IOC_MAGIC, 0, isdnoip_byp_chan_t) + #define ISDNOIP_IOC_CLEAR_CHAN _IO(ISDNOIP_IOC_MAGIC, 1) + #define ISDNOIP_IOC_BYP_ENABLE _IO(ISDNOIP_IOC_MAGIC, 2) + #define ISDNOIP_IOC_BYP_DISABLE _IO(ISDNOIP_IOC_MAGIC, 3) + #define ISDNOIP_IOC_GET_TX_BUFINFO _IOR(ISDNOIP_IOC_MAGIC, 4, int) + #define ISDNOIP_IOC_SET_TX_JB _IOW(ISDNOIP_IOC_MAGIC, 5, int) + #define ISDNOIP_IOC_SET_RX_CHUNK _IOW(ISDNOIP_IOC_MAGIC, 6, int) + #define ISDNOIP_IOC_GET_RX_BUFINFO _IOR(ISDNOIP_IOC_MAGIC, 7, int) + + /* ioctl channel structure */ + typedef struct { + int cardid; + int intfid; /* 0 - 7 */ + int chanid; /* 0=B1, 1=B2 */ + } isdnoip_byp_chan_t; +#endif + static char global_tracefile[BUFFERSIZE + 1]; static int g_config_initialized = 0; @@ -576,6 +598,14 @@ * \brief Next channel call record in the list. */ struct chan_list *next; +#ifdef ISDNOIP_BYP + int byp_en; + int byp; + int byp_fd; + int byp_chunksize; + int byp_jb; + int byp_drain; +#endif }; @@ -5923,6 +5953,12 @@ misdn_cfg_get(port, MISDN_CFG_TXGAIN, &bc->txgain, sizeof(bc->txgain)); misdn_cfg_get(port, MISDN_CFG_RXGAIN, &bc->rxgain, sizeof(bc->rxgain)); +#ifdef ISDNOIP_BYP + misdn_cfg_get( port, MISDN_CFG_BYPASS, &ch->byp_en, sizeof(int)); + misdn_cfg_get( port, MISDN_CFG_BYPASS_SIZE, &ch->byp_chunksize, sizeof(int)); + misdn_cfg_get( port, MISDN_CFG_BYPASS_JB, &ch->byp_jb, sizeof(int)); +#endif + misdn_cfg_get(port, MISDN_CFG_INCOMING_EARLY_AUDIO, &ch->incoming_early_audio, sizeof(ch->incoming_early_audio)); misdn_cfg_get(port, MISDN_CFG_SENDDTMF, &bc->send_dtmf, sizeof(bc->send_dtmf)); @@ -7244,11 +7280,12 @@ ast_debug(1, "Detected inband DTMF digit: %c\n", f->subclass.integer); - if (tmp->faxdetect && (f->subclass.integer == 'f')) { + if (tmp->faxdetect && (f->subclass.integer == 'f' || f->subclass.integer == 'e')) { /* Fax tone -- Handle and return NULL */ if (!tmp->faxhandled) { struct ast_channel *ast = tmp->ast; tmp->faxhandled++; + if (f->subclass.integer == 'f') chan_misdn_log(0, tmp->bc->port, "Fax detected, preparing %s for fax transfer.\n", ast->name); tmp->bc->rxgain = 0; isdn_lib_update_rxgain(tmp->bc); @@ -7261,8 +7298,7 @@ #endif isdn_lib_update_ec(tmp->bc); isdn_lib_stop_dtmf(tmp->bc); - switch (tmp->faxdetect) { - case 1: + if (f->subclass.integer == 'f' && tmp->faxdetect == 1) { if (strcmp(ast->exten, "fax")) { char *context; char context_tmp[BUFFERSIZE]; @@ -7282,31 +7318,94 @@ } else { ast_debug(1, "Already in a fax extension, not redirecting\n"); } - break; - case 2: + } else { ast_verb(3, "Not redirecting %s to fax extension, nojump is set.\n", ast->name); - break; - default: - break; } } else { ast_debug(1, "Fax already handled\n"); } } - if (tmp->ast_dsp && (f->subclass.integer != 'f')) { + if (tmp->ast_dsp && f->subclass.integer != 'f' && f->subclass.integer != 'e') { chan_misdn_log(2, tmp->bc->port, " --> * SEND: DTMF (AST_DSP) :%c\n", f->subclass.integer); } return f; } +static void misdn_byp_jb_empty(struct chan_list* ch) { + struct misdn_bchannel *bc = ch->bc; + char buf[1024]; + //char *data=&buf[mISDN_HEADER_LEN]; + //iframe_t *txfrm= (iframe_t*)buf; + int jlen; + int i; + int max; + int fill = 0; + if (!ch->byp) + return; + + /* small chunk sizes (e.g. 1 for backward compatibility) can do up to 8 writes */ + if (ch->byp_chunksize < 4) + max = 8; + else + max = 4; + + if (ioctl(ch->byp_fd, ISDNOIP_IOC_GET_TX_BUFINFO, &fill) < 0) { + fill = 0; // old driver, don't know, just fill it + } else { + chan_misdn_log(9, bc->port, "Bypass write fill %d\n", fill); + } + + if ((max - fill) < 0) { + chan_misdn_log(8, bc->port, "Bypass write max %d fill %d, do nothing\n", max, fill); + return; + } + + /* Fill bypass buffers, up to 'max' chunks of samples (=max*chunksize ms) if available */ + for (i = 0; i < ((max - fill)+1); i++) { + int t; + struct pollfd pfd = { .fd = -1, .events = POLLOUT }; + + pfd.fd = ch->byp_fd; + t = ast_poll(&pfd, 1, 0); + + if (!t) { + // No room in buffer, just exit + if (i == 0) + chan_misdn_log(8, bc->port, "Bypass write select timed out\n"); + return; + } + if (t<0) { + chan_misdn_log(-1, bc->port, "Bypass write select error (err=%s)\n",strerror(errno)); + return; + } + + if (pfd.revents & POLLOUT) { + jlen = cb_jb_empty(bc, buf, ch->byp_chunksize*8); + if (jlen >= 8) { + int ret = write(ch->byp_fd, buf, jlen); + if (ret<=0) { + chan_misdn_log(0, bc->port, "Bypass write returned <=0 (err=%s)\n", strerror(errno)); + } + } else { + if (i == 0) + chan_misdn_log(8, bc->port, "Bypass write no samples available\n"); + return; + } + } else { + chan_misdn_log(1, bc->port, "Bypass write full!\n"); + } + } +} + static struct ast_frame *misdn_read(struct ast_channel *ast) { struct chan_list *tmp; int len, t; struct pollfd pfd = { .fd = -1, .events = POLLIN }; + int port, nojitter, capability; if (!ast) { chan_misdn_log(1, 0, "misdn_read called without ast\n"); @@ -7322,8 +7421,26 @@ return NULL; } + port = tmp->bc->port; + nojitter = tmp->bc->nojitter; + capability = tmp->bc->capability; + +#ifdef ISDNOIP_BYP + if (tmp->byp_en) { + if (!tmp->byp) { + t = 1; + } else { + int ms = tmp->byp_chunksize+5; + pfd.fd = tmp->byp_fd; + t = ast_poll(&pfd, 1, ms); + } + } else { +#endif pfd.fd = tmp->pipe[0]; t = ast_poll(&pfd, 1, 20); +#ifdef ISDNOIP_BYP + } +#endif if (t < 0) { chan_misdn_log(-1, tmp->bc->port, "poll() error (err=%s)\n", strerror(errno)); @@ -7334,7 +7451,33 @@ chan_misdn_log(3, tmp->bc->port, "poll() timed out\n"); len = 160; } else if (pfd.revents & POLLIN) { +#ifdef ISDNOIP_BYP + if (tmp->byp_en) { + if (!tmp->byp) { + len = tmp->byp_chunksize*8; + memset(tmp->ast_rd_buf, 0x55, len); + } else { + int size = 2*tmp->byp_chunksize*8; + len = read(tmp->byp_fd, tmp->ast_rd_buf, size); + chan_misdn_log(9, port, "Read %d bytes\n", len); +#ifdef DEBUG_GRAB_AUDIO + if (tmp->bc->port == 1 && tmp->bc->channel == 1) { + if (!read_fp) + read_fp = fopen("/tmp/misdn.raw", "wb"); + fwrite(tmp->ast_rd_buf, 1, len, read_fp); + } +#endif + if (!nojitter && misdn_cap_is_speech(capability)) { + /* synchronize transmission from jb */ + misdn_byp_jb_empty(tmp); + } + } + } else { +#endif len = read(tmp->pipe[0], tmp->ast_rd_buf, sizeof(tmp->ast_rd_buf)); +#ifdef ISDNOIP_BYP + } +#endif if (len <= 0) { /* we hangup here, since our pipe is closed */ @@ -7359,22 +7502,28 @@ if (tmp->faxdetect_timeout) { if (ast_tvzero(tmp->faxdetect_tv)) { tmp->faxdetect_tv = ast_tvnow(); - chan_misdn_log(2, tmp->bc->port, "faxdetect: starting detection with timeout: %ds ...\n", tmp->faxdetect_timeout); + chan_misdn_log(2, port, "faxdetect: starting detection with timeout: %ds ...\n", tmp->faxdetect_timeout); return process_ast_dsp(tmp, &tmp->frame); } else { struct timeval tv_now = ast_tvnow(); int diff = ast_tvdiff_ms(tv_now, tmp->faxdetect_tv); if (diff <= (tmp->faxdetect_timeout * 1000)) { - chan_misdn_log(5, tmp->bc->port, "faxdetect: detecting ...\n"); - return process_ast_dsp(tmp, &tmp->frame); + chan_misdn_log(5, port, "faxdetect: detecting ...\n"); + char rb[4096]; + memcpy(rb, tmp->frame.data.ptr, tmp->frame.datalen); + struct ast_frame* f = process_ast_dsp(tmp, &tmp->frame); + if (f == &tmp->frame) { + memcpy(tmp->frame.data.ptr, rb, tmp->frame.datalen); + } + return f; } else { - chan_misdn_log(2, tmp->bc->port, "faxdetect: stopping detection (time ran out) ...\n"); + chan_misdn_log(2, port, "faxdetect: stopping detection (time ran out) ...\n"); tmp->faxdetect = 0; return &tmp->frame; } } } else { - chan_misdn_log(5, tmp->bc->port, "faxdetect: detecting ... (no timeout)\n"); + chan_misdn_log(5, port, "faxdetect: detecting ... (no timeout)\n"); return process_ast_dsp(tmp, &tmp->frame); } } else { @@ -7479,6 +7628,62 @@ } chan_misdn_log(9, ch->bc->port, "Sending :%d bytes to MISDN\n", frame->samples); +#ifdef ISDNOIP_BYP + if (ch->byp_en && (ch->bc->nojitter || !misdn_cap_is_speech(ch->bc->capability))) { + int t; + struct timeval tv; + struct pollfd pfd = { .fd = -1, .events = POLLOUT }; + + if (!ch->byp) + return 0; + + tv.tv_sec=0; + tv.tv_usec=125*frame->samples+1000; + + int fill = 0; + if (ioctl(ch->byp_fd, ISDNOIP_IOC_GET_TX_BUFINFO, &fill) < 0) { + // old driver, don't know + } else { + chan_misdn_log(9, ch->bc->port, "Bypass write fill %d\n", fill); + } + if (ch->byp_drain) { + chan_misdn_log(8, ch->bc->port, "Bypass write draining... fill %d\n", fill); + if (fill < ch->byp_jb) + ch->byp_drain = 0; + else { + // Wait a frame... + select(0,NULL,NULL,NULL,&tv); + return 0; + } + } else if (fill >= (ch->byp_jb+2)) { + chan_misdn_log(8, ch->bc->port, "Bypass write fill %d, backing off\n", fill); + ch->byp_drain = 1; + // Wait a frame... + select(0,NULL,NULL,NULL,&tv); + return 0; + } + + pfd.fd = ch->byp_fd; + t = ast_poll(&pfd, 1, tv.tv_usec/1000); + + if (!t) { + chan_misdn_log(8, ch->bc->port, "Bypass write select timed out\n"); + return 0; + } + if (t<0) { + chan_misdn_log(-1, ch->bc->port, "Bypass write select error (err=%s)\n",strerror(errno)); + return 0; + } + if (pfd.revents & POLLOUT) { + int ret = write(ch->byp_fd, frame->data.ptr, frame->samples); + if (ret<=0) { + chan_misdn_log(0, ch->bc->port, "Bypass write returned <=0 (err=%s)\n", strerror(errno)); + } + } else { + chan_misdn_log(1, ch->bc->port, "Bypass write full!\n"); + } + } else { +#endif if (!ch->bc->nojitter && misdn_cap_is_speech(ch->bc->capability)) { /* Buffered Transmit (triggered by read from isdn side)*/ if (misdn_jb_fill(ch->jb, frame->data.ptr, frame->samples) < 0) { @@ -7491,6 +7696,9 @@ /* transmit without jitterbuffer */ i = misdn_lib_tx2misdn_frm(ch->bc, frame->data.ptr, frame->samples); } +#ifdef ISDNOIP_BYP + } +#endif return 0; } @@ -7723,13 +7931,22 @@ ast_mutex_destroy(&ch->overlap_tv_lock); } +#ifdef ISDNOIP_BYP + if (ch->byp_en) { + close(ch->byp_fd); + ch->byp = 0; + } else { +#endif if (-1 < ch->pipe[0]) { close(ch->pipe[0]); } if (-1 < ch->pipe[1]) { close(ch->pipe[1]); } +#ifdef ISDNOIP_BYP } +#endif +} /*! Returns a reference to the new chan_list. */ static struct chan_list *chan_list_init(int orig) @@ -7756,6 +7973,41 @@ return cl; } +#ifdef ISDNOIP_BYP +/* Init B-channel bypass if enabled - must be called after read_config */ +static void misdn_init_bypass(struct chan_list* chlist, int port, int c) +{ + if (chlist->byp_en) { + if ((chlist->byp_fd = open("/dev/isdnoip", O_RDWR)) < 0) { + perror("cannot open /dev/isdnoip\n"); + } + + if (c) { + isdnoip_byp_chan_t cinfo; + cinfo.cardid = 0; // intfid is used to select comma unit and port + cinfo.intfid = c ? port-1 : 0; /* Ticket #79 */ + cinfo.chanid = c ? c-1 : 0; + chan_misdn_log(2, 0, " --> * INIT SETUP CHANNEL port=%d chan=%d\n",port,c); + if (ioctl(chlist->byp_fd, ISDNOIP_IOC_SET_CHAN, &cinfo) < 0) { + perror("cannot ioctl /dev/isdnoip\n"); + } else { + if (ioctl(chlist->byp_fd, ISDNOIP_IOC_SET_RX_CHUNK, &chlist->byp_chunksize) < 0) { + // silently fail, could be old driver + } + if (ioctl(chlist->byp_fd, ISDNOIP_IOC_SET_TX_JB, &chlist->byp_jb) < 0) { + // silently fail, could be old driver + } + chlist->byp = 1; + chlist->byp_drain = 0; + chlist->ast->fds[0] = chlist->byp_fd; + close(chlist->pipe[0]); + close(chlist->pipe[1]); + } + } + } +} +#endif + static struct ast_channel *misdn_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause) { struct ast_channel *ast; @@ -7998,6 +8250,9 @@ /* fill in the config into the objects */ read_config(cl); +#ifdef ISDNOIP_BYP + misdn_init_bypass(cl, port, channel); +#endif /* important */ cl->need_hangup = 0; @@ -8084,7 +8339,34 @@ ast_change_name(tmp, newname); chan_misdn_log(3, port, " --> updating channel name to [%s]\n", tmp->name); } +#ifdef ISDNOIP_BYP + struct chan_list *ch=MISDN_ASTERISK_TECH_PVT(tmp); + if (ch->byp_en) { + if (!ch->byp && (c == 1 || c == 2)) { + isdnoip_byp_chan_t cinfo; + cinfo.cardid = 0; + cinfo.intfid = port - 1; + cinfo.chanid = c - 1; + if (ioctl(ch->byp_fd, ISDNOIP_IOC_SET_CHAN, &cinfo) < 0) { + perror("cannot ioctl /dev/isdnoip\n"); + } else { + if (ioctl(ch->byp_fd, ISDNOIP_IOC_SET_RX_CHUNK, &ch->byp_chunksize) < 0) { + // silently fail, could be old driver } + if (ioctl(ch->byp_fd, ISDNOIP_IOC_SET_TX_JB, &ch->byp_jb) < 0) { + // silently fail, could be old driver + } + chan_misdn_log(2, 0, " --> * SETUP CHANNEL port=%d chan=%d\n",port,c); + ch->byp = 1; + ch->byp_drain = 0; + ch->ast->fds[0] = ch->byp_fd; + close(ch->pipe[0]); + close(ch->pipe[1]); + } + } + } +#endif +} static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, const char *linkedid, int port, int c) { @@ -8109,6 +8391,13 @@ ast_callerid_parse(callerid, &cid_name, &cid_num); } + // Fix for #654 + /* TBD: for now use original until clarification received + if (c == 0) { + tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", linkedid, 0, "%s/%d-_u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); + } else { + tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", linkedid, 0, "%s/%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); + }*/ tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", linkedid, 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); if (tmp) { chan_misdn_log(2, 0, " --> * NEW CHANNEL dialed:%s caller:%s\n", exten, callerid); @@ -10131,6 +10420,9 @@ } read_config(ch); +#ifdef ISDNOIP_BYP + misdn_init_bypass(ch, bc->port, bc->channel); +#endif export_ch(chan, bc, ch); @@ -10739,6 +11031,11 @@ struct pollfd pfd = { .fd = ch->pipe[1], .events = POLLOUT }; int t; +#ifdef ISDNOIP_BYP + if (ch->byp_en) { + chan_misdn_log(1, bc->port, "isdnoip_byp: not expecting B-channel data from stack\n"); + } else { +#endif t = ast_poll(&pfd, 1, 0); if (t < 0) { @@ -10762,7 +11059,10 @@ } else { chan_misdn_log(1, bc->port, "Write Pipe full!\n"); } +#ifdef ISDNOIP_BYP } +#endif + } break; case EVENT_TIMEOUT: if (ch && bc) { --- channels/misdn/isdn_lib.c (.../https://fsn.dnsalias.com/svn/comma/asterisk/tags/asterisk-1.8.3-import) (revision 5261) +++ channels/misdn/isdn_lib.c (working copy) @@ -1128,7 +1128,8 @@ li.st = bc->b_stid; /* given idx */ -#define MISDN_DSP +#undef MISDN_DSP +/* MW: we get a major improvement in performance by dropping the DSP */ #ifndef MISDN_DSP bc->nodsp=1; #endif --- channels/misdn/chan_misdn_config.h (.../https://fsn.dnsalias.com/svn/comma/asterisk/tags/asterisk-1.8.3-import) (revision 5261) +++ channels/misdn/chan_misdn_config.h (working copy) @@ -23,6 +23,7 @@ #define CHAN_MISDN_CONFIG_H #define BUFFERSIZE 512 +#define ISDNOIP_BYP 1 enum misdn_cfg_elements { @@ -98,6 +99,11 @@ MISDN_CFG_FAXDETECT_CONTEXT, /* char[] */ MISDN_CFG_FAXDETECT_TIMEOUT, /* int */ MISDN_CFG_PTP, /* int (bool) */ +#ifdef ISDNOIP_BYP + MISDN_CFG_BYPASS, /* int (bool) */ + MISDN_CFG_BYPASS_SIZE, /* int */ + MISDN_CFG_BYPASS_JB, /* int */ +#endif MISDN_CFG_LAST, /* general config items */