/*
 * G.711.c
 */

/* 
   All char references need to be altered as sizeof(char)==16bits on DSP
   for the time being, just return a 16bit variable containing the 8bit data in the
   bottom half.
   Eventually this should be changed, either so that the 8bit values are packed, 
   or so that the processing stage takes 2x16bit inputs and returns the 2x8bit in
   a 16bit variable 
*/

#include <std.h>
#include "mailbox.h"
#include "tokliBIOS.h"

#define BK_SIZE 256 // this needs to be exactly divisible by two
#define HALF_BK_SIZE 128


unsigned char newlinear2ulaw(short int);



struct g711_udata {
       short int *rbuf;
       unsigned char *wbuf;
};


#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
#define	NSEGS		(8)		/* Number of A-law segments. */
#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
#define	SEG_MASK	(0x70)		/* Segment field mask. */

static short int seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
			    0x3FF, 0x7FF, 0xFFF, 0x1FFF};

/* copy from CCITT G.711 specifications */
unsigned char _u2a[128] = {			/* u- to A-law conversions */
	1,	1,	2,	2,	3,	3,	4,	4,
	5,	5,	6,	6,	7,	7,	8,	8,
	9,	10,	11,	12,	13,	14,	15,	16,
	17,	18,	19,	20,	21,	22,	23,	24,
	25,	27,	29,	31,	33,	34,	35,	36,
	37,	38,	39,	40,	41,	42,	43,	44,
	46,	48,	49,	50,	51,	52,	53,	54,
	55,	56,	57,	58,	59,	60,	61,	62,
	64,	65,	66,	67,	68,	69,	70,	71,
	72,	73,	74,	75,	76,	77,	78,	79,
	80,	82,	83,	84,	85,	86,	87,	88,
	89,	90,	91,	92,	93,	94,	95,	96,
	97,	98,	99,	100,	101,	102,	103,	104,
	105,	106,	107,	108,	109,	110,	111,	112,
	113,	114,	115,	116,	117,	118,	119,	120,
	121,	122,	123,	124,	125,	126,	127,	128};

unsigned char _a2u[128] = {			/* A- to u-law conversions */
	1,	3,	5,	7,	9,	11,	13,	15,
	16,	17,	18,	19,	20,	21,	22,	23,
	24,	25,	26,	27,	28,	29,	30,	31,
	32,	32,	33,	33,	34,	34,	35,	35,
	36,	37,	38,	39,	40,	41,	42,	43,
	44,	45,	46,	47,	48,	48,	49,	49,
	50,	51,	52,	53,	54,	55,	56,	57,
	58,	59,	60,	61,	62,	63,	64,	64,
	65,	66,	67,	68,	69,	70,	71,	72,
	73,	74,	75,	76,	77,	78,	79,	80,
	80,	81,	82,	83,	84,	85,	86,	87,
	88,	89,	90,	91,	92,	93,	94,	95,
	96,	97,	98,	99,	100,	101,	102,	103,
	104,	105,	106,	107,	108,	109,	110,	111,
	112,	113,	114,	115,	116,	117,	118,	119,
	120,	121,	122,	123,	124,	125,	126,	127};




static Uns rcv_bksnd(struct dsptask *task, Uns count)
{
       struct g711_udata *udata = task->udata;
       short int *input_val;
       unsigned char *output_val; // use 16bit for the time being, should only use the bottom 8 bits of this variable though
       unsigned char temp1, temp2;
       short int count_shorts, i;

       /* Too much data sent across */
       if (count > BK_SIZE*sizeof(short int))
              return MBCMD_EID_BADCNT;

       input_val = (short int *) &(udata->rbuf[0]);
       output_val = (unsigned char *) &(udata->wbuf[0]);

       count_shorts = count/2; /* integer divide -> we combine the outputs to simulate an 8bit array */

       /* Process the data - only an even number of input shorts (so the output can be constructed without zero padding) */


       for(i=0; i<count; i++) {
           /* Currently we output a 16bit unsigned char, can I be crafty and get the code to 
              write the bottom 8bits over the top of the last entry's top 8bits so we get
              to return 8bit chars to the ARM side? */

           if(i%2==0) 
           {
               temp1 = newlinear2ulaw(*input_val);
           }
           else
           {
               temp2 = newlinear2ulaw(*input_val);
               *output_val = (temp1 & 0xFF) | ((temp2 & 0xFF) << 8);
               output_val++;
           }
           //*output_val = newlinear2ulaw(*input_val);

           input_val++;


           /**output_val = newlinear2ulaw(*input_val);
           input_val++;
           output_val++;*/
       }

       /* Send the output buffer */
       bksndp(task, udata->wbuf, count_shorts); /* As we've written chars after reading shorts */

       /* Request a new input buffer */
       bkreqp(task, udata->rbuf, BK_SIZE*sizeof(short int));
       return 0;
}



static Uns rcv_tctl(struct dsptask *task, Uns ctlcmd, Uns *ret, Uns arg)
{
        struct g711_udata *udata = task->udata;
	switch (ctlcmd) {
		case MBCMD_TCTL_TINIT:
                    memset(udata->rbuf, 0xbeef, BK_SIZE*sizeof(short int));
                    memset(udata->wbuf, 0xdead, HALF_BK_SIZE*sizeof(unsigned short int));
                    bkreqp(task, udata->rbuf, BK_SIZE*sizeof(short int));
                    return 0;
		case MBCMD_TCTL_TCLR:
		case MBCMD_TCTL_TKILL:
			/*
			 * cleanup code here
			 */
			return 0;
		default:
			return MBCMD_EID_BADTCTL;
	}
}


static short search(short int val, short int *table, short int size)
{
	short int		i;

	for (i = 0; i < size; i++) 
	{
		if (val <= *table++)
			return (i);
	}
	return (size);
}

#define	BIAS		(0x84)		/* Bias for linear code. */
#define CLIP            8159

/*
 * linear2ulaw() - Convert a linear PCM value to u-law
 *
 * In order to simplify the encoding process, the original linear magnitude
 * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
 * (33 - 8191). The result can be seen in the following encoding table:
 *
 *	Biased Linear Input Code	Compressed Code
 *	------------------------	---------------
 *	00000001wxyza			000wxyz
 *	0000001wxyzab			001wxyz
 *	000001wxyzabc			010wxyz
 *	00001wxyzabcd			011wxyz
 *	0001wxyzabcde			100wxyz
 *	001wxyzabcdef			101wxyz
 *	01wxyzabcdefg			110wxyz
 *	1wxyzabcdefgh			111wxyz
 *
 * Each biased linear code has a leading 1 which identifies the segment
 * number. The value of the segment number is equal to 7 minus the number
 * of leading 0's. The quantization interval is directly available as the
 * four bits wxyz.  * The trailing bits (a - h) are ignored.
 *
 * Ordinarily the complement of the resulting code word is used for
 * transmission, and so the code word is complemented before it is returned.
 *
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 * John Wiley & Sons, pps 98-111 and 472-476.
 */
unsigned char newlinear2ulaw(short int pcm_val)	/* 2's complement (16-bit range) */
{
	short int		mask;
	short int		seg;
	unsigned char		uval;

	/* Get the sign and the magnitude of the value. */
	pcm_val = pcm_val >> 2;
	if (pcm_val < 0) {
		pcm_val = -pcm_val;
		mask = 0x7F;
	} else {
		mask = 0xFF;
	}
        if ( pcm_val > CLIP ) pcm_val = CLIP;		/* clip the magnitude */
	pcm_val += (BIAS >> 2);

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_uend, 8);

	/*
	 * Combine the sign, segment, quantization bits;
	 * and complement the code word.
	 */
	if (seg >= 8)		/* out of range, return maximum value. */
		return (unsigned char) (0x7F ^ mask);
	else {
		uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
		return (uval ^ mask);
	}

}

#pragma DATA_SECTION(g711_rbuf_data, "ipbuf");
#pragma DATA_SECTION(g711_wbuf_data, "ipbuf");
static short int g711_rbuf_data[BK_SIZE];
static unsigned char g711_wbuf_data[HALF_BK_SIZE];
static struct g711_udata udata = { g711_rbuf_data, g711_wbuf_data };

#pragma DATA_SECTION(task_g711_ulaw, "dspgw_task")
struct dsptask task_g711_ulaw = {
	TID_MAGIC,	/* tid */
	"g711_ulaw",	/* name */
	MBCMD_TTYP_PVDM | MBCMD_TTYP_PVMD |
	MBCMD_TTYP_BKDM | MBCMD_TTYP_BKMD |
	MBCMD_TTYP_ASND | MBCMD_TTYP_ARCV,
			/* ttyp: active block snd, active block rcv */
	rcv_bksnd,	/* rcv_snd */
	NULL,		/* rcv_req */
	rcv_tctl,	/* rcv_tctl */
	NULL,		/* tsk_attrs */
	NULL,		/* mmap_info */
	&udata		/* udata */
};

