#include <stdio.h>
#include <string.h>
#include "bme.h"
#include "bme_app.h"
#include "mem.h"
#include "include/vdsl_ch.h"
#include "include/xdsl_fmc.h"
#include "include/bme_dma.h"
#include "include/precomp_vals_flex_up.h"
#include "include/precomp_vals_flex_down.h"

#include "hostType.h"

extern char capTest;

void tx_set_upb(u16 *tx_upb, int length)
{
	int i;
	
	for(i=0;i<length;i++) 
		tx_upb[i]=0x7fff;
}

void tx_set_precmp(u16 *tx_precmp, int length, LOCATION loc, BAND_PLANS bandplan)
{
	int i;
	
	for(i=0;i<length;i++) {
		tx_precmp[i]= 0x7fff;
	}
}

void tx_set_gi(u16 *tx_gi, int length)
{
	int i;
	
	for(i=0;i<length;i++) {
		tx_gi[i]= 512;
	}
}

void tx_chg_pilot_tone(int ptone_index) 
{
	int val; 

	val = readPIO_SingleRegChannel((int *)PTONE_2048T, 0);
	val = (val & 0xffff0000) | (ptone_index & 0x0ffff);
	writePIO_SingleRegChannel((int *)PTONE_2048T, 0, val);
}

void turn_off_ptone_scr()
{
	writePIO_short((short *)(ADDRESS_FRAMER_DCACHE_PIO + 0x024c + 6), 0x46);
}

void turn_on_ptone_scr()
{
	writePIO_short((short *)(ADDRESS_FRAMER_DCACHE_PIO + 0x024c + 6), 0x846);
}


void tx_set_lcs_lcp(u8 channel, u16 tx_prefix_len, u16 tx_suffix_len, u16 tx_beta_len)
{
	int     win_on;
	int     pkt_pfx_word; 
	int		is_ext_suffix;


	is_ext_suffix = (tx_suffix_len +tx_beta_len>0)? 1:0;
   	writePIO_SingleRegChannel((int *)PFXT_A, channel, (6<<27) + ((tx_suffix_len+tx_beta_len)<<15) + ((tx_prefix_len)<<3) + (is_ext_suffix<<2));
	win_on = (tx_beta_len>0? 1:0);
	writePIO_SingleRegChannel((int *)PFXT_B, channel, ((tx_suffix_len)<<13) + ((tx_prefix_len - tx_beta_len)<<1) + win_on);
	writePIO_SingleRegChannel((int *)PFXT_C, channel, 128*channel);
    if (!capTest) {
        logMsg("\n tx Lce win setting Lcp = %d, Lcs = %d, beta = %d", tx_prefix_len, tx_suffix_len+tx_beta_len, tx_beta_len);    
    }
    if(tx_prefix_len+tx_suffix_len-tx_beta_len>0) { 
		pkt_pfx_word = ((channel << 8) | 0x04);
        writePIO_short((short *)(ADDRESS_FRAMER_DCACHE_PIO + 0x024c + 4), pkt_pfx_word);
    } else { 
		pkt_pfx_word = ((channel << 8) | 0x00);
        writePIO_short((short *)(ADDRESS_FRAMER_DCACHE_PIO + 0x024c + 4), pkt_pfx_word);
    } 
}

int is_not_in_band(int bin_index, int num_band, int *band_start, int *band_stop)
{
	int i;
	
	for(i=0;i<num_band;i++)
		if(bin_index>=band_start[i] && bin_index<=band_stop[i])
			return(0);

	return(1);			
}

void write_XF_Fifo(int ch_num, u8 *xdata, int len)
// writes 'len' bytes from xdata to X-Fast-Fifo of given ch_num.
// written by doing DMA in memory mode, starting from start of Xfifo.
// NOTE: assumes each ch uses 4 KB and that ch number N starts at offset 
// 	 of N*4096 bytes from start of X-Fast-Fifo. 
{
    if (host_mode_flag) {
		hostWriteBMEMemories((u8 *)(ADDRESS_XFIFO_FAST+ch_num*len), xdata,  len, 1, 0, 0);
    } else {
		writeBMEMemories_diag (xdata, (u8 *)(ADDRESS_XFIFO_FAST+ch_num*len), 0x0, len, 0,  DMA_BUSY_POLLING, DMA_FOR_INIT_TX, 
				DMA_ECR_SW_INIT_DMA);
    }
}

void send_tx_bitTable(u16 *tx_tone_bit_table, int len, int chan_num)
{
   
	if(host_mode_flag) {
	    hostWriteBMEMemories((u8 *)(ADDRESS_MAPPERTONEORDER+chan_num*len*sizeof(u16)), (u8 *)tx_tone_bit_table, len*sizeof(u16), 1, 0, chan_num);
		hostWriteBMEMemories((u8 *)(ADDRESS_MAPPERTONEORDER), (u8 *)tx_tone_bit_table, len*sizeof(u16), 1, 0, 0);
    } else {
	    writeBMEMemories_diag ((u8 *)tx_tone_bit_table, (u8 *)ADDRESS_MAPPERTONEORDER, 0x0, len*sizeof(u16), chan_num, 
						DMA_BUSY_POLLING, DMA_FOR_INIT_TX, DMA_ECR_SW_INIT_DMA);
    }		
}


void send_tx_gtable_init(u16 *gtable, int len)
{
	if(host_mode_flag) {
    	hostWriteBMEMemories((u8 *)(ADDRESS_GAINTABLE + len*2*(NUM_VDSL_CHANNELS+1)), (u8 *)gtable, len*sizeof(u16), 1, 0, 0);
   	}  else	{
		writeBMEMemories_diag ((u8 *)gtable, (u8 *)ADDRESS_GAINTABLE, len*2*(NUM_VDSL_CHANNELS+1), len*sizeof(u16), 0x0, 
   				DMA_BUSY_POLLING, DMA_FOR_INIT_TX, DMA_ECR_SW_INIT_DMA);
	}
}

void send_tx_gtable(u16 *gtable, int len, int chan_num)
{

    if(host_mode_flag) {
         hostWriteBMEMemories((u8 *)(ADDRESS_GAINTABLE + len*sizeof(u16)*chan_num), (u8 *)gtable, len*sizeof(u16), 1, 0, chan_num);
         // a hack here
         hostWriteBMEMemories((u8 *)(ADDRESS_GAINTABLE), (u8 *)gtable, len*sizeof(u16), 1, 0, chan_num);
    } else {
        writeBMEMemories_diag ((u8 *)gtable, (u8 *)ADDRESS_GAINTABLE, 0, len*sizeof(u16), chan_num, DMA_BUSY_POLLING, DMA_FOR_INIT_TX, DMA_ECR_SW_INIT_DMA);					
    }
}

void gen_bit_tone_table(u16 *tone_table, u8 *bit_table, u16 numTones)
{
	u16 i=0;			/*index of the bins*/
	u16 k=0;
	u16 nobits;			/*Number of bits to send per tone*/

	for (nobits=0; nobits<16; nobits=nobits+1)
	{
		for (i=0; i<numTones; i=i+1)
		{
			if (bit_table[i]==nobits)
			{
				tone_table[k] = (i<<4)|nobits;
				k=(k+1)%numTones;
			}

		}
	}
}

void tx_init_table(XDSL_LINE *curr_line)
{
	int		i, j, k;
	int		len;                             
		
	len = curr_line->tx_fft_size>>1;
			
	if(curr_line->curr_tx_state == OP_TRAINING && curr_line->curr_tx_symbol_num == 0) {
		for(i=0;i<len;i++) {
			curr_line->tx_bit_table[i]=0x0;
		}
		
		for(i=0;i<curr_line->num_tx_bands;i++) 
			for(j=curr_line->tx_band_start[i];j<=curr_line->tx_band_stop[i];j++)
				curr_line->tx_bit_table[j]=2;

		train_up_gain_correction(curr_line->tx_gain_table, curr_line->tx_precmp, curr_line->tx_bit_table, 
							  curr_line->tx_gi, curr_line->tx_upb, len, 0, curr_line->tx_pilot_tone);
		j = 0;
		for(i=0;i<len;i++)		
			if(is_not_in_band(i, curr_line->num_tx_bands, curr_line->tx_band_start, curr_line->tx_band_stop))
				 curr_line->tx_tone_bit_table[j++] = (i<<4)|0x0;
				 
		for(i=0;i<curr_line->num_tx_bands;i++) 
			for(k=curr_line->tx_band_start[i];k<=curr_line->tx_band_stop[i];k++) 
				curr_line->tx_tone_bit_table[j++] = (k<<4)|2;
		if(curr_line->tx_pilot_tone<len)
			curr_line->tx_gain_table[curr_line->tx_pilot_tone]<<=1;
		send_tx_bitTable(curr_line->tx_tone_bit_table, len, 0);
		send_tx_gtable_init(curr_line->tx_gain_table, len);	
		send_tx_gtable(curr_line->tx_gain_table, len, 0);
	}  else	if(curr_line->curr_tx_state == TX_SHOWTIME_INIT)
	{
		memset((u8 *)curr_line->tx_tone_bit_table, 0x0, len*sizeof(u16));
		train_up_gain_correction(curr_line->tx_gain_table, curr_line->tx_precmp, curr_line->tx_bit_table, 
							  curr_line->tx_gi, curr_line->tx_upb, len, 1, curr_line->tx_pilot_tone);	
		if(curr_line->tx_pilot_tone<len)
			curr_line->tx_gain_table[curr_line->tx_pilot_tone]<<=1;
		gen_bit_tone_table(curr_line->tx_tone_bit_table,  curr_line->tx_bit_table, len);	  
		send_tx_bitTable(curr_line->tx_tone_bit_table, len, 1);
		send_tx_gtable(curr_line->tx_gain_table, len, 1);
	}	
}

void get_next_tx_state(XDSL_LINE *curr_line)
{
	if(curr_line->curr_tx_symbol_num >= curr_line->tx_showtime_start && curr_line->curr_tx_state == OP_TRAINING)
		curr_line->curr_tx_state = TX_SHOWTIME_INIT;
	else if(curr_line->curr_tx_state == TX_SHOWTIME_INIT)
	{
		curr_line->curr_tx_state = TX_SHOWTIME;
	}	
	
	curr_line->curr_tx_symbol_num++;
}
 

void do_tx(XDSL_LINE *curr_line)
{
	FILE	*fp;
	
	if(!curr_line->is_tx_enabled)
		return;
		
	if(curr_line->curr_tx_state == OP_TRAINING && !curr_line->is_op_training_started) {
		// at frm 1, turn on vbus
		// at frm=imm,  go to op-train state with reset frm num
		framerCpu(curr_line->ch_num, START_OP_TRAINING, 0, 0);
		curr_line->is_op_training_started = 1;
	} else if(curr_line->curr_tx_state == MED_TRAINING)
	{
		if(!curr_line->is_medley_training_started) {
	   		logMsg("\n tx sends medley training data");
			// send framer cmd here
			framerCpu(curr_line->ch_num, START_OP_MEDLEY, 0, 0);
			fp = fopen(curr_line->tx_medley_start_done_file_sem, "w");
			fprintf(fp, "tx goes into medley data transmitting mode");
			fclose(fp);		   	
			curr_line->is_medley_training_started = 1;
		}
	}
}

// start tx with offset to synchronize tx & rx symbol boundaries
void adjust_zbuf_wr(XDSL_LINE *curr_line)
{
	int	zbuf_wr_adjustment;
	int	dmt_size;

	// set zfifo size to 64KB 
	// writePIO_SingleRegChannel((int *) (ZFIFO_CZFT_A),  curr_line->ch_num, ((0/128)<<7) + (9<<3) + 1);		 // 32 k per channel
	
	dmt_size = curr_line->tx_fft_size + curr_line->tx_prefix_len + curr_line->tx_suffix_len - curr_line->tx_beta_len;
	zbuf_wr_adjustment = curr_line->tot_symb_delay%dmt_size; // curr_line->symb_status.tot_delay_adj%dmt_size;
	zbuf_wr_adjustment = curr_line->zbuf_offset + (2*zbuf_wr_adjustment);
	while(zbuf_wr_adjustment<0)
		zbuf_wr_adjustment+=(2*dmt_size);
	zbuf_wr_adjustment = zbuf_wr_adjustment	% (2*dmt_size);
	logMsg("\n total zbuf adj = %d", zbuf_wr_adjustment);
	set_ZFIFO_wrt_ptr(zbuf_wr_adjustment, curr_line->ch_num);            
}



