#include "xparameters.h"
#include "xbasic_types.h"
#include "xio.h"
#include "types.h"
#include "pcmu_l.h"
#include "phone.h"

static BYTE incr = 0;

BYTE packet[260];
BYTE audio_out[160];
BYTE tag[15];

char rtpport1;
char rtpport2;
short isRTP = 0;
int taglen;
unsigned int initPacketReceive(void);


#define BIAS 0x84   /* define the add-in bias for 16 bit samples */
#define CLIP 32635
short pkt_cntr = 0;
short audio_cntr = 0;
BYTE enable = 0;

void audio_int_handler(void * poop){  
  unsigned short linear_data;
  unsigned char ulaw;
  unsigned char packet_data;
  int sample_to_write;
  if(!enable){
    return;
  }else{
    microblaze_disable_interrupts();
    linear_data = XIo_In32(0xFEFE0000);
    ulaw = l16_to_pcmu(linear_data);
    
    audio_out[audio_cntr] = ulaw;
    if(audio_cntr < 159){
      audio_cntr++;
    }
    
    packet_data = packet[54 + 12 + pkt_cntr];

    if(pkt_cntr < 159){
      pkt_cntr++;
    }else{
      enable = 0;
    }
    
    sample_to_write = (pcmu_to_l16(packet_data) & 0xFFFF);
    
    //print("\r\n");
    //putnum(packet_data);
    //print(" > ");
    //putnum(sample_to_write);

    XIo_Out32(0xFEFE2222,(sample_to_write >> 2));
    microblaze_enable_interrupts();
    return;
    }
}

void sendSRAMPacket(int start, int end) {
  int length = end-start;
  unsigned int i,j,k,addr,word;
  
  outnic(CR,0x22);
  
  
  //arp 596, 596+60

  outnic(RSAR0, 0); // set DMA starting address
  outnic(RSAR1, TXSTART);

  outnic(ISR, 0xFF); // clear ISR

  
  outnic(RBCR0, length*2); // set Remote DMA Byte Count
  outnic(RBCR1, 0);
  outnic(TBCR0, (length&0xff)); // set Transmit Byte Count
  outnic(TBCR1, (length>>8));


  outnic(CMDR, 0x12); // start the DMA write

//    print("\r\COPYING SRAM PACKET VALUES\r\n\r\n");

  for(i=start; i<end; i++){
    addr = 0xC00000 + (i<<1);
    j = XIo_In8(addr);
    i++;
    addr = 0xC00000 + ((i)<<1);
    k = XIo_In8(addr);

    word = (j) | (k<<8);
    outnic(DATAPORT, word);
    
  }


  if(innic(ISR)&0x40){
    print("OK\r\n");
  }
  else{
    print("Error! bad DMA); // . Restart the board\r\n\r\n");    
    //return;
  }
  
    outnic(TPSR, TXSTART); // set Transmit Page Start Register
    outnic(CMDR, 0x24); // start transmission


  
  j=1000;
  while(j-->0 && !(innic(ISR)&0x02));
  if(!j){
    return 1;
  }

  outnic(ISR,0xFF);



  outnic(CR,0x22);
}


#define  nextblock_ptr        0x01
static unsigned char nextPage;
static unsigned int currentRetrieveAddress;


int main()
{
  unsigned int i,j;
  unsigned char recvstatus, length;
//  BYTE packet[15];

  WORD word;
  int k,addr;
    
  //-----REGISTER INTERRUPT:
  microblaze_register_handler(audio_int_handler, (void *)0);


  //microblaze_enable_icache();

//  print("\r\n\r\n***** Ethernet Diagnostic *****\r\n\r\n");
//  diag();
//  print("\r\n\r\n***** Ethernet Configuration *****\r\n\r\n");
  
  // reset the NIC card
  if(!init_etherne()){
    print("Ethernet NIC not detected");
//        return;
  }
//  print("Done!\r\n");



  print("Writing headers to memory...");

/*
for (i=0; i<3; i++) {


// ------GRATUITOUS ARP REQUEST ------
sendSRAMPacket(596, 596+60);


  delay(15);

}

*/

// --------------SIP INVITE -----------------
sendSRAMPacket(0, 596);
  
//  delay(20);

// -----------SIP ACK ------------
//sendSRAMPacket(596+60, 404+596+60);

// RECEIVE

  microblaze_enable_interrupts();
  while (1) {

    //init_etherne();
    //  print("receiving\r\n");
    //  outnic(ISR, 0x00);
    //  outnic(ISR, 0xff);
 
    recvstatus=0;  

    //<<7, >>7 perform a modulo 2 (get last bit)
    while((recvstatus&0x01)!=1 && (recvstatus&0x04)!=0x04) {  // || recvstatus == 9) {
    //    delay(5);
      recvstatus =  innic(ISR);
      //putnum(recvstatus);
      //j = innic(RCR);
      //print(" ");
      //putnum(j);
      //print("\r\n");    
    }  
  
    length = initPacketReceive();
    if (isRTP == 1) {
      pkt_cntr = 0;
      audio_cntr = 0;
      enable = 1; 
      isRTP=0;
    }

    if (length == 5) break;
    
    outnic(ISR, 0x40);
    outnic(CR, (RD2|START));
    recvstatus=0;
    
    //    putnum(length);
    //    print("\r\n\r\n");
  }
  
  return 0;
    
}




// diagnostic that tests nic functionality
void diag(){
  int i;
/*  
  print("         Command Register Page Switching Test\r\n");
  print("Switching to page 1\r\n");
  outnic(CMDR, 0x61);

  print("Writing to and reading from 0x0D (value should be 0x4e): ");
  outnic(0x0D*2,0x4e);
  putnum(innic(0x0D*2));
  print("\r\n");

  print("Switching to page 0\r\n");
  outnic(CMDR, 0x21);

  print("Reading from reg offset 0x0D: ");
  putnum(innic(0x0D*2));
  print("\r\n");

  print("Switching to page 1\r\n");
  outnic(CMDR, 0x61);
  print("Reading from reg offset 0x0D (should be 0x4e): ");
  putnum(innic(0x0D*2));
  print("\r\n\r\n");

  print("         Default Value Test\r\n");
  print("Switching to page 0\r\n");
  outnic(CMDR,0x21);
  print("Reading from 0x16 (value should be 0x15): ");
  putnum(innic(0x16*2));
  print("\r\n");
  print("Reading from 0x12 (value should be 0x0c): ");
  putnum(innic(0x12*2));
  print("\r\n");
  print("Reading from 0x13 (value should be 0x12): ");
  putnum(innic(0x13*2));
  print("\r\n");
*/
}

int init_etherne()
{
  outnic(NE_RESET, innic(NE_RESET));  // Do reset
  print("Reset sent...\n\r");
  //delay(2);
  if ((innic(ISR) & 0x80) == 0)       // Report if failed
  {
//    print("Ethernet card failed to reset! Read (should be 0x80)\r\n");
//   putnum(innic(ISR));
    print("failed\r\n");
    return 0;
  }
  else
  {

//    print("Ethernet card reset successful!\r\n");
    resetnic(); // Reset Ethernet card,
//    print("Ethernet card intialization complete!\r\n");
//    diag();
  }

  return 1;
}








void resetnic(){
  int i, count;

  outnic(CMDR, RD2|STOP);  // Abort and DMA and stop the NIC
  delay(10);

  outnic(DCR, DCRVAL);   // Set word-wide access

  outnic(RBCR0, 0x00); // Clear the count regs
  outnic(RBCR1, 0x00);

  outnic(IMR, 0x00);   // Mask completion irq
  outnic(ISR, 0xFF);   // clear interrupt status register

  outnic(RCR, 0x20);   // 0x20  Set to monitor mode
//  outnic(RCR, 0x6f);
  
  //outnic(TCR, 0x02);  // put nic in normal operation

  // Set Rx start, Rx stop, Boundary and TX start regs
  outnic(BNRY, RXSTART);
  outnic(PSTART, RXSTART);
  outnic(PSTOP, RXSTOP);
  nextPage = RXSTART+1;
    
  outnic(CMDR, (PS0|RD2|STOP));
  //write mac
  outnic(PAR0+0, 0x00);
  outnic(PAR0+1, 0x08);
  outnic(PAR0+2, 0x02);
  outnic(PAR0+3, 0x62);
  outnic(PAR0+4, 0x30);
  outnic(PAR0+5, 0x5d); //laptop = 5d

  outnic(CURR, RXSTART+1);
  
  
  outnic(CMDR, (RD2|START));
  outnic(RCR, 0x40+0x10+8+4+2+1);
  
  //full duplex
  outnic(TCR, 0x80);

  outnic(TPSR, TXSTART);
  
  
  outnic(CMDR, (RD2|STOP));

  outnic(DCR, DCRVAL);   // Set word-wide access

  outnic(CMDR, (RD2|START));
  
  
  outnic(ISR, 0xFF);   // clear interrupt status register
  outnic(IMR, 0x01);   // Mask completion irq

  outnic(TCR, 0x80);


  // put nic in start mode
  outnic(CMDR, 0x22); // start nic
  outnic(TCR, 0x80);  // put nic in normal operation
  
  
}



unsigned int initPacketReceive(void)
{
//	unsigned char writePagePtr;
//	unsigned char readPagePtr;
//	unsigned char bnryPagePtr;
	unsigned char i, j, tagstart, tagend;
	unsigned int k;
	int length;
	
	unsigned char pageheader[4];
	//	BYTE packet[260]; //was 214 for testing
	
	unsigned int rxlen, word;
	
	
	// clear the packet received interrupt flag
	outnic(ISR, 0x01);
	
	// if the boundary pointer is invalid,
	// reset the contents of the buffer and exit


	if( (nextPage < RXSTART) || (nextPage >= RXSTOP) )
	{
		//print("nextpage");
		outnic(BNRY, RXSTART);
		outnic(CR, (PS0|RD2|START));
		outnic(CURR, RXSTART+1);
		outnic(CR, (RD2|START));
//		nextPage = RXSTART+1;
//		return 0;
	}



	// initiate DMA to transfer the RTL8019 packet header
	outnic(RBCR0, 4);
	outnic(RBCR1, 0);
	outnic(RSAR0, 0);
	outnic(RSAR1, nextPage);   //readPagePtr);
	outnic(CR, (RD0|START));
	
	//print("\r\nPacket header:\r\n");
	
	for(i=0;i<4;i++) {
		j = innic(DATAPORT);
//		pageheader[i+1] = j;
		pageheader[i] = (unsigned char)j;
		
	}
	
	
	// end the DMA operation
    	
	outnic(CR, (RD2|START));
    	for(i = 0; i <= 20; i++)
        	if(innic(ISR) & 0x40)
            		break;
    	
	outnic(ISR, 0x40);
	
	
	//using wrong pageheader reader, length is in pageheader[1]
	//length = pageheader[1]-4;

	//trying w/ correct - 2?
	length = pageheader[1]-4;
	 
	 
	if (length > 255) length = 255;
	currentRetrieveAddress = (nextPage<<8) + 2;
	  
	
	outnic(ISR, 0xFF); // clear ISR


	outnic(RBCR0, (unsigned char)length);
	outnic(RBCR1, (unsigned char)(length>>8));
	outnic(RSAR0, (unsigned char)currentRetrieveAddress);
	outnic(RSAR1, (unsigned char)(currentRetrieveAddress >> 8));
	outnic(CR, (RD0|START));
	
	for (i=0; i<length; i+=2) {
		k = innic(DATAPORT);
		packet[i] = (k); //lower 8
		packet[i+1] = (k>>8);//upper 8
		//switch i, i+1 for easy retransmission
	}
	
	outnic(CR, (RD2|START));
	for (i=0; i<=20; i++) {
		if (innic(ISR) & 0x40)
			break;
	}
	outnic(ISR, 0x40);
	
	
	
	
	length = pageheader[1]-4;

		

	//nextPage++ works, but not for longer/linked packets, me thinks
	//packets that extend beyond a single buffer page need
	//to obtain nextPage off the pageheader[]	
	
	nextPage++;
	
	if(nextPage-1 < RXSTART) nextPage = RXSTOP;
	
	
	outnic(BNRY, nextPage-1);		
	
	
//	nextPage++;
//	i = nextPage;  //store temporarily b/c we try find linked buffers for a SIP 183
//	nextPage = pageheader[3];
//	nextPage++;
//	outnic(BNRY, nextPage-1);



		
	//CHECK IF SIP OR RTP PACKET
	
	//RANDOM SIP NOTES:
	/* we don't need to get the RTP port out of the 183, we can just receive an
	   RTP packet, which is small, and find the source port; otherwise we'll have
	   to read extra linked buffer pages on the 183 ... screw that! */
	
	//DEBUGGING:
	/*
	print("page: ");
	putnum(nextPage);
	print("\r\n");
	for (i=42; i<48; i++) {
	putnum(packet[i]); //1 = 56
	print("\r\n");
	}
	return 0;
	*/
		

	
	//SIP
	
	if (packet[44] == 0x53) {
	
		j = (packet[52]-0x30)*100 + (packet[53]-0x30)*10 + (packet[54]-0x30);
		
	
		
		if (j==100) {
			print("SIP 100\r\n");
//			nextPage++;
			return 4;
		} else if (j==200) {
			print("SIP 200\r\n");
			
			//if we missed the 183 packet, let's get the tag now
			
			
			return 5;	
				
		} else if (j==183) {
			print("SIP 183\r\n");
			
			return 5;
			
	
			//the 183 packet is really long (includes SDP)...
			//going to have to find linked packets
			//the "TO" "TAG" should be in the 2nd buffer
			//and we need this tag from here b/c we need to send an ACK
			//response including the tag
	
			currentRetrieveAddress+=length;
			
			print("\r\nlength: ");
			putnum(length);
			
			outnic(ISR, 0xFF); // clear ISR

			outnic(RBCR0, (unsigned char)200);
			outnic(RBCR1, (unsigned char)(200 >> 8));
			outnic(RSAR0, (unsigned char)currentRetrieveAddress);
			outnic(RSAR1, (unsigned char)(currentRetrieveAddress >> 8));
			outnic(CR, (RD0|START));
	
			//overwrite old packet to preserve space
			for (i=0; i<200; i+=2) {
				k = innic(DATAPORT);
//				putnum(k);
//				print("\r\n");
				packet[i] = (k); //lower 8
				packet[i+1] = (k>>8);//upper 8
			}
	
			outnic(CR, (RD2|START));
			
			for (i=0; i<=20; i++) {
				if (innic(ISR) & 0x40)
					break;
			}

			outnic(ISR, 0x40);

			
			
			i=0;
			for (j=0; j<200; j++) {
				//traverse looking for "To:"
				if (i!=1 && packet[j] == 0x0a && packet[j+1] == 0x54 && packet[j+2]==0x6f) {
					//"to" found at position a in packet
					i = 1;
					print("TO\r\n");
					
				} else if (i==1 && packet[j]==0x74 && packet[j+1]==0x61 && packet[j+2]==0x67) {
					print("TAG\r\n");

					j+=4 ;
					tagstart = j;
					
					i=0;
					while (packet[j] != 0x0d && i<15) {  //dangerous?
						putnum(packet[j]);	
						print("\r\n");
						tag[i] = packet[j];
						j++;
						i++;
					}
					i=-1;
															
					j-1;
					taglen = tagend-j-1;					
//					putnum(tagend - tagstart);
//					
// -----------SIP ACK ------------
//sendSRAMPacket(596+60, 419+596+60, 1, 0);

					//break;
				}					
					
			}



			
			return 5;
			
			
		} 

		return 4;		
	
	//RTP: 
	} else if (packet[44] == 0x80) {
	  //print("RTP!\r\n");
		
		isRTP = 1;

	
		if (rtpport1 == 0) {
			//first RTP, parse out port # in bytes 36, 37
			//packet is offset by 2
			rtpport1 = packet[34];
			rtpport2 = packet[35];

			//print("Port: ");
			//putnum(rtpport1 | (rtpport2 << 8));
			//print("\r\n");

		}
	
		//send an rtp packet in response
		
		return 4;
		
		
	} else {
	  //print("\r\nnon ");
	  //putnum(packet[44]);
	}
		
	//END CHECK
	
	

	return 0;
	
	
}







void delay(int mult){
  int i;
  int delay = 1000000*mult;
  for(i=0; i<delay; i++);
}

/*
* Convert 16 bit linear sample to 8-bit ulaw sample.
*/

unsigned char l16_to_pcmu(short sample)
{
  static short exp_lut[256] = 
  {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
   5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
   5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
  int sign, exponent, mantissa;
  unsigned char ulawbyte;

  //Get the sample into sign-magnitude. 
  sign = (sample >> 8) & 0x80;      //set aside the sign 
  if (sign) sample = -sample;       // get magnitude 
  if (sample > CLIP) sample = CLIP; // clip the magnitude 

  // Convert from 16 bit linear to ulaw. 
  sample = sample + BIAS;
  exponent = exp_lut[( sample >> 7 ) & 0xFF];
  mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
  ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
  //#ifdef ZEROTRAP
  if (ulawbyte == 0) ulawbyte = 0x02;  // optional CCITT trap 
  //#endif

  return ulawbyte;
} 

/* l16_to_pcmu */


/*
* Convert input of 8-bit ulaw to signed 16-bit linear sample.
*/
int pcmu_to_l16(unsigned char ulawbyte)
{
  static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
  int sign, exponent, mantissa, sample;

  ulawbyte = ~ ulawbyte;
  sign = ( ulawbyte & 0x80 );
  exponent = ( ulawbyte >> 4 ) & 0x07;
  mantissa = ulawbyte & 0x0F;
  sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
  if (sign) sample = -sample;

  return sample;
} /* pcmu_to_l16 */


