Index: channels/chan_dahdi.c =================================================================== --- channels/chan_dahdi.c (revision 221968) +++ channels/chan_dahdi.c (working copy) @@ -7870,7 +7870,7 @@ p->subs[idx].f.data.ptr = NULL; p->subs[idx].f.datalen= 0; } - if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress || p->waitingfordt.tv_sec) && !idx) { + if (!p->digital && p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress || p->waitingfordt.tv_sec) && !idx) { /* Perform busy detection etc on the dahdi line */ int mute; Index: apps/app_v110.c =================================================================== --- apps/app_v110.c (revision 0) +++ apps/app_v110.c (revision 0) @@ -0,0 +1,993 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, David Woddhouse + * + * David Woodhouse + * Generic app execution and other stuff by Moises Silva + * + * 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 V.110 application -- accept incoming v.110 call and spawn a custom application + * + * \author\verbatim David Woodhouse \endverbatim + * + * \ingroup applications + */ + +/*** MODULEINFO + no + ***/ + +#include "asterisk.h" + +#include "asterisk/file.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/logger.h" +#include "asterisk/app.h" +#include "asterisk/transcap.h" + +#include +#include + +#ifndef __BYTE_ORDER +#error "__BYTE_ORDER must be defined to compile this application" +#endif + +static char *app = "V110"; + +static char *synopsis = "Accept V.110 call."; + +static char *descrip = + " V110(): Check the incoming call type. For V.110 data calls on an ISDN channel\n" + " Answers the call, then and launches a custom application connecting stdout and stdin to the data stream.\n"; + + +#define URATE_EBITS 0 /* In E-bits or negotiated in-band */ +#define URATE_600 1 +#define URATE_1200 2 +#define URATE_2400 3 +#define URATE_3600 4 +#define URATE_4800 5 +#define URATE_7200 6 +#define URATE_8000 7 +#define URATE_9600 8 +#define URATE_14400 9 /* isdn4linux abuses this for 38400 */ +#define URATE_16000 10 +#define URATE_19200 11 +#define URATE_32000 12 + +#define URATE_48000 14 +#define URATE_56000 15 +#define URATE_135 21 /* 134.5 */ +#define URATE_100 22 +#define URATE_75_1200 23 /* 75 forward, 1200 back */ +#define URATE_1200_75 24 /* 1200 forward, 75 back */ +#define URATE_50 25 +#define URATE_75 26 +#define URATE_110 27 +#define URATE_150 28 +#define URATE_200 29 +#define URATE_300 30 +#define URATE_12000 31 + +#define IBUF_LEN 8192 +#define OBUF_LEN 1024 +#define OBUF_THRESH 16 + +struct v110_state { + /* Input v.110 frame buffer */ + unsigned char vframe_in[10]; + unsigned vframe_in_len; + + /* Input data buffer */ + unsigned char ibuf[IBUF_LEN]; + unsigned ibufend; + unsigned ibufstart; + unsigned nextibit; + + /* Output data buffer */ + unsigned char obuf[OBUF_LEN]; + unsigned obufend; + unsigned obufstart; + unsigned nextobit; + + /* Output v.110 frame buffer */ + unsigned nextoline; + + int bufwarning; + unsigned char cts, rts, sbit; + int synccount; + + /* frame data */ + struct ast_frame f; + unsigned char friendly[AST_FRIENDLY_OFFSET]; + unsigned char fdata[4096]; + + /* hooks to call depending on the rate */ + void (*input_frame)(struct v110_state *, struct ast_frame *); + void (*fill_outframe)(struct v110_state *, int); + + /* trace in/out frames */ + char raw_trace_prefix[255]; + char data_trace_prefix[255]; + FILE *trace_raw_in; + FILE *trace_raw_out; + FILE *trace_data_in; + FILE *trace_data_out; +}; + +void v110_input_frame_x4(struct v110_state *vs, struct ast_frame *); +void v110_input_frame_x2(struct v110_state *vs, struct ast_frame *); +void v110_input_frame_x1(struct v110_state *vs, struct ast_frame *); +void v110_fill_outframe_x4(struct v110_state *vs, int); +void v110_fill_outframe_x2(struct v110_state *vs, int); +void v110_fill_outframe_x1(struct v110_state *vs, int); + +int launch_custom_app(const char *appname, char *appargs[], int *readfd, int *writefd); + +/* David Woddhouse used the inverse bit order as the V.110 spec + * may be mISDN (the original user of the app) provided the data that way, he + * seemed to work a lot on PPC architecture and I have seen bit endianess in PPC + * in the past, so this may be another case. Anyways, to make it work with dahdi x86 + * I had to revert the bits (TODO: search for Asterisk routine to do this) + * */ +const unsigned char bit_reverse_table[256] = +{ + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff +}; + +static struct ast_frame *v110_read_from_isdn_chan(struct v110_state *s, struct ast_channel *c) +{ + unsigned b; + unsigned char *chandata; + struct ast_frame *f = ast_read(c); + if (!f) { + return NULL; + } + chandata = f->data.ptr; + if (s->trace_raw_in) { + int len = 0; + len = fwrite(chandata, 1, f->datalen, s->trace_raw_in); + if (len != f->datalen) { + ast_log(LOG_WARNING, "just wrote %d bytes of raw input when %d were requested\n", len, f->datalen); + } + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + chandata = f->data.ptr; + for (b = 0; b < f->datalen; b++) { + chandata[b] = bit_reverse_table[chandata[b]]; + } +#endif + return f; +} + +static int v110_write_to_isdn_chan(struct v110_state *s, struct ast_channel *c) +{ + unsigned b; + unsigned char *chandata = s->f.data.ptr; +#if __BYTE_ORDER == __LITTLE_ENDIAN + for (b = 0; b < s->f.datalen; b++) { + chandata[b] = bit_reverse_table[chandata[b]]; + } +#endif + + if (s->trace_raw_out) { + int len = 0; + len = fwrite(chandata, 1, s->f.datalen, s->trace_raw_out); + if (len != s->f.datalen) { + ast_log(LOG_WARNING, "just wrote %d bytes of raw output when %d were requested\n", len, s->f.datalen); + } + } + return ast_write(c, &s->f); +} + +#define V110_MAX_ARGS 10 +#define V110_TRACE_OPEN(member) \ + snprintf(trace_file_name, sizeof(trace_file_name), "%s-"#member, trace_prefix_var); \ + vs->member = fopen(trace_file_name, "wb"); \ + if (vs->member) { \ + ast_log(LOG_NOTICE, "V.110 activated trace file %s\n", trace_file_name); \ + } else { \ + ast_log(LOG_ERROR, "V.110 trace file '%s' could not be open: %s\n", trace_file_name, strerror(errno)); \ + } +static int echo_v110(struct ast_channel *chan, const char *data) +{ + int res = -1; + int apppid = -1; + int appstatus = 0; + int appargc = 0; + int urate = -1; + int primelen = 0; + char trace_file_name[255]; + char *appargv[V110_MAX_ARGS]; + char *userargs = NULL; + char *arg = NULL; + const char *trace_prefix_var = NULL; + const char *urate_var = NULL; + struct ast_frame *f = NULL; + struct v110_state *vs = NULL; + int writefd = -1; + int readfd = -1; + + if (!(chan->transfercapability & AST_TRANS_CAP_DIGITAL)) { + ast_log(LOG_ERROR, "V.110: Not an async V.110 call, capabilities = [%d]\n", chan->transfercapability); + return -1; + } + + if (!data) { + ast_log(LOG_ERROR, "V.110: You need to specify an application to launch\n"); + return -1; + } + userargs = ast_strdupa(data); + + urate_var = pbx_builtin_getvar_helper(chan, "ISDN_V110_URATE"); + if (urate_var) { + urate = atoi(urate_var); + } + + if (urate == -1) { + ast_log(LOG_NOTICE, "V.110 assuming 9600 rate, use ISDN_V110_URATE variable to override\n"); + urate = URATE_9600; + } + + vs = ast_calloc(1, sizeof(*vs)); + if (!vs) { + ast_log(LOG_ERROR, "Allocation of V.110 data structure failed\n"); + res = -1; + goto out; + } + + if (!strcasecmp(chan->tech->type, "mISDN")) { + /* why isn't transfercapability digital enough for mISDN channel driver?? */ + pbx_builtin_setvar_helper(chan, "MISDN_DIGITAL_TRANS", "1"); + } + /* we could restrict usage to DAHDI or mISDN channels, but, the truth is that this application only cares about + the provided V.110 bit stream, if some other channel can provide that, good for them */ + + trace_prefix_var = pbx_builtin_getvar_helper(chan, "ISDN_V110_TRACE"); + if (trace_prefix_var) { + V110_TRACE_OPEN(trace_raw_in); + V110_TRACE_OPEN(trace_raw_out); + V110_TRACE_OPEN(trace_data_in); + V110_TRACE_OPEN(trace_data_out); + } + + /* TODO: Waste bits between characters instead of relying on flow control */ + switch (urate) { + case URATE_1200: + case URATE_2400: + case URATE_4800: + case URATE_7200: + case URATE_8000: + case URATE_9600: + vs->input_frame = v110_input_frame_x4; + vs->fill_outframe = v110_fill_outframe_x4; + primelen = 200; + break; + + case URATE_12000: + case URATE_16000: + case URATE_19200: + vs->input_frame = v110_input_frame_x2; + vs->fill_outframe = v110_fill_outframe_x2; + primelen = 200; + break; + + case URATE_14400: /* NB. isdn4linux 38400 */ + case URATE_32000: + vs->input_frame = v110_input_frame_x1; + vs->fill_outframe = v110_fill_outframe_x1; + primelen = 200; + break; + + default: + ast_log(LOG_NOTICE, "V.110 call at rate %d not supported\n", urate); + goto out; + } + + vs->nextoline = 10; + vs->rts = 0x80; + vs->sbit = 0x80; + vs->cts = 1; + vs->synccount = 5; + vs->bufwarning = 5; + vs->f.data.ptr = vs->fdata; + vs->f.offset = AST_FRIENDLY_OFFSET; + vs->f.subclass = chan->readformat; + vs->f.frametype = AST_FRAME_VOICE; + + /* if arguments provided, then use them to launch a custom app */ + memset(appargv, 0, sizeof(appargv)); + arg = strsep(&userargs, ","); + while (arg && strlen(arg) && appargc < (V110_MAX_ARGS - 1)) { + appargv[appargc++] = arg; + arg = strsep(&userargs, ","); + } + + apppid = launch_custom_app(appargv[0], appargv, &readfd, &writefd); + if (apppid == -1) { + goto out; + } + + ast_log(LOG_NOTICE, "Accepting V.110 call\n"); + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + vs->fill_outframe(vs, primelen); + v110_write_to_isdn_chan(vs, chan); + res = 0; + while (ast_waitfor(chan, -1) > -1) { + int r, want; + + f = v110_read_from_isdn_chan(vs, chan); + if (!f) { + break; + } + + if (f->frametype != AST_FRAME_VOICE) { + ast_frfree(f); + continue; + } + + f->delivery.tv_sec = 0; + f->delivery.tv_usec = 0; + + vs->input_frame(vs, f); + +#ifndef V110_LOOPBACK + /* normal operation */ + want = f->datalen; + while (want > 4096) { + vs->fill_outframe(vs, 4096); + if (v110_write_to_isdn_chan(vs, chan)) { + res = -1; + goto out; + } + want -= 4096; + } + + vs->fill_outframe(vs, f->datalen); + if (v110_write_to_isdn_chan(vs, chan)) { + res = -1; + break; + } +#else + /* loopback mode for testing */ + if (f->datalen >= 8) + ast_log(LOG_NOTICE, "Received frame: %02x %02x %02x %02x %02x %02x %02x %02x\n", + ((unsigned char *)f->data.ptr)[0], + ((unsigned char *)f->data.ptr)[1], + ((unsigned char *)f->data.ptr)[2], + ((unsigned char *)f->data.ptr)[3], + ((unsigned char *)f->data.ptr)[4], + ((unsigned char *)f->data.ptr)[5], + ((unsigned char *)f->data.ptr)[6], + ((unsigned char *)f->data.ptr)[7]); + if (ast_write(chan, f)) { + res = -1; + break; + } +#endif + + /* Flush v.110 incoming buffer */ + if (vs->ibufend > vs->ibufstart) { + want = vs->ibufend - vs->ibufstart; + } else if (vs->ibufend < vs->ibufstart) { + want = IBUF_LEN - vs->ibufstart; + } else { + want = 0; + } + if (want) { + if (vs->trace_data_in) { + int len = 0; + len = fwrite(&vs->ibuf[vs->ibufstart], 1, want, vs->trace_data_in); + if (len != want) { + ast_log(LOG_WARNING, "Just wrote %d bytes of input data when %d were requested\n", + len, want); + } + } + r = write(writefd, &vs->ibuf[vs->ibufstart], want); + if (r < 0 && errno == EAGAIN) { + r = 0; + } + if (r < 0) { + ast_log(LOG_WARNING, "Error writing data: %s\n", strerror(errno)); + res = -1; + goto out; + } + vs->ibufstart += r; + if (vs->ibufstart == IBUF_LEN) { + vs->ibufstart = 0; + } + + /* Set flow control state. */ + if (r < want) { + vs->rts = 0x80; + } else { + vs->rts = 0; + } + } + + /* Replenish v.110 outgoing buffer */ + if (vs->obufend >= vs->obufstart) { + if (vs->obufend - vs->obufstart < OBUF_THRESH) { + want = OBUF_LEN - vs->obufend - !vs->obufstart; + } else { + want = 0; + } + } else { + if (vs->obufstart + OBUF_LEN - vs->obufend < OBUF_THRESH) { + want = vs->obufstart - vs->obufend - 1; + } else { + want = 0; + } + } + if (want) { + r = read(readfd, &vs->obuf[vs->obufend], want); + if (r < 0 && errno == EAGAIN) { + r = 0; + } + if (r < 0) { + /* It's expected that we get error when the other end closes the pipe or pty */ + ast_log(LOG_DEBUG, "Error reading application data: %s\n", strerror(errno)); + goto out; + } + if (vs->trace_data_out) { + int len = 0; + len = fwrite(&vs->obuf[vs->obufend], 1, r, vs->trace_data_out); + if (len != r) { + ast_log(LOG_WARNING, "Just wrote %d bytes of output data when %d were requested\n", + len, want); + } + } + vs->obufend += r; + if (vs->obufend == OBUF_LEN) { + vs->obufend = 0; + } + } + } + + +out: + ast_log(LOG_DEBUG, "Going out of app_v110\n"); + + /* free structures */ + if (vs) { + ast_free(vs); + } + if (f) { + ast_frfree(f); + } + + /* close traces */ + if (vs->trace_raw_in) { + fclose(vs->trace_raw_in); + } + if (vs->trace_raw_out) { + fclose(vs->trace_raw_out); + } + if (vs->trace_data_in) { + fclose(vs->trace_data_in); + } + if (vs->trace_data_out) { + fclose(vs->trace_data_out); + } + + /* cleanup custom app */ + if (apppid > -1) { + ast_log(LOG_DEBUG, "Cleaning application pid %d\n", apppid); + kill(apppid, SIGINT); + waitpid(apppid, &appstatus, 0); + ast_log(LOG_DEBUG, "Application status = %d\n", WEXITSTATUS(appstatus)); + close(readfd); + close(writefd); + } + return res; +} + +void v110_process_frame(struct v110_state *vs); +void v110_process_frame(struct v110_state *vs) +{ + int octet; + + + if (0) ast_log(LOG_NOTICE, "frame %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + vs->vframe_in[0], vs->vframe_in[1], vs->vframe_in[2], + vs->vframe_in[3], vs->vframe_in[4], vs->vframe_in[5], + vs->vframe_in[6], vs->vframe_in[7], vs->vframe_in[8], + vs->vframe_in[9]); + + /* Check that line 5 (E-bits) starts '1011'. */ + if ((vs->vframe_in[5] & 0xf) != 0xd) + return; + + /* Check that each other octet starts with '1' */ + if (!(vs->vframe_in[1] & vs->vframe_in[2] & vs->vframe_in[3] & + vs->vframe_in[4] & vs->vframe_in[6] & vs->vframe_in[7] & + vs->vframe_in[8] & vs->vframe_in[9] & 0x01)) + return; + + /* Extract flow control signal from last octet */ + if (vs->synccount) { + if (!--vs->synccount) { + ast_log(LOG_NOTICE, "V.110 synchronisation achieved\n"); + vs->sbit = 0; + vs->rts = 0; + } + } else + vs->cts = vs->vframe_in[7] & 0x80; + + for (octet = 1; octet < 10; octet++) { + unsigned char tmp; + + /* Skip E-bits in line 5 */ + if (octet == 5) + continue; + + tmp = vs->vframe_in[octet] & 0x7e; + + /* Search for start bit if not yet found */ + if (!vs->nextibit) { + + /* First check for no zero bits. This will be common */ + if (tmp == 0x7e) + continue; + + /* Check for start bit being last in the octet */ + if (tmp == 0x3e) { + vs->nextibit = 1; /* Expecting first data bit now */ + vs->ibuf[vs->ibufend] = 0; + continue; + } + + /* Scan for the start bit, copy the data bits (of which + there will be at least one) into the next byte of ibuf */ + vs->nextibit = 7; + do { + tmp >>= 1; + vs->nextibit--; + } while (tmp & 1); + + /* Start bit is now (host's) LSB */ + vs->ibuf[vs->ibufend] = tmp >> 1; + continue; + } + + tmp >>= 1; + + if (vs->nextibit < 9) { + /* Add next bits of incoming byte to ibuf */ + vs->ibuf[vs->ibufend] |= tmp << (vs->nextibit-1); + + + if (vs->nextibit <= 3) { + /* Haven't finished this byte (including stop) yet */ + vs->nextibit += 6; + continue; + } + + tmp >>= (9 - vs->nextibit); + } + + /* Check for stop bit */ + if (tmp & 1) { + unsigned newend = (vs->ibufend + 1) & (IBUF_LEN-1); + + if (newend == vs->ibufstart) { + /* Buffer full. This shouldn't happen because we should + have asserted flow control long ago */ + if (vs->bufwarning) { + vs->bufwarning--; + ast_log(LOG_NOTICE, "incoming buffer full\n"); + } + continue; + } else + vs->ibufend = newend; + } else { + ast_log(LOG_NOTICE, "No stop bit\n"); + } + + /* Now, scan for next start bit */ + tmp >>= 1; + vs->nextibit -= 4; + while (vs->nextibit && (tmp & 1)) { + tmp >>= 1; + vs->nextibit--; + } + if (vs->nextibit > 1) + vs->ibuf[vs->ibufend] = tmp >> 1; + + } + +} + +/* We don't handle multiple multiplexed channels. Nobody really does */ +void v110_input_frame_x4(struct v110_state *vs, struct ast_frame *f) +{ + int datalen = f->datalen; + unsigned char *frame_data = f->data.ptr; + + { + FILE *fp=fopen("/tmp/in","a"); + int i; + for (i=0;idatalen;i++) { + fprintf(fp,"%x ",frame_data[i]); + } + fprintf(fp, "%s", "\n"); + fclose(fp); + } + + while (datalen) { + if (vs->vframe_in_len < 4) { + /* Find zero octet in buffer */ + if ( (*frame_data) & 3 ) { + vs->vframe_in_len = 0; + frame_data++; + datalen--; + continue; + } + /* Found a suitable byte. Add it. */ + if (++vs->vframe_in_len == 4) + memset(vs->vframe_in, 0, 10); + frame_data++; + datalen--; + continue; + } + /* Add in these two bits */ + vs->vframe_in[vs->vframe_in_len/4] |= + ((*frame_data) & 3) << ((vs->vframe_in_len & 3) * 2); + + vs->vframe_in_len++; + frame_data++; + datalen--; + + if (vs->vframe_in_len == 40) { + v110_process_frame(vs); + vs->vframe_in_len = 0; + } + } +} + +void v110_input_frame_x2(struct v110_state *vs, struct ast_frame *f) +{ + int datalen = f->datalen; + unsigned char *frame_data = f->data.ptr; + + while (datalen) { + if (vs->vframe_in_len < 2) { + /* Find zero octet in buffer */ + if ( (*frame_data) & 7 ) { + vs->vframe_in_len = 0; + frame_data++; + datalen--; + continue; + } + /* Found a suitable byte. Add it. */ + if (++vs->vframe_in_len == 2) + memset(vs->vframe_in, 0, 10); + frame_data++; + datalen--; + continue; + } + /* Add in these four bits */ + vs->vframe_in[vs->vframe_in_len/2] |= + ((*frame_data) & 15) << ((vs->vframe_in_len & 1) * 4); + + vs->vframe_in_len++; + frame_data++; + datalen--; + + if (vs->vframe_in_len == 20) { + v110_process_frame(vs); + vs->vframe_in_len = 0; + } + } +} + +void v110_input_frame_x1(struct v110_state *vs, struct ast_frame *f) +{ + int datalen = f->datalen; + unsigned char *frame_data = f->data.ptr; + + while (datalen) { + if (!vs->vframe_in_len) { + /* Find zero octet in buffer */ + if ( (*frame_data)) { + vs->vframe_in_len = 0; + frame_data++; + datalen--; + continue; + } + /* Found a suitable byte. Add it. */ + vs->vframe_in_len++; + memset(vs->vframe_in, 0, 10); + frame_data++; + datalen--; + continue; + } + /* Add byte to frame */ + vs->vframe_in[vs->vframe_in_len] = *frame_data; + + vs->vframe_in_len++; + frame_data++; + datalen--; + + if (vs->vframe_in_len == 10) { + v110_process_frame(vs); + vs->vframe_in_len = 0; + } + } +} + +/* Some bitmasks to ease calculation. */ +static unsigned char helper1[] = { 0x81, 0x81, 0x81, 0xc1, 0xe1, 0xf1, 0xf9, 0xfd, 0xff }; +static unsigned char helper2[] = { 0x81, 0x83, 0x87, 0x8f, 0x9f, 0xbf }; + +unsigned char v110_getline(struct v110_state *vs); +unsigned char v110_getline(struct v110_state *vs) +{ + unsigned char octet; + int line = vs->nextoline++; + int place = 2; + + if (line == 10) { + vs->nextoline = 1; + return 0x00; /* Header */ + } else if (line == 5) { + return 0xfd; /* E-bits. 10111111 (reversed) */ + } else if (line == 2 || line == 7) { + octet = 0x7f | vs->rts; + } else { + octet = 0x7f | vs->sbit; + } + + /* If we're already sending a byte, finish it */ + if (vs->nextobit) { + unsigned char tmp; + + /* Shift the data byte so that the bit we want is in bit 1 */ + tmp = vs->obuf[vs->obufstart] >> (vs->nextobit - 2); + + /* Mask in the bits we don't want to touch and the stop bit */ + tmp |= helper1[vs->nextobit - 1]; + + /* Clear bits in the generated octet to match */ + octet &= tmp; + + if (vs->nextobit < 4) { + /* There's some of this byte left; possibly just the stop bit */ + vs->nextobit += 6; + return octet; + } + + /* We've finished this byte */ + vs->obufstart++; + if (vs->obufstart == OBUF_LEN) + vs->obufstart = 0; + + if (vs->nextobit < 5) { + /* But there's still no room in this octet for any more */ + vs->nextobit = 0; + return octet; + } + /* Work out where to put the next data byte */ + place = 12 - vs->nextobit; + vs->nextobit = 0; + } else { + /* Nothing to follow; start bit of new byte at bit 1 */ + place = 2; + } + + /* Honour flow control when starting new characters */ + if (vs->cts || vs->obufstart == vs->obufend) + return octet; + + /* 'place' is the location within the octet to start the new + data byte. It'll be 2 unless we've already got the tail of + a previous data byte in this octet. If you're starting at it + and think there's an off-by-one error, remember the start bit + which is zero, and in bit (place-1). */ + octet &= (vs->obuf[vs->obufstart] << place) | helper2[place-2]; + vs->nextobit = 8 - place; + + return octet; +} + +void v110_fill_outframe_x4(struct v110_state *vs, int datalen) +{ + unsigned char *pos = vs->f.data.ptr; + { + FILE *fp=fopen("/tmp/out","a"); + + int i; + for (i=0;if.datalen = vs->f.samples = datalen; + + while (datalen) { + unsigned char tmp = v110_getline(vs); + pos[0] = 0xfc | (tmp & 3); + tmp >>= 2; + pos[1] = 0xfc | (tmp & 3); + tmp >>= 2; + pos[2] = 0xfc | (tmp & 3); + tmp >>= 2; + pos[3] = 0xfc | tmp; + pos += 4; + datalen -= 4; + } +} + +void v110_fill_outframe_x2(struct v110_state *vs, int datalen) +{ + unsigned char *pos = vs->f.data.ptr; + + if (datalen & 1) + vs->f.datalen = datalen = datalen + 1; + + vs->f.datalen = vs->f.samples = datalen; + + while (datalen) { + unsigned char tmp = v110_getline(vs); + pos[0] = 0xf0 | (tmp & 15); + tmp >>= 4; + pos[1] = 0xf0 | tmp; + pos += 2; + datalen -= 2; + } +} + +void v110_fill_outframe_x1(struct v110_state *vs, int datalen) +{ + unsigned char *pos = vs->f.data.ptr; + + vs->f.datalen = vs->f.samples = datalen; + + while (datalen) { + *pos = v110_getline(vs); + pos++; + datalen--; + } +} + +int launch_custom_app(const char *appname, char *appargs[], int *readfd, int *writefd) +{ + int pid, readpipe[2], writepipe[2], res; + + if (pipe(readpipe)) { + ast_log(LOG_WARNING, "Unable to create application data pipe: %s\n", strerror(errno)); + return -1; + } + + if (pipe(writepipe)) { + ast_log(LOG_WARNING, "Unable to create application data pipe: %s\n",strerror(errno)); + close(readpipe[0]); + close(readpipe[1]); + return -1; + } + + res = fcntl(readpipe[0], F_GETFL); + if (res > -1) { + res = fcntl(readpipe[0], F_SETFL, res | O_NONBLOCK); + } + if (res < 0) { + ast_log(LOG_WARNING, "unable to set application read pipe flags: %s\n", strerror(errno)); + close(readpipe[0]); + close(readpipe[1]); + close(writepipe[0]); + close(writepipe[1]); + return -1; + } + + res = fcntl(writepipe[1], F_GETFL); + if (res > -1) { + res = fcntl(writepipe[1], F_SETFL, res | O_NONBLOCK); + } + if (res < 0) { + ast_log(LOG_WARNING, "unable to set application write pipe flags: %s\n", strerror(errno)); + close(readpipe[0]); + close(readpipe[1]); + close(writepipe[0]); + close(writepipe[1]); + return -1; + } + + if ((pid = ast_safe_fork(1)) < 0) { + ast_log(LOG_WARNING, "Failed to fork() to launch custom application: %s\n", strerror(errno)); + close(readpipe[0]); + close(readpipe[1]); + close(writepipe[0]); + close(writepipe[1]); + return -1; + } + + if (!pid) { + ast_set_priority(0); + + /* Redirect stdin and out, provide enhanced audio channel if desired */ + dup2(writepipe[0], STDIN_FILENO); + dup2(readpipe[1], STDOUT_FILENO); + + /* Close everything but stdin/out/error */ + ast_close_fds_above_n(STDERR_FILENO + 1); + + execv(appname, appargs); + + /* Can't use ast_log since FD's are closed */ + ast_child_verbose(1, "Failed to execute custom app %s: %s", appname, strerror(errno)); + _exit(1); + } + ast_verb(3, "Launched custom application %s\n", appname); + *readfd = readpipe[0]; + *writefd = writepipe[1]; + return pid; +} + +static int load_module(void) +{ + return ast_register_application(app, echo_v110, synopsis, descrip) ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return ast_unregister_application(app); +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "V.110 dialin"); +