/*
 * Asterisk -- An open source telephony toolkit.
 *
 * Copyright (C) 2009, David Woddhouse
 *
 * David Woodhouse <dwmw2@infradead.org> 
 * Generic app execution and other stuff by Moises Silva <moises.silva@gmail.com> 
 *
 * 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 <dwmw2@infradead.org> \endverbatim
 * 
 * \ingroup applications
 */

/*** MODULEINFO
	<defaultenabled>no</defaultenabled>
 ***/

#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 <sys/types.h>
#include <sys/wait.h>

#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;i<f->datalen;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;i<datalen;i++) {
			fprintf(fp,"%x ",pos[i]);
		}
		fprintf(fp, "%s", "\n");
		fclose(fp);
	}


	if (datalen & 3)
		datalen = (datalen + 3) & ~3;

	vs->f.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");

