#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];

BYTE enableSend = 0;
BYTE rtpport1;
BYTE rtpport2;
short isRTP = 0;
int taglen;
unsigned int sequence=0;
unsigned long timestamp=0;
unsigned int initPacketReceive(void);

#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 seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
			    0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
#define BIAS 0x84   /* define the add-in bias for 16 bit samples */
#define CLIP 32635
short clear_au_in = 0;
short pkt_cntr = 0;
short audio_cntr = 0;
unsigned char zed = 0;
unsigned char enable = 1;

void audio_int_handler(void * poop){  
  short linear_data;
  unsigned char ulaw;
  unsigned char packet_data;
  
  int sample_to_write;
  if(!(enable||enableSend)){
    return;
  }else{
    microblaze_disable_interrupts();
    linear_data = XIo_In32(0xFEFE0000) ;
    //ulaw = l16_to_pcmu(linear_data >> 3);
    //print("\r\n");
    //putnum(linear_data << 2);
    
    ulaw = linear2ulaw((linear_data));
    
    //print("\r\n");
    //putnum(ulaw);

    if(audio_cntr < 159){
//      if(ulaw > 0x8 || ulaw < 0xf2){
	if(ulaw == 0x0){
		ulaw = 0x2;	
	}
	audio_out[audio_cntr++] = ulaw;
//	  }
      //      print(".");
    }else{
      audio_out[audio_cntr] = 0x7f;
    }
    
    
    //    packet_data = audio_in[pkt_cntr];

    if(pkt_cntr < 159){
      packet_data = packet[54 + pkt_cntr];
      pkt_cntr++;
    }else{
      enable = 0;
      packet_data = 0x7f;
    }
    
    //sample_to_write = pcmu_to_l16(packet_data);
    sample_to_write = ulaw2linear(packet_data);
    
    //print("\r\n");
    //putnum(sample_to_write);
    //print(" > ");
    //putnum(linear_data);

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


void loadRTP() {
  unsigned int i,j,k,memaddress, packlen, addr, word;

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

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

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

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

  for(memaddress=1065; memaddress<1109; memaddress++) {
    if (memaddress == 1065+36) {
        word = rtpport1 | (rtpport2<<8);
	memaddress++;
    } else {
      
      addr = 0xC00000 + (memaddress<<1);
      j = XIo_In8(addr);
      memaddress++;
      addr = 0xC00000 + ((memaddress)<<1);
      k = XIo_In8(addr);
      word = (j) | (k<<8);
          
    }
    
  
    outnic(DATAPORT, word);
    
  }
  

  if(innic(ISR)&0x40){
    print("RTP Loaded\r\n");
  }
  else{
    print("RTP Load Failed!\r\n"); // . Restart the board\r\n\r\n");    
    //return;
  }
  
    
}


void sendRTP() {
  unsigned int i,j,k,memaddress, packlen, addr, word;

//  print("sending");

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

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

  

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

  timestamp +=400;
  sequence+=1;
  
  
    outnic(DATAPORT, ((sequence>>8) | (sequence<<8)));
    i = timestamp >> 16;
    j = timestamp & 0xffff;

    outnic(DATAPORT, ((i >> 8) | (i << 8)));
    outnic(DATAPORT, ((j >> 8) | (j << 8))); 

    //synch source id - random
    outnic(DATAPORT, 0x5823);
    outnic(DATAPORT, 0x4264);

    for (i=0; i<160; i+=2) {
      outnic(DATAPORT, (audio_out[i] | (audio_out[i+1] << 8)));
      
      //		word = audioout[i] | (audioout[i+1] << 8);
    }


  if(innic(ISR)&0x40){
  }
  else{
//    print("RTP Failed!\r\n"); // . Restart the board\r\n\r\n");    
    //return;
  }

    outnic(TPSR, TXSTART); // set Transmit Page Start Register
    outnic(CMDR, 0x24); // start transmission

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

  outnic(ISR,0xFF);

//    print("OK\r\n");


  outnic(CR,0x22);

}


void sendSRAMPacket(int start, int end, BYTE puttag, BYTE rtp) {
  int tagpos=-1;
  int length = end-start;
  unsigned int i,j,k,memaddress, packlen, addr, word;
  outnic(CR,0x22);
  
  if (puttag == 1) {
    length = length + (taglen);
    rtp = 0;
  }

  if (rtp == 1) {
    length = 214;
    puttag=0;
  }



  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
  memaddress = start;

  if (rtp==1) {
    timestamp +=1000;
    sequence+=1;
    
    //putnum(sequence);
  }


  for(i=start; i<start+length; i+=2){


   //if RTP - change the seq # and timestamp
    if (rtp==1 && i >= end) break;

   //if ACK - add To tag
    if (puttag==1 && i>=start+266 && i <= start+266+(taglen)) {
      j = tag[++tagpos];
      k = tag[++tagpos];

      if (tagpos > taglen) {
	//j = k;
	k = 0x0d;
	memaddress++;
      }

      word = j | (k << 8);

    } else if (rtp==1 && (memaddress == start+36)) {
        word = rtpport1 | (rtpport2<<8);
	memaddress +=2;
    } else {
      
      addr = 0xC00000 + (memaddress<<1);
      j = XIo_In8(addr);
      memaddress++;
      addr = 0xC00000 + ((memaddress)<<1);
      k = XIo_In8(addr);
      word = (j) | (k<<8);
      memaddress++;
          
      if (puttag==1 && (memaddress == start+18 || memaddress == start+40)) 
	word=word+((taglen-10)<<8);

    }
    
    
    
    if (puttag==1 && (memaddress == start+26)) { 
      print("checksum\r\n");

      if (taglen == 12 || taglen==13) word = 0xb577;
   }
    
    outnic(DATAPORT, word);
    
  }
  

  if (rtp == 1) {
    outnic(DATAPORT, ((sequence>>8) | (sequence<<8)));
    i = timestamp >> 16;
    j = timestamp & 0xffff;

    outnic(DATAPORT, ((i >> 8) | (i << 8)));
    outnic(DATAPORT, ((j >> 8) | (j << 8))); 

    //synch source id - random
    outnic(DATAPORT, 0x5823);
    outnic(DATAPORT, 0x4264);

    for (i=0; i<160; i+=2) {
      outnic(DATAPORT, (audio_out[i] | (audio_out[i+1] << 8)));
      
      //		word = audioout[i] | (audioout[i+1] << 8);
    }
    
  }

  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

    //    if (rtp==1)
    //  audio_cntr=0;
  
  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;
  short audio_to_swap;
	
//  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, 0, 0);
  


enableSend=0;

// RECEIVE

  microblaze_enable_interrupts();
  while (1) {

    recvstatus=0;  

    while((recvstatus&0x01)!=1 && (recvstatus&0x04)!=0x04) {  // || recvstatus == 9) {

      recvstatus =  innic(ISR);

      if (enableSend == 1 && audio_cntr >= 159 ) {
	//sendSRAMPacket(1065, 1109, 0, 1);
	for(i = 0; i < 159; i+=2){
		audio_to_swap = audio_out[i+1];
		audio_out[i+1] = audio_out[i];
		audio_out[i] = audio_to_swap;
	}
	sendRTP();
	audio_cntr=0;

      }  else {
	//	putnum(audio_cntr);
	// print("\r");
      }



    }  
  
    length = initPacketReceive();
    if (isRTP == 1) {
      pkt_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 4;	
     */			
		} else if (j==200) { //|| j==183) { // || j==183) {
			print("SIP 183/200\r\n");
			
			//return 4;
			
	
			//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)250);
			outnic(RBCR1, (unsigned char)(250 >> 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<250; 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);

	
			print("searching...\r\n");
			
			
			i=0;
			for (j=0; j<250; j++) {
				//putnum(j);
				//print(" ");
				//putnum(packet[j]);
				//print("\r\n");
				//traverse looking for "To:"
				if (i!=1 && packet[j] == 0x0a && packet[j+1] == 0x54 && (packet[j+2] == 0x6e || 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] == 0x66 || 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 = j-1-tagstart;					
					putnum(tagend - tagstart);
					
					// -----------SIP ACK ------------
					sendSRAMPacket(596+60, 409+596+60, 1, 0);

					loadRTP();
					
	
					enableSend = 1;
 

					//break;
				}									     
			}



			
			return 4;
			
			
		} 

		return 4;		
		
	//RTP: 
	} else if (rtpport1 == 0 && packet[44] == 0x80) {
	  //print("RTP!\r\n");
	  isRTP = 1;
	 
	    //first RTP, parse out port # in bytes 36, 37
	    //packet is offset by 2
	    rtpport1 = packet[36];
	    rtpport2 = packet[37];
	    
	    print("Port: ");
	    putnum(rtpport1 | (rtpport2 << 8));
	    print("\r\n");
	    
	    return 4;
	} else if (rtpport1 != 0 && packet[36] == rtpport1 && packet[37]==rtpport2) {
	  isRTP = 1;
	  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;
} 
*/

/*
* 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 */

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

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

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

/*
 * 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
linear2ulaw(
	int		pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	uval;

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

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

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

}

/*
 * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
 *
 * First, a biased linear code is derived from the code word. An unbiased
 * output can then be obtained by subtracting 33 from the biased code.
 *
 * Note that this function expects to be passed the complement of the
 * original code word. This is in keeping with ISDN conventions.
 */
int
ulaw2linear(
	unsigned char	u_val)
{
	int		t;

	/* Complement to obtain normal u-law value. */
	u_val = ~u_val;

	/*
	 * Extract and bias the quantization bits. Then
	 * shift up by the segment number and subtract out the bias.
	 */
	t = ((u_val & QUANT_MASK) << 3) + BIAS;
	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;

	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}



