/*
 * Gnophone: A client for the Asterisk PBX
 *
 * Copyright (C) 2000, Linux Support Services, Inc.
 *
 * Written by Mark Spencer
 *
 * Linux/UNIX version distributed under the terms of
 * the GNU General Public License
 *
 * audio-phone.c: Linux Telephony Driver
 *
 */

/*
 * We can optionally add additional enhancements that
 * are Quicknet specific
 */

#define IXJ_ENHANCED
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/telephony.h>
#ifdef IXJ_ENHANCED
#include <linux/ixjuser.h>
#endif
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include "phonecore.h"
#include "audio.h"
#include <iax/iax-client.h>
#include "dialtone.h"
#include "phonecore.h"

#define MAX_DEVS 4

/* 
 * Fragment size is of the form 2^x.  With 160 byte GSM frames, we can 
 * only use 64-byte fragments, which are very small and create fairly
 * substantial overhead.  at some point we might want to use larger 
 * fragments
 * 
 */

#define FRAG_SIZE  6

/*
 * The InternetPhonejack can't take but 1 buffer with signed linear.  POS.
 * We use our own "circular queue" of buffers to prevent overruns, which incidently
 * cause the machine to completely freeze for a few ms.
 */

#define BUFFERS    20

/* Again, the Internet PhoneJack can't handle anything except EXACTLY 480 byte packets */
#define BUFFERLEN 480

static int phone_open(struct audio_channel *c);
static int phone_close(struct audio_channel *c);
static int phone_activate(struct audio_channel *c);
static int phone_deactivate(struct audio_channel *c);
static int phone_configure(struct audio_channel *c);
static int phone_setspeed(struct audio_channel *c, int speed);
static int phone_sendaudio(struct audio_channel *c, int format, void *data, int len);
static int phone_readaudio(struct audio_channel *c, int format, void *buffer, int *len);
static int phone_flush(struct audio_channel *c);

static char phone_driver[] = "Gnophone/phone";

#define STATE_NONE    0
#define STATE_DSP     1
#define STATE_RINGING 3
#define STATE_BUSY    5

#define STATE_RING    8

struct phone_buffer {
	int full;
	char buf[BUFFERLEN];
};

struct phone_pvt {
	/* Desired buffser size */
	int frags;
	/* IXJ specific: Are we using pots or what? */

	int port;
	/* State */
	int state;
	/* Real fd, used for writing and pre-reading */
	int writefd;
	/* Filename of phone device */
	char fn[80];
	/* A bunch of buffers */
	struct phone_buffer bufs[BUFFERS];
	/* Where in our circle we're writing to */
	int inbuf;
	/* Where in our circle we're reading from */
	int outbuf;
	/* How far into our current "in" buffer we've filled */
	int buflen;

	/* Buffer playback id */
	int *bpid;

	/* In order to work around the idiocy of only
	   being able to read exactly 480 bytes, we have to 
	   buffer the voice. */
	int fds[2];
	/* GDK input id */
	int *id;
	/* Dialtone id */
	int *dtid;
	/* Dial tone position */
	int dtpos;
};

static char *phone_card_is_good(int fd, int silent)
{
	/* Perform checks on the sound card to be sure it will work for us */

	phone_codec codec = LINEAR16;
	

	if (ioctl(fd, PHONE_PLAY_CODEC, codec)) {
		return "Device does not support 16-bit signed linear samples on playback";
	}

	if (ioctl(fd, PHONE_REC_CODEC, codec)) {
		return "Device does not support 16-bit signed linear samples on playback";
	}

#ifdef IXJ_ENHANCED
	ioctl(fd, IXJCTL_AEC_START, AEC_MED);
#endif
	
	return NULL;
	
}

static int phone_close(struct audio_channel *c)
{
	struct phone_pvt *pvt = c->pvt;
	if (pvt->id)
		pc_io_remove(pvt->id);
	if (pvt->dtid)
		pc_io_remove(pvt->dtid);
	if (pvt->bpid)
		pc_io_remove(pvt->bpid);

	pvt->id = NULL;
	pvt->dtid  = NULL;
	pvt->bpid = NULL;
	bzero(pvt->bufs, sizeof(pvt->bufs));
	
	ioctl(pvt->writefd, PHONE_REC_STOP);
	ioctl(pvt->writefd, PHONE_PLAY_STOP);
	close(pvt->writefd);
	close(pvt->fds[0]);
	close(pvt->fds[1]);
	c->fd = -1;
	return 0;
}

static int phone_ringing(struct audio_channel *c)
{
	struct phone_pvt *pvt = c->pvt;
	pvt->state = STATE_RINGING;
	if (ioctl(pvt->writefd, PHONE_RINGBACK)) {
		fprintf(stderr, "Couldn't make the phone sound like it was ringing\n");
		return -1;
	}
	if (!ioctl(pvt->writefd, PHONE_HOOKSTATE)) {
		pvt->state |= STATE_RING;
		ioctl(pvt->writefd, PHONE_RING_START);
	}
	return 0;
}

static char *get_dev_name(int fd)
{
	static char devname[80];
	int capcount, x;
	struct phone_capability *caps;
	
	capcount = ioctl(fd, PHONE_CAPABILITIES);
	if (capcount < 1) {
		fprintf(stderr, "Unable to get capability count\n");
		return "Telephone";
	}
	caps = malloc(sizeof(struct phone_capability) * capcount);
	if (!caps) {
		fprintf(stderr, "Unable to allocate memory for %d capabilities\n", capcount);
		return "Telephone";
	}
	memset(caps, 0, sizeof(struct phone_capability) * capcount);
	if (ioctl(fd, PHONE_CAPABILITIES_LIST, caps) < 0) {
		fprintf(stderr, "Unable to list capabilities.\n");
		free(caps);
		return "Telephone";
	}
	for (x=0;x<capcount;x++) 
		if (caps[x].captype == device) {
			strncpy(devname, caps[x].desc, sizeof(devname));
			free(caps);
			return devname;
		}
	fprintf(stderr, "Didn't find a device name\n");
	return "Telephone";
}

static int phone_ring(struct audio_channel *c)
{
	struct phone_pvt *pvt = c->pvt;
	pvt->state = STATE_RING;
	if (ioctl(pvt->writefd, PHONE_RING_START)) {
		fprintf(stderr, "Couldn't make the phone ring\n");
		return -1;
	}
	return 0;
}

static int phone_play_digit(struct audio_channel *c, char digit)
{
	struct phone_pvt *pvt = c->pvt;
	if (ioctl(pvt->writefd, PHONE_PLAY_TONE, digit)) {
		fprintf(stderr, "Couldn't make the phone play digit '%c'\n", digit);
		return -1;
	}
	return 0;
}

static int phone_busy(struct audio_channel *c)
{
	struct phone_pvt *pvt = c->pvt;
	pvt->state = STATE_BUSY;
	if (ioctl(pvt->writefd, PHONE_BUSY)) {
		fprintf(stderr, "Couldn't make the phone sound like it was busy\n");
		return -1;
	}
	return 0;
}

static int dialtone_play(int *id, int fd, short events, void *av)
{
	struct audio_channel *ac = av;
	struct phone_pvt *pvt = ac->pvt;
	int res;
	/* When we can write to it, write a frame */
	res = write(pvt->writefd, ((void *)dialtone) + pvt->dtpos, BUFFERLEN);
	if (res < 0) {
		fprintf(stderr, "%s Error writing frame to fd %d: %s\n",__PRETTY_FUNCTION__,pvt->writefd, strerror(errno));
	}
	pvt->dtpos += BUFFERLEN;
	if (pvt->dtpos >= sizeof(dialtone))
		pvt->dtpos = 0;
	return 0;
}

static int phone_data_available(int *id, int fd, short c, void *av)
{
	struct audio_channel *ac = av;
	struct phone_pvt *pvt = ac->pvt;
	union telephony_exception e;
	int res, res2;
	int hook;
	char buf[BUFFERLEN];
	char dtmf;
	if (c & AST_IO_PRI) {
		printf("Exception!\n");
		e.bytes = ioctl(pvt->writefd, PHONE_EXCEPTION);  
		if (e.bits.hookstate) {
			hook = ioctl(pvt->writefd, PHONE_HOOKSTATE);
			printf("Phone hook: %d\n", hook);
			if (hook) {
				phone_card_is_good(pvt->writefd, 0);
				ioctl(pvt->writefd, PHONE_PLAY_START);
				ioctl(pvt->writefd, PHONE_REC_START);
				pvt->dtpos = 0;
				if (pvt->state & STATE_RING) {
					ioctl(pvt->writefd, PHONE_RING_STOP);
					if (pvt->state & STATE_RINGING) {
						pvt->state = STATE_RINGING;
						if (ioctl(pvt->writefd, PHONE_RINGBACK)) {
							fprintf(stderr, "Couldn't make the phone sound like it was ringing\n");
							return 0;
						}
					} else {
						printf("Answering!\n");
						pvt->state = STATE_NONE;
						pc_offhook(SOURCE_PC, 0);
					}
				} else {
					pvt->dtid = pc_io_add(pvt->writefd, dialtone_play, AST_IO_OUT, ac);
				}
			} else {
				if (pvt->dtid)
					pc_io_remove(pvt->dtid);
				pvt->dtid = NULL;
				if (pvt->bpid)
					pc_io_remove(pvt->bpid);
				pvt->bpid = NULL;
				bzero(pvt->bufs, sizeof(pvt->bufs));
				ioctl(pvt->writefd, PHONE_PLAY_STOP);
				ioctl(pvt->writefd, PHONE_REC_STOP);
				pc_onhook(SOURCE_PC, 0);
			}
		}
		if (e.bits.dtmf_ready) {
			dtmf = ioctl(pvt->writefd, PHONE_GET_DTMF_ASCII);
			audio_digit_ready(dtmf);
			if (pvt->dtid)
				pc_io_remove(pvt->dtid);
			pvt->dtid = NULL;
		}
	}
	if (c & AST_IO_IN) {
		res =  read(pvt->writefd, buf, sizeof(buf));
		if (res < 0) {
			fprintf(stderr, "Error reading from phone: %s\n", strerror(errno));
			return 0;
		}
		res2 = write(pvt->fds[1], buf, res);
		if (res2 != res) 
			fprintf(stderr, "%s Error writing to pipe: %s\n",__PRETTY_FUNCTION__, strerror(errno));
	}
	return 0;
}

static struct audio_channel *phone_channel_new(int fd, char *filename)
{
	struct audio_channel *ac;
	struct phone_pvt *pvt = (struct phone_pvt *)malloc(sizeof(struct phone_pvt));
	ac = audio_new();
	pvt = (struct phone_pvt *)malloc(sizeof(struct phone_pvt));
	bzero(pvt, sizeof(struct phone_pvt));
	if (ac && pvt) {
		strncpy(pvt->fn, filename, sizeof(pvt->fn));
		pvt->frags = BUFFERS;
		pvt->id = NULL;
		pvt->dtid = NULL;
		pvt->bpid = NULL;
		snprintf(ac->name, sizeof(ac->name), "%s on %s", get_dev_name(fd), filename);
		ac->priority = 100;
		ac->driver = phone_driver;
		ac->open = phone_open;
		ac->close = phone_close;
		ac->play_digit = phone_play_digit;
		ac->activate = phone_activate;
		ac->ring = phone_ring;
		ac->busy = phone_busy;
		ac->hz = 8000;						/* We only support 8000 hz */
		ac->fastbusy = phone_busy;			/* XXX We need a fastbusy procedure XXX */
		ac->ringing = phone_ringing;
		ac->deactivate = phone_deactivate;
		ac->configure = phone_configure;
		ac->sendaudio = phone_sendaudio;
		ac->readaudio = phone_readaudio;
		/* ac->exception = NULL; */
		ac->flush = phone_flush;
		ac->setspeed = phone_setspeed;
		ac->sformats = AST_FORMAT_SLINEAR;
		ac->cananswer = 1;
		ac->duplex = 1;
		/* ac->simduplex = NULL */
		ac->fd = -1;
		ac->pvt = pvt;
		ac->echocancelled = 0;
		
	} else if (!pvt) { free(ac); ac = NULL; }
	return ac;
}

static int phone_is_off_hook(struct audio_channel *ac)
{
	struct phone_pvt *pvt = ac->pvt;
	return ioctl(pvt->writefd, PHONE_HOOKSTATE);
}

static int phone_open(struct audio_channel *ac)
{
	struct phone_pvt *pvt = ac->pvt;
	char *c;
	if (ac->fd > -1) {
		fprintf(stderr, "Channel %s already open?\n", ac->name);
		return 0;
	}
	if (pipe(pvt->fds)) {
		fprintf(stderr, "Unable to create pipe\n");
		return -1;
	}
	pvt->dtid = NULL;
	pvt->id = NULL;
	pvt->writefd = open(pvt->fn, O_RDWR | O_NONBLOCK, 0);
	if (pvt->writefd < 0) {
		fprintf(stderr, "Error opening %s: %s\n", pvt->fn, strerror(errno));
		return -1;
	}
	if ((c=phone_card_is_good(pvt->writefd, 0))) {
		fprintf(stderr, "Unable to set mode: %s\n", c);
		return -1;
	}
	pvt->id = pc_io_add(pvt->writefd, phone_data_available, AST_IO_IN | AST_IO_PRI, ac);
	ac->fd = pvt->fds[0];
	return 0;
}

static int phone_setspeed(struct audio_channel *ac, int speed)
{
	if (ac->hz != 8000) {
		fprintf(stderr, "We can only support 8000 hz (%s)\n", ac->name);
		return -1;
	}
	return 0;
}

static int phone_activate(struct audio_channel *ac)
{
	/* Nothing really necessary to activate us */
	return 0;		
}

static int phone_deactivate(struct audio_channel *ac)
{
	struct phone_pvt *pvt = ac->pvt;
	/* Turn off any DSP activity */
	if (pvt->state & STATE_DSP)
		ioctl(pvt->writefd, PHONE_CPT_STOP);
	if (pvt->state & STATE_RING)
		ioctl(pvt->writefd, PHONE_RING_STOP);
	return 0;
}

static int phone_configure(struct audio_channel *ac)
{
	/* XXX Bug: I ought to bring a configuration window so you can
	       set the frags and see info, etc XXX */
	return 0;
}

static int phone_readaudio(struct audio_channel *ac, int format, void *data, int *len)
{
	/* 
	 * *SIGH* The quicknet will only let us read exactly 480 bytes of 
	 * signed linear at a time.  Reading other values can cause 
	 * kernel panics on some kernels.
	 */
	 
	int res;
	char ign[BUFFERLEN];
	if (format != AST_FORMAT_SLINEAR) {
		fprintf(stderr, "Can only handle signed linear (little endian) data on %s\n", ac->name);
		*len = 0;
		return -1;
	}
	if (!phone_is_off_hook(ac)) {
		/* Don't return anything if we're not off hook */
		read(ac->fd, ign, sizeof(ign));
		*len = 0;
		return 0;
	}
	res = read(ac->fd, data, *len);
	if (res < 0) {
		fprintf(stderr, "Error reading from %s: %s\n", ac->name, strerror(errno));
		*len = 0;
		return -1;
	}
	*len = res;
	return 0;
}

inline static int next_buffer(int buffer)
{
	buffer++;
	if (buffer >= BUFFERS)
		buffer=0;
	return buffer;
}


#define MINBUFS 4

inline static int prev_buffer(int buffer, int howmuch)
{
	buffer -= howmuch;
	if (buffer < 0)
		buffer += BUFFERS;
	return buffer;
}

static int buffers = 0;

static int buffer_playback(int *id, int fd, short events, void *av)
{
	struct audio_channel *ac = av;
	struct phone_pvt *pvt = ac->pvt;
	if (pvt->bufs[pvt->outbuf].full) {

		/* We actually have a full frame to write, so write it */
		if (write(pvt->writefd, pvt->bufs[pvt->outbuf].buf, BUFFERLEN) < BUFFERLEN) {
			fprintf(stderr, "%s Error writing frame to fd %d: %s\n",__PRETTY_FUNCTION__,pvt->writefd, strerror(errno));
			return 0;
		}

		/* Mark the buffer free */
		pvt->bufs[pvt->outbuf].full = 0;

		/* Prepare to go to the next one */
		pvt->outbuf = next_buffer(pvt->outbuf);
		
		buffers--;
	} else {

		/* No more stuff, so just cancel our ID */
		pc_io_remove(pvt->bpid);
		pvt->bpid = NULL;
	}
	return 0;
}

static int start_buffer_playback(struct audio_channel *ac, struct phone_pvt *pvt, int bufnum)
{
	/* Make sure we're not playing back dialtone */
	if (pvt->dtid) {
		fprintf(stderr, "Notice: turning off dialtone\n");
		pc_io_remove(pvt->dtid);
		pvt->dtid = NULL;
	}

	/* Turn off any DSP activity */
	if (pvt->state & STATE_DSP)
		ioctl(pvt->writefd, PHONE_CPT_STOP);
	if (pvt->state & STATE_RING)
		ioctl(pvt->writefd, PHONE_RING_STOP);

	pvt->state = STATE_NONE;
	
	/* Start where we're supposed to */
	pvt->outbuf = bufnum;

	/* Add the input */
	pvt->bpid = pc_io_add(pvt->writefd, buffer_playback, AST_IO_OUT, ac);
	
	/* We're done.  It will almost immediately be called to send the first frame */
	return 0;
}

#if 0
static int time_from_last_write(void)
{
	static struct timeval last = { 0, 0};
	struct timeval tv;
	int ms;
	gettimeofday(&tv, NULL);
	if (last.tv_usec) {
		ms = (tv.tv_sec - last.tv_sec) * 1000 + (tv.tv_usec - last.tv_usec) / 1000;
	} else {
		ms = 0;
	}
	last = tv;
	return ms;
	
}
#endif

static int phone_sendaudio(struct audio_channel *ac, int format, void *data, int len)
{
	int amt;
	struct phone_pvt *pvt;
#if 0
	printf("Sending %d bytes, %d ms since last write, buffers=%d\n", len, time_from_last_write(), buffers);
#endif
	pvt = ac->pvt;
	if (format != AST_FORMAT_SLINEAR) {
		fprintf(stderr, "Can only handle signed linear (little endian) data on %s\n", ac->name);
		return -1;
	}
	if (!ioctl(pvt->writefd, PHONE_HOOKSTATE)) {
		/* We're on hook.  Ignore the request */
		return 0;
	}
	if (pvt->state & STATE_RINGING) {
		/* If we're supposed to be ringing, stop when we're not */
		if (pvt->state & STATE_DSP)
			ioctl(pvt->writefd, PHONE_CPT_STOP);
		pvt->state &= ~STATE_RINGING;
	}
	if (pvt->bufs[pvt->inbuf].full) {
		fprintf(stderr, "Dropping %d bytes, buffer full (buffers = %d)\n", len, buffers);
		return 0;
	}

	/* This code puts our sound into (not so) convenient 480 byte segments */
	while(len) {
		amt = len;
		if (amt > BUFFERLEN - pvt->buflen)
			amt = BUFFERLEN - pvt->buflen;
		/* Copy our data into our buffer */
		memcpy(pvt->bufs[pvt->inbuf].buf + pvt->buflen, data, amt);
		/* Adjust our recorded buffer size accordingly */
		pvt->buflen += amt;
		/* Subtract the length from the original */
		len -= amt;
		/* Move the input pointer accordingly */
		data += amt;
		if (pvt->buflen > BUFFERLEN) {
			fprintf(stderr, "Huh? It's out of bufferlen\n");
			pvt->buflen = 0;
			return -1;
		}
		if (pvt->buflen == BUFFERLEN) {
			/* We've filled our current buffer, mark this one in use. */
			pvt->buflen = 0;
			pvt->bufs[pvt->inbuf].full = 1;
			buffers++;

			/* ... and move to the next input buffer */
			pvt->inbuf = next_buffer(pvt->inbuf);

			/* We need to start the buffer playback if we have at least a couple of buffers full */
			if ((pvt->bpid < 0) && buffers >= MINBUFS)
				start_buffer_playback(ac, pvt, prev_buffer(pvt->inbuf, MINBUFS));

#if 0
			/* Note if we're overwriting our circular buffer. */
			if (pvt->bufs[pvt->inbuf].full) {
				fprintf(stderr, "Warning: Overwriting circular buffer (buffers = %d)\n", buffers);
				buffers --;
			}
#endif			
		}
	}
	return 0;
	
}

int init()
{
	char fn[80];
	int fd;
	int x;
	struct audio_channel *ac;
	struct stat statbuf;
	char *c;
	for (x=0;x<MAX_DEVS; x++) {
		if (!x && stat("/dev/phone0", &statbuf)) {
			/* Maybe some systems have only /dev/phone and not /dev/phone0 */
			strcpy(fn, "/dev/phone");
		} else
			sprintf(fn, "/dev/phone%d", x);
		fd = open(fn, O_RDWR | O_NONBLOCK, 0);
		if (fd >= 0) {
			if (!(c = phone_card_is_good(fd, 0))) {
				/* Candidate sound card */
				ac = phone_channel_new(fd, fn);
				close(fd);
				fd = -1;
				if (ac)
					audio_register_channel(ac);
			} else {
				fprintf(stderr, "Card %s is no good because %s\n", fn, c);
			}
			if (fd >= 0)
				close(fd);
		}
	}
	return 0;
}

char *key() 
{
	return KEY;
}

char *name()
{
	return "phone";
}

static int phone_flush(struct audio_channel *c)
{
	return 0;
}
